Last active
August 26, 2015 21:33
-
-
Save nolanlawson/ac387be0722dad3a207c to your computer and use it in GitHub Desktop.
Basic PouchDB authentication test site
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="utf-8"> | |
<title>Mocha Tests</title> | |
<link rel="stylesheet" href="./mocha.css" /> | |
<script src="./mocha.js"></script> | |
<script> | |
mocha.setup('bdd'); | |
</script> | |
<script src="./test-bundle.js"></script> | |
</head> | |
<body> | |
<div id="mocha"></div> | |
<script> | |
mocha.run(); | |
</script> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@charset "utf-8"; | |
body { | |
margin:0; | |
} | |
#mocha { | |
font: 20px/1.5 "Helvetica Neue", Helvetica, Arial, sans-serif; | |
margin: 60px 50px; | |
} | |
#mocha ul, #mocha li { | |
margin: 0; | |
padding: 0; | |
} | |
#mocha ul { | |
list-style: none; | |
} | |
#mocha h1, #mocha h2 { | |
margin: 0; | |
} | |
#mocha h1 { | |
margin-top: 15px; | |
font-size: 1em; | |
font-weight: 200; | |
} | |
#mocha h1 a { | |
text-decoration: none; | |
color: inherit; | |
} | |
#mocha h1 a:hover { | |
text-decoration: underline; | |
} | |
#mocha .suite .suite h1 { | |
margin-top: 0; | |
font-size: .8em; | |
} | |
#mocha .hidden { | |
display: none; | |
} | |
#mocha h2 { | |
font-size: 12px; | |
font-weight: normal; | |
cursor: pointer; | |
} | |
#mocha .suite { | |
margin-left: 15px; | |
} | |
#mocha .test { | |
margin-left: 15px; | |
overflow: hidden; | |
} | |
#mocha .test.pending:hover h2::after { | |
content: '(pending)'; | |
font-family: arial, sans-serif; | |
} | |
#mocha .test.pass.medium .duration { | |
background: #C09853; | |
} | |
#mocha .test.pass.slow .duration { | |
background: #B94A48; | |
} | |
#mocha .test.pass::before { | |
content: '✓'; | |
font-size: 12px; | |
display: block; | |
float: left; | |
margin-right: 5px; | |
color: #00d6b2; | |
} | |
#mocha .test.pass .duration { | |
font-size: 9px; | |
margin-left: 5px; | |
padding: 2px 5px; | |
color: white; | |
-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.2); | |
-moz-box-shadow: inset 0 1px 1px rgba(0,0,0,.2); | |
box-shadow: inset 0 1px 1px rgba(0,0,0,.2); | |
-webkit-border-radius: 5px; | |
-moz-border-radius: 5px; | |
-ms-border-radius: 5px; | |
-o-border-radius: 5px; | |
border-radius: 5px; | |
} | |
#mocha .test.pass.fast .duration { | |
display: none; | |
} | |
#mocha .test.pending { | |
color: #0b97c4; | |
} | |
#mocha .test.pending::before { | |
content: '◦'; | |
color: #0b97c4; | |
} | |
#mocha .test.fail { | |
color: #c00; | |
} | |
#mocha .test.fail pre { | |
color: black; | |
} | |
#mocha .test.fail::before { | |
content: '✖'; | |
font-size: 12px; | |
display: block; | |
float: left; | |
margin-right: 5px; | |
color: #c00; | |
} | |
#mocha .test pre.error { | |
color: #c00; | |
max-height: 300px; | |
overflow: auto; | |
} | |
#mocha .test pre { | |
display: block; | |
float: left; | |
clear: left; | |
font: 12px/1.5 monaco, monospace; | |
margin: 5px; | |
padding: 15px; | |
border: 1px solid #eee; | |
border-bottom-color: #ddd; | |
-webkit-border-radius: 3px; | |
-webkit-box-shadow: 0 1px 3px #eee; | |
-moz-border-radius: 3px; | |
-moz-box-shadow: 0 1px 3px #eee; | |
border-radius: 3px; | |
} | |
#mocha .test h2 { | |
position: relative; | |
} | |
#mocha .test a.replay { | |
position: absolute; | |
top: 3px; | |
right: 0; | |
text-decoration: none; | |
vertical-align: middle; | |
display: block; | |
width: 15px; | |
height: 15px; | |
line-height: 15px; | |
text-align: center; | |
background: #eee; | |
font-size: 15px; | |
-moz-border-radius: 15px; | |
border-radius: 15px; | |
-webkit-transition: opacity 200ms; | |
-moz-transition: opacity 200ms; | |
transition: opacity 200ms; | |
opacity: 0.3; | |
color: #888; | |
} | |
#mocha .test:hover a.replay { | |
opacity: 1; | |
} | |
#mocha-report.pass .test.fail { | |
display: none; | |
} | |
#mocha-report.fail .test.pass { | |
display: none; | |
} | |
#mocha-report.pending .test.pass, | |
#mocha-report.pending .test.fail { | |
display: none; | |
} | |
#mocha-report.pending .test.pass.pending { | |
display: block; | |
} | |
#mocha-error { | |
color: #c00; | |
font-size: 1.5em; | |
font-weight: 100; | |
letter-spacing: 1px; | |
} | |
#mocha-stats { | |
position: fixed; | |
top: 15px; | |
right: 10px; | |
font-size: 12px; | |
margin: 0; | |
color: #888; | |
z-index: 1; | |
} | |
#mocha-stats .progress { | |
float: right; | |
padding-top: 0; | |
} | |
#mocha-stats em { | |
color: black; | |
} | |
#mocha-stats a { | |
text-decoration: none; | |
color: inherit; | |
} | |
#mocha-stats a:hover { | |
border-bottom: 1px solid #eee; | |
} | |
#mocha-stats li { | |
display: inline-block; | |
margin: 0 5px; | |
list-style: none; | |
padding-top: 11px; | |
} | |
#mocha-stats canvas { | |
width: 40px; | |
height: 40px; | |
} | |
#mocha code .comment { color: #ddd } | |
#mocha code .init { color: #2F6FAD } | |
#mocha code .string { color: #5890AD } | |
#mocha code .keyword { color: #8A6343 } | |
#mocha code .number { color: #2F6FAD } | |
@media screen and (max-device-width: 480px) { | |
#mocha { | |
margin: 60px 0px; | |
} | |
#mocha #stats { | |
position: absolute; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;(function(){ | |
// CommonJS require() | |
function require(p){ | |
var path = require.resolve(p) | |
, mod = require.modules[path]; | |
if (!mod) throw new Error('failed to require "' + p + '"'); | |
if (!mod.exports) { | |
mod.exports = {}; | |
mod.call(mod.exports, mod, mod.exports, require.relative(path)); | |
} | |
return mod.exports; | |
} | |
require.modules = {}; | |
require.resolve = function (path){ | |
var orig = path | |
, reg = path + '.js' | |
, index = path + '/index.js'; | |
return require.modules[reg] && reg | |
|| require.modules[index] && index | |
|| orig; | |
}; | |
require.register = function (path, fn){ | |
require.modules[path] = fn; | |
}; | |
require.relative = function (parent) { | |
return function(p){ | |
if ('.' != p.charAt(0)) return require(p); | |
var path = parent.split('/') | |
, segs = p.split('/'); | |
path.pop(); | |
for (var i = 0; i < segs.length; i++) { | |
var seg = segs[i]; | |
if ('..' == seg) path.pop(); | |
else if ('.' != seg) path.push(seg); | |
} | |
return require(path.join('/')); | |
}; | |
}; | |
require.register("browser/debug.js", function(module, exports, require){ | |
module.exports = function(type){ | |
return function(){ | |
} | |
}; | |
}); // module: browser/debug.js | |
require.register("browser/diff.js", function(module, exports, require){ | |
/* See LICENSE file for terms of use */ | |
/* | |
* Text diff implementation. | |
* | |
* This library supports the following APIS: | |
* JsDiff.diffChars: Character by character diff | |
* JsDiff.diffWords: Word (as defined by \b regex) diff which ignores whitespace | |
* JsDiff.diffLines: Line based diff | |
* | |
* JsDiff.diffCss: Diff targeted at CSS content | |
* | |
* These methods are based on the implementation proposed in | |
* "An O(ND) Difference Algorithm and its Variations" (Myers, 1986). | |
* http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.4.6927 | |
*/ | |
var JsDiff = (function() { | |
/*jshint maxparams: 5*/ | |
function clonePath(path) { | |
return { newPos: path.newPos, components: path.components.slice(0) }; | |
} | |
function removeEmpty(array) { | |
var ret = []; | |
for (var i = 0; i < array.length; i++) { | |
if (array[i]) { | |
ret.push(array[i]); | |
} | |
} | |
return ret; | |
} | |
function escapeHTML(s) { | |
var n = s; | |
n = n.replace(/&/g, '&'); | |
n = n.replace(/</g, '<'); | |
n = n.replace(/>/g, '>'); | |
n = n.replace(/"/g, '"'); | |
return n; | |
} | |
var Diff = function(ignoreWhitespace) { | |
this.ignoreWhitespace = ignoreWhitespace; | |
}; | |
Diff.prototype = { | |
diff: function(oldString, newString) { | |
// Handle the identity case (this is due to unrolling editLength == 0 | |
if (newString === oldString) { | |
return [{ value: newString }]; | |
} | |
if (!newString) { | |
return [{ value: oldString, removed: true }]; | |
} | |
if (!oldString) { | |
return [{ value: newString, added: true }]; | |
} | |
newString = this.tokenize(newString); | |
oldString = this.tokenize(oldString); | |
var newLen = newString.length, oldLen = oldString.length; | |
var maxEditLength = newLen + oldLen; | |
var bestPath = [{ newPos: -1, components: [] }]; | |
// Seed editLength = 0 | |
var oldPos = this.extractCommon(bestPath[0], newString, oldString, 0); | |
if (bestPath[0].newPos+1 >= newLen && oldPos+1 >= oldLen) { | |
return bestPath[0].components; | |
} | |
for (var editLength = 1; editLength <= maxEditLength; editLength++) { | |
for (var diagonalPath = -1*editLength; diagonalPath <= editLength; diagonalPath+=2) { | |
var basePath; | |
var addPath = bestPath[diagonalPath-1], | |
removePath = bestPath[diagonalPath+1]; | |
oldPos = (removePath ? removePath.newPos : 0) - diagonalPath; | |
if (addPath) { | |
// No one else is going to attempt to use this value, clear it | |
bestPath[diagonalPath-1] = undefined; | |
} | |
var canAdd = addPath && addPath.newPos+1 < newLen; | |
var canRemove = removePath && 0 <= oldPos && oldPos < oldLen; | |
if (!canAdd && !canRemove) { | |
bestPath[diagonalPath] = undefined; | |
continue; | |
} | |
// Select the diagonal that we want to branch from. We select the prior | |
// path whose position in the new string is the farthest from the origin | |
// and does not pass the bounds of the diff graph | |
if (!canAdd || (canRemove && addPath.newPos < removePath.newPos)) { | |
basePath = clonePath(removePath); | |
this.pushComponent(basePath.components, oldString[oldPos], undefined, true); | |
} else { | |
basePath = clonePath(addPath); | |
basePath.newPos++; | |
this.pushComponent(basePath.components, newString[basePath.newPos], true, undefined); | |
} | |
var oldPos = this.extractCommon(basePath, newString, oldString, diagonalPath); | |
if (basePath.newPos+1 >= newLen && oldPos+1 >= oldLen) { | |
return basePath.components; | |
} else { | |
bestPath[diagonalPath] = basePath; | |
} | |
} | |
} | |
}, | |
pushComponent: function(components, value, added, removed) { | |
var last = components[components.length-1]; | |
if (last && last.added === added && last.removed === removed) { | |
// We need to clone here as the component clone operation is just | |
// as shallow array clone | |
components[components.length-1] = | |
{value: this.join(last.value, value), added: added, removed: removed }; | |
} else { | |
components.push({value: value, added: added, removed: removed }); | |
} | |
}, | |
extractCommon: function(basePath, newString, oldString, diagonalPath) { | |
var newLen = newString.length, | |
oldLen = oldString.length, | |
newPos = basePath.newPos, | |
oldPos = newPos - diagonalPath; | |
while (newPos+1 < newLen && oldPos+1 < oldLen && this.equals(newString[newPos+1], oldString[oldPos+1])) { | |
newPos++; | |
oldPos++; | |
this.pushComponent(basePath.components, newString[newPos], undefined, undefined); | |
} | |
basePath.newPos = newPos; | |
return oldPos; | |
}, | |
equals: function(left, right) { | |
var reWhitespace = /\S/; | |
if (this.ignoreWhitespace && !reWhitespace.test(left) && !reWhitespace.test(right)) { | |
return true; | |
} else { | |
return left === right; | |
} | |
}, | |
join: function(left, right) { | |
return left + right; | |
}, | |
tokenize: function(value) { | |
return value; | |
} | |
}; | |
var CharDiff = new Diff(); | |
var WordDiff = new Diff(true); | |
var WordWithSpaceDiff = new Diff(); | |
WordDiff.tokenize = WordWithSpaceDiff.tokenize = function(value) { | |
return removeEmpty(value.split(/(\s+|\b)/)); | |
}; | |
var CssDiff = new Diff(true); | |
CssDiff.tokenize = function(value) { | |
return removeEmpty(value.split(/([{}:;,]|\s+)/)); | |
}; | |
var LineDiff = new Diff(); | |
LineDiff.tokenize = function(value) { | |
return value.split(/^/m); | |
}; | |
return { | |
Diff: Diff, | |
diffChars: function(oldStr, newStr) { return CharDiff.diff(oldStr, newStr); }, | |
diffWords: function(oldStr, newStr) { return WordDiff.diff(oldStr, newStr); }, | |
diffWordsWithSpace: function(oldStr, newStr) { return WordWithSpaceDiff.diff(oldStr, newStr); }, | |
diffLines: function(oldStr, newStr) { return LineDiff.diff(oldStr, newStr); }, | |
diffCss: function(oldStr, newStr) { return CssDiff.diff(oldStr, newStr); }, | |
createPatch: function(fileName, oldStr, newStr, oldHeader, newHeader) { | |
var ret = []; | |
ret.push('Index: ' + fileName); | |
ret.push('==================================================================='); | |
ret.push('--- ' + fileName + (typeof oldHeader === 'undefined' ? '' : '\t' + oldHeader)); | |
ret.push('+++ ' + fileName + (typeof newHeader === 'undefined' ? '' : '\t' + newHeader)); | |
var diff = LineDiff.diff(oldStr, newStr); | |
if (!diff[diff.length-1].value) { | |
diff.pop(); // Remove trailing newline add | |
} | |
diff.push({value: '', lines: []}); // Append an empty value to make cleanup easier | |
function contextLines(lines) { | |
return lines.map(function(entry) { return ' ' + entry; }); | |
} | |
function eofNL(curRange, i, current) { | |
var last = diff[diff.length-2], | |
isLast = i === diff.length-2, | |
isLastOfType = i === diff.length-3 && (current.added !== last.added || current.removed !== last.removed); | |
// Figure out if this is the last line for the given file and missing NL | |
if (!/\n$/.test(current.value) && (isLast || isLastOfType)) { | |
curRange.push('\\ No newline at end of file'); | |
} | |
} | |
var oldRangeStart = 0, newRangeStart = 0, curRange = [], | |
oldLine = 1, newLine = 1; | |
for (var i = 0; i < diff.length; i++) { | |
var current = diff[i], | |
lines = current.lines || current.value.replace(/\n$/, '').split('\n'); | |
current.lines = lines; | |
if (current.added || current.removed) { | |
if (!oldRangeStart) { | |
var prev = diff[i-1]; | |
oldRangeStart = oldLine; | |
newRangeStart = newLine; | |
if (prev) { | |
curRange = contextLines(prev.lines.slice(-4)); | |
oldRangeStart -= curRange.length; | |
newRangeStart -= curRange.length; | |
} | |
} | |
curRange.push.apply(curRange, lines.map(function(entry) { return (current.added?'+':'-') + entry; })); | |
eofNL(curRange, i, current); | |
if (current.added) { | |
newLine += lines.length; | |
} else { | |
oldLine += lines.length; | |
} | |
} else { | |
if (oldRangeStart) { | |
// Close out any changes that have been output (or join overlapping) | |
if (lines.length <= 8 && i < diff.length-2) { | |
// Overlapping | |
curRange.push.apply(curRange, contextLines(lines)); | |
} else { | |
// end the range and output | |
var contextSize = Math.min(lines.length, 4); | |
ret.push( | |
'@@ -' + oldRangeStart + ',' + (oldLine-oldRangeStart+contextSize) | |
+ ' +' + newRangeStart + ',' + (newLine-newRangeStart+contextSize) | |
+ ' @@'); | |
ret.push.apply(ret, curRange); | |
ret.push.apply(ret, contextLines(lines.slice(0, contextSize))); | |
if (lines.length <= 4) { | |
eofNL(ret, i, current); | |
} | |
oldRangeStart = 0; newRangeStart = 0; curRange = []; | |
} | |
} | |
oldLine += lines.length; | |
newLine += lines.length; | |
} | |
} | |
return ret.join('\n') + '\n'; | |
}, | |
applyPatch: function(oldStr, uniDiff) { | |
var diffstr = uniDiff.split('\n'); | |
var diff = []; | |
var remEOFNL = false, | |
addEOFNL = false; | |
for (var i = (diffstr[0][0]==='I'?4:0); i < diffstr.length; i++) { | |
if(diffstr[i][0] === '@') { | |
var meh = diffstr[i].split(/@@ -(\d+),(\d+) \+(\d+),(\d+) @@/); | |
diff.unshift({ | |
start:meh[3], | |
oldlength:meh[2], | |
oldlines:[], | |
newlength:meh[4], | |
newlines:[] | |
}); | |
} else if(diffstr[i][0] === '+') { | |
diff[0].newlines.push(diffstr[i].substr(1)); | |
} else if(diffstr[i][0] === '-') { | |
diff[0].oldlines.push(diffstr[i].substr(1)); | |
} else if(diffstr[i][0] === ' ') { | |
diff[0].newlines.push(diffstr[i].substr(1)); | |
diff[0].oldlines.push(diffstr[i].substr(1)); | |
} else if(diffstr[i][0] === '\\') { | |
if (diffstr[i-1][0] === '+') { | |
remEOFNL = true; | |
} else if(diffstr[i-1][0] === '-') { | |
addEOFNL = true; | |
} | |
} | |
} | |
var str = oldStr.split('\n'); | |
for (var i = diff.length - 1; i >= 0; i--) { | |
var d = diff[i]; | |
for (var j = 0; j < d.oldlength; j++) { | |
if(str[d.start-1+j] !== d.oldlines[j]) { | |
return false; | |
} | |
} | |
Array.prototype.splice.apply(str,[d.start-1,+d.oldlength].concat(d.newlines)); | |
} | |
if (remEOFNL) { | |
while (!str[str.length-1]) { | |
str.pop(); | |
} | |
} else if (addEOFNL) { | |
str.push(''); | |
} | |
return str.join('\n'); | |
}, | |
convertChangesToXML: function(changes){ | |
var ret = []; | |
for ( var i = 0; i < changes.length; i++) { | |
var change = changes[i]; | |
if (change.added) { | |
ret.push('<ins>'); | |
} else if (change.removed) { | |
ret.push('<del>'); | |
} | |
ret.push(escapeHTML(change.value)); | |
if (change.added) { | |
ret.push('</ins>'); | |
} else if (change.removed) { | |
ret.push('</del>'); | |
} | |
} | |
return ret.join(''); | |
}, | |
// See: http://code.google.com/p/google-diff-match-patch/wiki/API | |
convertChangesToDMP: function(changes){ | |
var ret = [], change; | |
for ( var i = 0; i < changes.length; i++) { | |
change = changes[i]; | |
ret.push([(change.added ? 1 : change.removed ? -1 : 0), change.value]); | |
} | |
return ret; | |
} | |
}; | |
})(); | |
if (typeof module !== 'undefined') { | |
module.exports = JsDiff; | |
} | |
}); // module: browser/diff.js | |
require.register("browser/events.js", function(module, exports, require){ | |
/** | |
* Module exports. | |
*/ | |
exports.EventEmitter = EventEmitter; | |
/** | |
* Check if `obj` is an array. | |
*/ | |
function isArray(obj) { | |
return '[object Array]' == {}.toString.call(obj); | |
} | |
/** | |
* Event emitter constructor. | |
* | |
* @api public | |
*/ | |
function EventEmitter(){}; | |
/** | |
* Adds a listener. | |
* | |
* @api public | |
*/ | |
EventEmitter.prototype.on = function (name, fn) { | |
if (!this.$events) { | |
this.$events = {}; | |
} | |
if (!this.$events[name]) { | |
this.$events[name] = fn; | |
} else if (isArray(this.$events[name])) { | |
this.$events[name].push(fn); | |
} else { | |
this.$events[name] = [this.$events[name], fn]; | |
} | |
return this; | |
}; | |
EventEmitter.prototype.addListener = EventEmitter.prototype.on; | |
/** | |
* Adds a volatile listener. | |
* | |
* @api public | |
*/ | |
EventEmitter.prototype.once = function (name, fn) { | |
var self = this; | |
function on () { | |
self.removeListener(name, on); | |
fn.apply(this, arguments); | |
}; | |
on.listener = fn; | |
this.on(name, on); | |
return this; | |
}; | |
/** | |
* Removes a listener. | |
* | |
* @api public | |
*/ | |
EventEmitter.prototype.removeListener = function (name, fn) { | |
if (this.$events && this.$events[name]) { | |
var list = this.$events[name]; | |
if (isArray(list)) { | |
var pos = -1; | |
for (var i = 0, l = list.length; i < l; i++) { | |
if (list[i] === fn || (list[i].listener && list[i].listener === fn)) { | |
pos = i; | |
break; | |
} | |
} | |
if (pos < 0) { | |
return this; | |
} | |
list.splice(pos, 1); | |
if (!list.length) { | |
delete this.$events[name]; | |
} | |
} else if (list === fn || (list.listener && list.listener === fn)) { | |
delete this.$events[name]; | |
} | |
} | |
return this; | |
}; | |
/** | |
* Removes all listeners for an event. | |
* | |
* @api public | |
*/ | |
EventEmitter.prototype.removeAllListeners = function (name) { | |
if (name === undefined) { | |
this.$events = {}; | |
return this; | |
} | |
if (this.$events && this.$events[name]) { | |
this.$events[name] = null; | |
} | |
return this; | |
}; | |
/** | |
* Gets all listeners for a certain event. | |
* | |
* @api public | |
*/ | |
EventEmitter.prototype.listeners = function (name) { | |
if (!this.$events) { | |
this.$events = {}; | |
} | |
if (!this.$events[name]) { | |
this.$events[name] = []; | |
} | |
if (!isArray(this.$events[name])) { | |
this.$events[name] = [this.$events[name]]; | |
} | |
return this.$events[name]; | |
}; | |
/** | |
* Emits an event. | |
* | |
* @api public | |
*/ | |
EventEmitter.prototype.emit = function (name) { | |
if (!this.$events) { | |
return false; | |
} | |
var handler = this.$events[name]; | |
if (!handler) { | |
return false; | |
} | |
var args = [].slice.call(arguments, 1); | |
if ('function' == typeof handler) { | |
handler.apply(this, args); | |
} else if (isArray(handler)) { | |
var listeners = handler.slice(); | |
for (var i = 0, l = listeners.length; i < l; i++) { | |
listeners[i].apply(this, args); | |
} | |
} else { | |
return false; | |
} | |
return true; | |
}; | |
}); // module: browser/events.js | |
require.register("browser/fs.js", function(module, exports, require){ | |
}); // module: browser/fs.js | |
require.register("browser/path.js", function(module, exports, require){ | |
}); // module: browser/path.js | |
require.register("browser/progress.js", function(module, exports, require){ | |
/** | |
* Expose `Progress`. | |
*/ | |
module.exports = Progress; | |
/** | |
* Initialize a new `Progress` indicator. | |
*/ | |
function Progress() { | |
this.percent = 0; | |
this.size(0); | |
this.fontSize(11); | |
this.font('helvetica, arial, sans-serif'); | |
} | |
/** | |
* Set progress size to `n`. | |
* | |
* @param {Number} n | |
* @return {Progress} for chaining | |
* @api public | |
*/ | |
Progress.prototype.size = function(n){ | |
this._size = n; | |
return this; | |
}; | |
/** | |
* Set text to `str`. | |
* | |
* @param {String} str | |
* @return {Progress} for chaining | |
* @api public | |
*/ | |
Progress.prototype.text = function(str){ | |
this._text = str; | |
return this; | |
}; | |
/** | |
* Set font size to `n`. | |
* | |
* @param {Number} n | |
* @return {Progress} for chaining | |
* @api public | |
*/ | |
Progress.prototype.fontSize = function(n){ | |
this._fontSize = n; | |
return this; | |
}; | |
/** | |
* Set font `family`. | |
* | |
* @param {String} family | |
* @return {Progress} for chaining | |
*/ | |
Progress.prototype.font = function(family){ | |
this._font = family; | |
return this; | |
}; | |
/** | |
* Update percentage to `n`. | |
* | |
* @param {Number} n | |
* @return {Progress} for chaining | |
*/ | |
Progress.prototype.update = function(n){ | |
this.percent = n; | |
return this; | |
}; | |
/** | |
* Draw on `ctx`. | |
* | |
* @param {CanvasRenderingContext2d} ctx | |
* @return {Progress} for chaining | |
*/ | |
Progress.prototype.draw = function(ctx){ | |
var percent = Math.min(this.percent, 100) | |
, size = this._size | |
, half = size / 2 | |
, x = half | |
, y = half | |
, rad = half - 1 | |
, fontSize = this._fontSize; | |
ctx.font = fontSize + 'px ' + this._font; | |
var angle = Math.PI * 2 * (percent / 100); | |
ctx.clearRect(0, 0, size, size); | |
// outer circle | |
ctx.strokeStyle = '#9f9f9f'; | |
ctx.beginPath(); | |
ctx.arc(x, y, rad, 0, angle, false); | |
ctx.stroke(); | |
// inner circle | |
ctx.strokeStyle = '#eee'; | |
ctx.beginPath(); | |
ctx.arc(x, y, rad - 1, 0, angle, true); | |
ctx.stroke(); | |
// text | |
var text = this._text || (percent | 0) + '%' | |
, w = ctx.measureText(text).width; | |
ctx.fillText( | |
text | |
, x - w / 2 + 1 | |
, y + fontSize / 2 - 1); | |
return this; | |
}; | |
}); // module: browser/progress.js | |
require.register("browser/tty.js", function(module, exports, require){ | |
exports.isatty = function(){ | |
return true; | |
}; | |
exports.getWindowSize = function(){ | |
if ('innerHeight' in global) { | |
return [global.innerHeight, global.innerWidth]; | |
} else { | |
// In a Web Worker, the DOM Window is not available. | |
return [640, 480]; | |
} | |
}; | |
}); // module: browser/tty.js | |
require.register("context.js", function(module, exports, require){ | |
/** | |
* Expose `Context`. | |
*/ | |
module.exports = Context; | |
/** | |
* Initialize a new `Context`. | |
* | |
* @api private | |
*/ | |
function Context(){} | |
/** | |
* Set or get the context `Runnable` to `runnable`. | |
* | |
* @param {Runnable} runnable | |
* @return {Context} | |
* @api private | |
*/ | |
Context.prototype.runnable = function(runnable){ | |
if (0 == arguments.length) return this._runnable; | |
this.test = this._runnable = runnable; | |
return this; | |
}; | |
/** | |
* Set test timeout `ms`. | |
* | |
* @param {Number} ms | |
* @return {Context} self | |
* @api private | |
*/ | |
Context.prototype.timeout = function(ms){ | |
this.runnable().timeout(ms); | |
return this; | |
}; | |
/** | |
* Set test slowness threshold `ms`. | |
* | |
* @param {Number} ms | |
* @return {Context} self | |
* @api private | |
*/ | |
Context.prototype.slow = function(ms){ | |
this.runnable().slow(ms); | |
return this; | |
}; | |
/** | |
* Inspect the context void of `._runnable`. | |
* | |
* @return {String} | |
* @api private | |
*/ | |
Context.prototype.inspect = function(){ | |
return JSON.stringify(this, function(key, val){ | |
if ('_runnable' == key) return; | |
if ('test' == key) return; | |
return val; | |
}, 2); | |
}; | |
}); // module: context.js | |
require.register("hook.js", function(module, exports, require){ | |
/** | |
* Module dependencies. | |
*/ | |
var Runnable = require('./runnable'); | |
/** | |
* Expose `Hook`. | |
*/ | |
module.exports = Hook; | |
/** | |
* Initialize a new `Hook` with the given `title` and callback `fn`. | |
* | |
* @param {String} title | |
* @param {Function} fn | |
* @api private | |
*/ | |
function Hook(title, fn) { | |
Runnable.call(this, title, fn); | |
this.type = 'hook'; | |
} | |
/** | |
* Inherit from `Runnable.prototype`. | |
*/ | |
function F(){}; | |
F.prototype = Runnable.prototype; | |
Hook.prototype = new F; | |
Hook.prototype.constructor = Hook; | |
/** | |
* Get or set the test `err`. | |
* | |
* @param {Error} err | |
* @return {Error} | |
* @api public | |
*/ | |
Hook.prototype.error = function(err){ | |
if (0 == arguments.length) { | |
var err = this._error; | |
this._error = null; | |
return err; | |
} | |
this._error = err; | |
}; | |
}); // module: hook.js | |
require.register("interfaces/bdd.js", function(module, exports, require){ | |
/** | |
* Module dependencies. | |
*/ | |
var Suite = require('../suite') | |
, Test = require('../test') | |
, utils = require('../utils'); | |
/** | |
* BDD-style interface: | |
* | |
* describe('Array', function(){ | |
* describe('#indexOf()', function(){ | |
* it('should return -1 when not present', function(){ | |
* | |
* }); | |
* | |
* it('should return the index when present', function(){ | |
* | |
* }); | |
* }); | |
* }); | |
* | |
*/ | |
module.exports = function(suite){ | |
var suites = [suite]; | |
suite.on('pre-require', function(context, file, mocha){ | |
/** | |
* Execute before running tests. | |
*/ | |
context.before = function(fn){ | |
suites[0].beforeAll(fn); | |
}; | |
/** | |
* Execute after running tests. | |
*/ | |
context.after = function(fn){ | |
suites[0].afterAll(fn); | |
}; | |
/** | |
* Execute before each test case. | |
*/ | |
context.beforeEach = function(fn){ | |
suites[0].beforeEach(fn); | |
}; | |
/** | |
* Execute after each test case. | |
*/ | |
context.afterEach = function(fn){ | |
suites[0].afterEach(fn); | |
}; | |
/** | |
* Describe a "suite" with the given `title` | |
* and callback `fn` containing nested suites | |
* and/or tests. | |
*/ | |
context.describe = context.context = function(title, fn){ | |
var suite = Suite.create(suites[0], title); | |
suites.unshift(suite); | |
fn.call(suite); | |
suites.shift(); | |
return suite; | |
}; | |
/** | |
* Pending describe. | |
*/ | |
context.xdescribe = | |
context.xcontext = | |
context.describe.skip = function(title, fn){ | |
var suite = Suite.create(suites[0], title); | |
suite.pending = true; | |
suites.unshift(suite); | |
fn.call(suite); | |
suites.shift(); | |
}; | |
/** | |
* Exclusive suite. | |
*/ | |
context.describe.only = function(title, fn){ | |
var suite = context.describe(title, fn); | |
mocha.grep(suite.fullTitle()); | |
return suite; | |
}; | |
/** | |
* Describe a specification or test-case | |
* with the given `title` and callback `fn` | |
* acting as a thunk. | |
*/ | |
context.it = context.specify = function(title, fn){ | |
var suite = suites[0]; | |
if (suite.pending) var fn = null; | |
var test = new Test(title, fn); | |
suite.addTest(test); | |
return test; | |
}; | |
/** | |
* Exclusive test-case. | |
*/ | |
context.it.only = function(title, fn){ | |
var test = context.it(title, fn); | |
var reString = '^' + utils.escapeRegexp(test.fullTitle()) + '$'; | |
mocha.grep(new RegExp(reString)); | |
return test; | |
}; | |
/** | |
* Pending test case. | |
*/ | |
context.xit = | |
context.xspecify = | |
context.it.skip = function(title){ | |
context.it(title); | |
}; | |
}); | |
}; | |
}); // module: interfaces/bdd.js | |
require.register("interfaces/exports.js", function(module, exports, require){ | |
/** | |
* Module dependencies. | |
*/ | |
var Suite = require('../suite') | |
, Test = require('../test'); | |
/** | |
* TDD-style interface: | |
* | |
* exports.Array = { | |
* '#indexOf()': { | |
* 'should return -1 when the value is not present': function(){ | |
* | |
* }, | |
* | |
* 'should return the correct index when the value is present': function(){ | |
* | |
* } | |
* } | |
* }; | |
* | |
*/ | |
module.exports = function(suite){ | |
var suites = [suite]; | |
suite.on('require', visit); | |
function visit(obj) { | |
var suite; | |
for (var key in obj) { | |
if ('function' == typeof obj[key]) { | |
var fn = obj[key]; | |
switch (key) { | |
case 'before': | |
suites[0].beforeAll(fn); | |
break; | |
case 'after': | |
suites[0].afterAll(fn); | |
break; | |
case 'beforeEach': | |
suites[0].beforeEach(fn); | |
break; | |
case 'afterEach': | |
suites[0].afterEach(fn); | |
break; | |
default: | |
suites[0].addTest(new Test(key, fn)); | |
} | |
} else { | |
var suite = Suite.create(suites[0], key); | |
suites.unshift(suite); | |
visit(obj[key]); | |
suites.shift(); | |
} | |
} | |
} | |
}; | |
}); // module: interfaces/exports.js | |
require.register("interfaces/index.js", function(module, exports, require){ | |
exports.bdd = require('./bdd'); | |
exports.tdd = require('./tdd'); | |
exports.qunit = require('./qunit'); | |
exports.exports = require('./exports'); | |
}); // module: interfaces/index.js | |
require.register("interfaces/qunit.js", function(module, exports, require){ | |
/** | |
* Module dependencies. | |
*/ | |
var Suite = require('../suite') | |
, Test = require('../test') | |
, utils = require('../utils'); | |
/** | |
* QUnit-style interface: | |
* | |
* suite('Array'); | |
* | |
* test('#length', function(){ | |
* var arr = [1,2,3]; | |
* ok(arr.length == 3); | |
* }); | |
* | |
* test('#indexOf()', function(){ | |
* var arr = [1,2,3]; | |
* ok(arr.indexOf(1) == 0); | |
* ok(arr.indexOf(2) == 1); | |
* ok(arr.indexOf(3) == 2); | |
* }); | |
* | |
* suite('String'); | |
* | |
* test('#length', function(){ | |
* ok('foo'.length == 3); | |
* }); | |
* | |
*/ | |
module.exports = function(suite){ | |
var suites = [suite]; | |
suite.on('pre-require', function(context, file, mocha){ | |
/** | |
* Execute before running tests. | |
*/ | |
context.before = function(fn){ | |
suites[0].beforeAll(fn); | |
}; | |
/** | |
* Execute after running tests. | |
*/ | |
context.after = function(fn){ | |
suites[0].afterAll(fn); | |
}; | |
/** | |
* Execute before each test case. | |
*/ | |
context.beforeEach = function(fn){ | |
suites[0].beforeEach(fn); | |
}; | |
/** | |
* Execute after each test case. | |
*/ | |
context.afterEach = function(fn){ | |
suites[0].afterEach(fn); | |
}; | |
/** | |
* Describe a "suite" with the given `title`. | |
*/ | |
context.suite = function(title){ | |
if (suites.length > 1) suites.shift(); | |
var suite = Suite.create(suites[0], title); | |
suites.unshift(suite); | |
return suite; | |
}; | |
/** | |
* Exclusive test-case. | |
*/ | |
context.suite.only = function(title, fn){ | |
var suite = context.suite(title, fn); | |
mocha.grep(suite.fullTitle()); | |
}; | |
/** | |
* Describe a specification or test-case | |
* with the given `title` and callback `fn` | |
* acting as a thunk. | |
*/ | |
context.test = function(title, fn){ | |
var test = new Test(title, fn); | |
suites[0].addTest(test); | |
return test; | |
}; | |
/** | |
* Exclusive test-case. | |
*/ | |
context.test.only = function(title, fn){ | |
var test = context.test(title, fn); | |
var reString = '^' + utils.escapeRegexp(test.fullTitle()) + '$'; | |
mocha.grep(new RegExp(reString)); | |
}; | |
/** | |
* Pending test case. | |
*/ | |
context.test.skip = function(title){ | |
context.test(title); | |
}; | |
}); | |
}; | |
}); // module: interfaces/qunit.js | |
require.register("interfaces/tdd.js", function(module, exports, require){ | |
/** | |
* Module dependencies. | |
*/ | |
var Suite = require('../suite') | |
, Test = require('../test') | |
, utils = require('../utils');; | |
/** | |
* TDD-style interface: | |
* | |
* suite('Array', function(){ | |
* suite('#indexOf()', function(){ | |
* suiteSetup(function(){ | |
* | |
* }); | |
* | |
* test('should return -1 when not present', function(){ | |
* | |
* }); | |
* | |
* test('should return the index when present', function(){ | |
* | |
* }); | |
* | |
* suiteTeardown(function(){ | |
* | |
* }); | |
* }); | |
* }); | |
* | |
*/ | |
module.exports = function(suite){ | |
var suites = [suite]; | |
suite.on('pre-require', function(context, file, mocha){ | |
/** | |
* Execute before each test case. | |
*/ | |
context.setup = function(fn){ | |
suites[0].beforeEach(fn); | |
}; | |
/** | |
* Execute after each test case. | |
*/ | |
context.teardown = function(fn){ | |
suites[0].afterEach(fn); | |
}; | |
/** | |
* Execute before the suite. | |
*/ | |
context.suiteSetup = function(fn){ | |
suites[0].beforeAll(fn); | |
}; | |
/** | |
* Execute after the suite. | |
*/ | |
context.suiteTeardown = function(fn){ | |
suites[0].afterAll(fn); | |
}; | |
/** | |
* Describe a "suite" with the given `title` | |
* and callback `fn` containing nested suites | |
* and/or tests. | |
*/ | |
context.suite = function(title, fn){ | |
var suite = Suite.create(suites[0], title); | |
suites.unshift(suite); | |
fn.call(suite); | |
suites.shift(); | |
return suite; | |
}; | |
/** | |
* Pending suite. | |
*/ | |
context.suite.skip = function(title, fn) { | |
var suite = Suite.create(suites[0], title); | |
suite.pending = true; | |
suites.unshift(suite); | |
fn.call(suite); | |
suites.shift(); | |
}; | |
/** | |
* Exclusive test-case. | |
*/ | |
context.suite.only = function(title, fn){ | |
var suite = context.suite(title, fn); | |
mocha.grep(suite.fullTitle()); | |
}; | |
/** | |
* Describe a specification or test-case | |
* with the given `title` and callback `fn` | |
* acting as a thunk. | |
*/ | |
context.test = function(title, fn){ | |
var suite = suites[0]; | |
if (suite.pending) var fn = null; | |
var test = new Test(title, fn); | |
suite.addTest(test); | |
return test; | |
}; | |
/** | |
* Exclusive test-case. | |
*/ | |
context.test.only = function(title, fn){ | |
var test = context.test(title, fn); | |
var reString = '^' + utils.escapeRegexp(test.fullTitle()) + '$'; | |
mocha.grep(new RegExp(reString)); | |
}; | |
/** | |
* Pending test case. | |
*/ | |
context.test.skip = function(title){ | |
context.test(title); | |
}; | |
}); | |
}; | |
}); // module: interfaces/tdd.js | |
require.register("mocha.js", function(module, exports, require){ | |
/*! | |
* mocha | |
* Copyright(c) 2011 TJ Holowaychuk <tj@vision-media.ca> | |
* MIT Licensed | |
*/ | |
/** | |
* Module dependencies. | |
*/ | |
var path = require('browser/path') | |
, utils = require('./utils'); | |
/** | |
* Expose `Mocha`. | |
*/ | |
exports = module.exports = Mocha; | |
/** | |
* Expose internals. | |
*/ | |
exports.utils = utils; | |
exports.interfaces = require('./interfaces'); | |
exports.reporters = require('./reporters'); | |
exports.Runnable = require('./runnable'); | |
exports.Context = require('./context'); | |
exports.Runner = require('./runner'); | |
exports.Suite = require('./suite'); | |
exports.Hook = require('./hook'); | |
exports.Test = require('./test'); | |
/** | |
* Return image `name` path. | |
* | |
* @param {String} name | |
* @return {String} | |
* @api private | |
*/ | |
function image(name) { | |
return __dirname + '/../images/' + name + '.png'; | |
} | |
/** | |
* Setup mocha with `options`. | |
* | |
* Options: | |
* | |
* - `ui` name "bdd", "tdd", "exports" etc | |
* - `reporter` reporter instance, defaults to `mocha.reporters.Dot` | |
* - `globals` array of accepted globals | |
* - `timeout` timeout in milliseconds | |
* - `bail` bail on the first test failure | |
* - `slow` milliseconds to wait before considering a test slow | |
* - `ignoreLeaks` ignore global leaks | |
* - `grep` string or regexp to filter tests with | |
* | |
* @param {Object} options | |
* @api public | |
*/ | |
function Mocha(options) { | |
options = options || {}; | |
this.files = []; | |
this.options = options; | |
this.grep(options.grep); | |
this.suite = new exports.Suite('', new exports.Context); | |
this.ui(options.ui); | |
this.bail(options.bail); | |
this.reporter(options.reporter); | |
if (null != options.timeout) this.timeout(options.timeout); | |
this.useColors(options.useColors) | |
if (options.slow) this.slow(options.slow); | |
} | |
/** | |
* Enable or disable bailing on the first failure. | |
* | |
* @param {Boolean} [bail] | |
* @api public | |
*/ | |
Mocha.prototype.bail = function(bail){ | |
if (0 == arguments.length) bail = true; | |
this.suite.bail(bail); | |
return this; | |
}; | |
/** | |
* Add test `file`. | |
* | |
* @param {String} file | |
* @api public | |
*/ | |
Mocha.prototype.addFile = function(file){ | |
this.files.push(file); | |
return this; | |
}; | |
/** | |
* Set reporter to `reporter`, defaults to "dot". | |
* | |
* @param {String|Function} reporter name or constructor | |
* @api public | |
*/ | |
Mocha.prototype.reporter = function(reporter){ | |
if ('function' == typeof reporter) { | |
this._reporter = reporter; | |
} else { | |
reporter = reporter || 'dot'; | |
var _reporter; | |
try { _reporter = require('./reporters/' + reporter); } catch (err) {}; | |
if (!_reporter) try { _reporter = require(reporter); } catch (err) {}; | |
if (!_reporter && reporter === 'teamcity') | |
console.warn('The Teamcity reporter was moved to a package named ' + | |
'mocha-teamcity-reporter ' + | |
'(https://npmjs.org/package/mocha-teamcity-reporter).'); | |
if (!_reporter) throw new Error('invalid reporter "' + reporter + '"'); | |
this._reporter = _reporter; | |
} | |
return this; | |
}; | |
/** | |
* Set test UI `name`, defaults to "bdd". | |
* | |
* @param {String} bdd | |
* @api public | |
*/ | |
Mocha.prototype.ui = function(name){ | |
name = name || 'bdd'; | |
this._ui = exports.interfaces[name]; | |
if (!this._ui) try { this._ui = require(name); } catch (err) {}; | |
if (!this._ui) throw new Error('invalid interface "' + name + '"'); | |
this._ui = this._ui(this.suite); | |
return this; | |
}; | |
/** | |
* Load registered files. | |
* | |
* @api private | |
*/ | |
Mocha.prototype.loadFiles = function(fn){ | |
var self = this; | |
var suite = this.suite; | |
var pending = this.files.length; | |
this.files.forEach(function(file){ | |
file = path.resolve(file); | |
suite.emit('pre-require', global, file, self); | |
suite.emit('require', require(file), file, self); | |
suite.emit('post-require', global, file, self); | |
--pending || (fn && fn()); | |
}); | |
}; | |
/** | |
* Enable growl support. | |
* | |
* @api private | |
*/ | |
Mocha.prototype._growl = function(runner, reporter) { | |
var notify = require('growl'); | |
runner.on('end', function(){ | |
var stats = reporter.stats; | |
if (stats.failures) { | |
var msg = stats.failures + ' of ' + runner.total + ' tests failed'; | |
notify(msg, { name: 'mocha', title: 'Failed', image: image('error') }); | |
} else { | |
notify(stats.passes + ' tests passed in ' + stats.duration + 'ms', { | |
name: 'mocha' | |
, title: 'Passed' | |
, image: image('ok') | |
}); | |
} | |
}); | |
}; | |
/** | |
* Add regexp to grep, if `re` is a string it is escaped. | |
* | |
* @param {RegExp|String} re | |
* @return {Mocha} | |
* @api public | |
*/ | |
Mocha.prototype.grep = function(re){ | |
this.options.grep = 'string' == typeof re | |
? new RegExp(utils.escapeRegexp(re)) | |
: re; | |
return this; | |
}; | |
/** | |
* Invert `.grep()` matches. | |
* | |
* @return {Mocha} | |
* @api public | |
*/ | |
Mocha.prototype.invert = function(){ | |
this.options.invert = true; | |
return this; | |
}; | |
/** | |
* Ignore global leaks. | |
* | |
* @param {Boolean} ignore | |
* @return {Mocha} | |
* @api public | |
*/ | |
Mocha.prototype.ignoreLeaks = function(ignore){ | |
this.options.ignoreLeaks = !!ignore; | |
return this; | |
}; | |
/** | |
* Enable global leak checking. | |
* | |
* @return {Mocha} | |
* @api public | |
*/ | |
Mocha.prototype.checkLeaks = function(){ | |
this.options.ignoreLeaks = false; | |
return this; | |
}; | |
/** | |
* Enable growl support. | |
* | |
* @return {Mocha} | |
* @api public | |
*/ | |
Mocha.prototype.growl = function(){ | |
this.options.growl = true; | |
return this; | |
}; | |
/** | |
* Ignore `globals` array or string. | |
* | |
* @param {Array|String} globals | |
* @return {Mocha} | |
* @api public | |
*/ | |
Mocha.prototype.globals = function(globals){ | |
this.options.globals = (this.options.globals || []).concat(globals); | |
return this; | |
}; | |
/** | |
* Emit color output. | |
* | |
* @param {Boolean} colors | |
* @return {Mocha} | |
* @api public | |
*/ | |
Mocha.prototype.useColors = function(colors){ | |
this.options.useColors = arguments.length && colors != undefined | |
? colors | |
: true; | |
return this; | |
}; | |
/** | |
* Set the timeout in milliseconds. | |
* | |
* @param {Number} timeout | |
* @return {Mocha} | |
* @api public | |
*/ | |
Mocha.prototype.timeout = function(timeout){ | |
this.suite.timeout(timeout); | |
return this; | |
}; | |
/** | |
* Set slowness threshold in milliseconds. | |
* | |
* @param {Number} slow | |
* @return {Mocha} | |
* @api public | |
*/ | |
Mocha.prototype.slow = function(slow){ | |
this.suite.slow(slow); | |
return this; | |
}; | |
/** | |
* Makes all tests async (accepting a callback) | |
* | |
* @return {Mocha} | |
* @api public | |
*/ | |
Mocha.prototype.asyncOnly = function(){ | |
this.options.asyncOnly = true; | |
return this; | |
}; | |
/** | |
* Run tests and invoke `fn()` when complete. | |
* | |
* @param {Function} fn | |
* @return {Runner} | |
* @api public | |
*/ | |
Mocha.prototype.run = function(fn){ | |
if (this.files.length) this.loadFiles(); | |
var suite = this.suite; | |
var options = this.options; | |
var runner = new exports.Runner(suite); | |
var reporter = new this._reporter(runner); | |
runner.ignoreLeaks = false !== options.ignoreLeaks; | |
runner.asyncOnly = options.asyncOnly; | |
if (options.grep) runner.grep(options.grep, options.invert); | |
if (options.globals) runner.globals(options.globals); | |
if (options.growl) this._growl(runner, reporter); | |
exports.reporters.Base.useColors = options.useColors; | |
return runner.run(fn); | |
}; | |
}); // module: mocha.js | |
require.register("ms.js", function(module, exports, require){ | |
/** | |
* Helpers. | |
*/ | |
var s = 1000; | |
var m = s * 60; | |
var h = m * 60; | |
var d = h * 24; | |
var y = d * 365.25; | |
/** | |
* Parse or format the given `val`. | |
* | |
* Options: | |
* | |
* - `long` verbose formatting [false] | |
* | |
* @param {String|Number} val | |
* @param {Object} options | |
* @return {String|Number} | |
* @api public | |
*/ | |
module.exports = function(val, options){ | |
options = options || {}; | |
if ('string' == typeof val) return parse(val); | |
return options.long | |
? long(val) | |
: short(val); | |
}; | |
/** | |
* Parse the given `str` and return milliseconds. | |
* | |
* @param {String} str | |
* @return {Number} | |
* @api private | |
*/ | |
function parse(str) { | |
var match = /^((?:\d+)?\.?\d+) *(ms|seconds?|s|minutes?|m|hours?|h|days?|d|years?|y)?$/i.exec(str); | |
if (!match) return; | |
var n = parseFloat(match[1]); | |
var type = (match[2] || 'ms').toLowerCase(); | |
switch (type) { | |
case 'years': | |
case 'year': | |
case 'y': | |
return n * y; | |
case 'days': | |
case 'day': | |
case 'd': | |
return n * d; | |
case 'hours': | |
case 'hour': | |
case 'h': | |
return n * h; | |
case 'minutes': | |
case 'minute': | |
case 'm': | |
return n * m; | |
case 'seconds': | |
case 'second': | |
case 's': | |
return n * s; | |
case 'ms': | |
return n; | |
} | |
} | |
/** | |
* Short format for `ms`. | |
* | |
* @param {Number} ms | |
* @return {String} | |
* @api private | |
*/ | |
function short(ms) { | |
if (ms >= d) return Math.round(ms / d) + 'd'; | |
if (ms >= h) return Math.round(ms / h) + 'h'; | |
if (ms >= m) return Math.round(ms / m) + 'm'; | |
if (ms >= s) return Math.round(ms / s) + 's'; | |
return ms + 'ms'; | |
} | |
/** | |
* Long format for `ms`. | |
* | |
* @param {Number} ms | |
* @return {String} | |
* @api private | |
*/ | |
function long(ms) { | |
return plural(ms, d, 'day') | |
|| plural(ms, h, 'hour') | |
|| plural(ms, m, 'minute') | |
|| plural(ms, s, 'second') | |
|| ms + ' ms'; | |
} | |
/** | |
* Pluralization helper. | |
*/ | |
function plural(ms, n, name) { | |
if (ms < n) return; | |
if (ms < n * 1.5) return Math.floor(ms / n) + ' ' + name; | |
return Math.ceil(ms / n) + ' ' + name + 's'; | |
} | |
}); // module: ms.js | |
require.register("reporters/base.js", function(module, exports, require){ | |
/** | |
* Module dependencies. | |
*/ | |
var tty = require('browser/tty') | |
, diff = require('browser/diff') | |
, ms = require('../ms'); | |
/** | |
* Save timer references to avoid Sinon interfering (see GH-237). | |
*/ | |
var Date = global.Date | |
, setTimeout = global.setTimeout | |
, setInterval = global.setInterval | |
, clearTimeout = global.clearTimeout | |
, clearInterval = global.clearInterval; | |
/** | |
* Check if both stdio streams are associated with a tty. | |
*/ | |
var isatty = tty.isatty(1) && tty.isatty(2); | |
/** | |
* Expose `Base`. | |
*/ | |
exports = module.exports = Base; | |
/** | |
* Enable coloring by default. | |
*/ | |
exports.useColors = isatty || (process.env.MOCHA_COLORS !== undefined); | |
/** | |
* Inline diffs instead of +/- | |
*/ | |
exports.inlineDiffs = false; | |
/** | |
* Default color map. | |
*/ | |
exports.colors = { | |
'pass': 90 | |
, 'fail': 31 | |
, 'bright pass': 92 | |
, 'bright fail': 91 | |
, 'bright yellow': 93 | |
, 'pending': 36 | |
, 'suite': 0 | |
, 'error title': 0 | |
, 'error message': 31 | |
, 'error stack': 90 | |
, 'checkmark': 32 | |
, 'fast': 90 | |
, 'medium': 33 | |
, 'slow': 31 | |
, 'green': 32 | |
, 'light': 90 | |
, 'diff gutter': 90 | |
, 'diff added': 42 | |
, 'diff removed': 41 | |
}; | |
/** | |
* Default symbol map. | |
*/ | |
exports.symbols = { | |
ok: '✓', | |
err: '✖', | |
dot: '․' | |
}; | |
// With node.js on Windows: use symbols available in terminal default fonts | |
if ('win32' == process.platform) { | |
exports.symbols.ok = '\u221A'; | |
exports.symbols.err = '\u00D7'; | |
exports.symbols.dot = '.'; | |
} | |
/** | |
* Color `str` with the given `type`, | |
* allowing colors to be disabled, | |
* as well as user-defined color | |
* schemes. | |
* | |
* @param {String} type | |
* @param {String} str | |
* @return {String} | |
* @api private | |
*/ | |
var color = exports.color = function(type, str) { | |
if (!exports.useColors) return str; | |
return '\u001b[' + exports.colors[type] + 'm' + str + '\u001b[0m'; | |
}; | |
/** | |
* Expose term window size, with some | |
* defaults for when stderr is not a tty. | |
*/ | |
exports.window = { | |
width: isatty | |
? process.stdout.getWindowSize | |
? process.stdout.getWindowSize(1)[0] | |
: tty.getWindowSize()[1] | |
: 75 | |
}; | |
/** | |
* Expose some basic cursor interactions | |
* that are common among reporters. | |
*/ | |
exports.cursor = { | |
hide: function(){ | |
isatty && process.stdout.write('\u001b[?25l'); | |
}, | |
show: function(){ | |
isatty && process.stdout.write('\u001b[?25h'); | |
}, | |
deleteLine: function(){ | |
isatty && process.stdout.write('\u001b[2K'); | |
}, | |
beginningOfLine: function(){ | |
isatty && process.stdout.write('\u001b[0G'); | |
}, | |
CR: function(){ | |
if (isatty) { | |
exports.cursor.deleteLine(); | |
exports.cursor.beginningOfLine(); | |
} else { | |
process.stdout.write('\n'); | |
} | |
} | |
}; | |
/** | |
* Outut the given `failures` as a list. | |
* | |
* @param {Array} failures | |
* @api public | |
*/ | |
exports.list = function(failures){ | |
console.error(); | |
failures.forEach(function(test, i){ | |
// format | |
var fmt = color('error title', ' %s) %s:\n') | |
+ color('error message', ' %s') | |
+ color('error stack', '\n%s\n'); | |
// msg | |
var err = test.err | |
, message = err.message || '' | |
, stack = err.stack || message | |
, index = stack.indexOf(message) + message.length | |
, msg = stack.slice(0, index) | |
, actual = err.actual | |
, expected = err.expected | |
, escape = true; | |
// uncaught | |
if (err.uncaught) { | |
msg = 'Uncaught ' + msg; | |
} | |
// explicitly show diff | |
if (err.showDiff && sameType(actual, expected)) { | |
escape = false; | |
err.actual = actual = stringify(actual); | |
err.expected = expected = stringify(expected); | |
} | |
// actual / expected diff | |
if ('string' == typeof actual && 'string' == typeof expected) { | |
fmt = color('error title', ' %s) %s:\n%s') + color('error stack', '\n%s\n'); | |
var match = message.match(/^([^:]+): expected/); | |
msg = match ? '\n ' + color('error message', match[1]) : ''; | |
if (exports.inlineDiffs) { | |
msg += inlineDiff(err, escape); | |
} else { | |
msg += unifiedDiff(err, escape); | |
} | |
} | |
// indent stack trace without msg | |
stack = stack.slice(index ? index + 1 : index) | |
.replace(/^/gm, ' '); | |
console.error(fmt, (i + 1), test.fullTitle(), msg, stack); | |
}); | |
}; | |
/** | |
* Initialize a new `Base` reporter. | |
* | |
* All other reporters generally | |
* inherit from this reporter, providing | |
* stats such as test duration, number | |
* of tests passed / failed etc. | |
* | |
* @param {Runner} runner | |
* @api public | |
*/ | |
function Base(runner) { | |
var self = this | |
, stats = this.stats = { suites: 0, tests: 0, passes: 0, pending: 0, failures: 0 } | |
, failures = this.failures = []; | |
if (!runner) return; | |
this.runner = runner; | |
runner.stats = stats; | |
runner.on('start', function(){ | |
stats.start = new Date; | |
}); | |
runner.on('suite', function(suite){ | |
stats.suites = stats.suites || 0; | |
suite.root || stats.suites++; | |
}); | |
runner.on('test end', function(test){ | |
stats.tests = stats.tests || 0; | |
stats.tests++; | |
}); | |
runner.on('pass', function(test){ | |
stats.passes = stats.passes || 0; | |
var medium = test.slow() / 2; | |
test.speed = test.duration > test.slow() | |
? 'slow' | |
: test.duration > medium | |
? 'medium' | |
: 'fast'; | |
stats.passes++; | |
}); | |
runner.on('fail', function(test, err){ | |
stats.failures = stats.failures || 0; | |
stats.failures++; | |
test.err = err; | |
failures.push(test); | |
}); | |
runner.on('end', function(){ | |
stats.end = new Date; | |
stats.duration = new Date - stats.start; | |
}); | |
runner.on('pending', function(){ | |
stats.pending++; | |
}); | |
} | |
/** | |
* Output common epilogue used by many of | |
* the bundled reporters. | |
* | |
* @api public | |
*/ | |
Base.prototype.epilogue = function(){ | |
var stats = this.stats; | |
var tests; | |
var fmt; | |
console.log(); | |
// passes | |
fmt = color('bright pass', ' ') | |
+ color('green', ' %d passing') | |
+ color('light', ' (%s)'); | |
console.log(fmt, | |
stats.passes || 0, | |
ms(stats.duration)); | |
// pending | |
if (stats.pending) { | |
fmt = color('pending', ' ') | |
+ color('pending', ' %d pending'); | |
console.log(fmt, stats.pending); | |
} | |
// failures | |
if (stats.failures) { | |
fmt = color('fail', ' %d failing'); | |
console.error(fmt, | |
stats.failures); | |
Base.list(this.failures); | |
console.error(); | |
} | |
console.log(); | |
}; | |
/** | |
* Pad the given `str` to `len`. | |
* | |
* @param {String} str | |
* @param {String} len | |
* @return {String} | |
* @api private | |
*/ | |
function pad(str, len) { | |
str = String(str); | |
return Array(len - str.length + 1).join(' ') + str; | |
} | |
/** | |
* Returns an inline diff between 2 strings with coloured ANSI output | |
* | |
* @param {Error} Error with actual/expected | |
* @return {String} Diff | |
* @api private | |
*/ | |
function inlineDiff(err, escape) { | |
var msg = errorDiff(err, 'WordsWithSpace', escape); | |
// linenos | |
var lines = msg.split('\n'); | |
if (lines.length > 4) { | |
var width = String(lines.length).length; | |
msg = lines.map(function(str, i){ | |
return pad(++i, width) + ' |' + ' ' + str; | |
}).join('\n'); | |
} | |
// legend | |
msg = '\n' | |
+ color('diff removed', 'actual') | |
+ ' ' | |
+ color('diff added', 'expected') | |
+ '\n\n' | |
+ msg | |
+ '\n'; | |
// indent | |
msg = msg.replace(/^/gm, ' '); | |
return msg; | |
} | |
/** | |
* Returns a unified diff between 2 strings | |
* | |
* @param {Error} Error with actual/expected | |
* @return {String} Diff | |
* @api private | |
*/ | |
function unifiedDiff(err, escape) { | |
var indent = ' '; | |
function cleanUp(line) { | |
if (escape) { | |
line = escapeInvisibles(line); | |
} | |
if (line[0] === '+') return indent + colorLines('diff added', line); | |
if (line[0] === '-') return indent + colorLines('diff removed', line); | |
if (line.match(/\@\@/)) return null; | |
if (line.match(/\\ No newline/)) return null; | |
else return indent + line; | |
} | |
function notBlank(line) { | |
return line != null; | |
} | |
msg = diff.createPatch('string', err.actual, err.expected); | |
var lines = msg.split('\n').splice(4); | |
return '\n ' | |
+ colorLines('diff added', '+ expected') + ' ' | |
+ colorLines('diff removed', '- actual') | |
+ '\n\n' | |
+ lines.map(cleanUp).filter(notBlank).join('\n'); | |
} | |
/** | |
* Return a character diff for `err`. | |
* | |
* @param {Error} err | |
* @return {String} | |
* @api private | |
*/ | |
function errorDiff(err, type, escape) { | |
var actual = escape ? escapeInvisibles(err.actual) : err.actual; | |
var expected = escape ? escapeInvisibles(err.expected) : err.expected; | |
return diff['diff' + type](actual, expected).map(function(str){ | |
if (str.added) return colorLines('diff added', str.value); | |
if (str.removed) return colorLines('diff removed', str.value); | |
return str.value; | |
}).join(''); | |
} | |
/** | |
* Returns a string with all invisible characters in plain text | |
* | |
* @param {String} line | |
* @return {String} | |
* @api private | |
*/ | |
function escapeInvisibles(line) { | |
return line.replace(/\t/g, '<tab>') | |
.replace(/\r/g, '<CR>') | |
.replace(/\n/g, '<LF>\n'); | |
} | |
/** | |
* Color lines for `str`, using the color `name`. | |
* | |
* @param {String} name | |
* @param {String} str | |
* @return {String} | |
* @api private | |
*/ | |
function colorLines(name, str) { | |
return str.split('\n').map(function(str){ | |
return color(name, str); | |
}).join('\n'); | |
} | |
/** | |
* Stringify `obj`. | |
* | |
* @param {Mixed} obj | |
* @return {String} | |
* @api private | |
*/ | |
function stringify(obj) { | |
if (obj instanceof RegExp) return obj.toString(); | |
return JSON.stringify(obj, null, 2); | |
} | |
/** | |
* Check that a / b have the same type. | |
* | |
* @param {Object} a | |
* @param {Object} b | |
* @return {Boolean} | |
* @api private | |
*/ | |
function sameType(a, b) { | |
a = Object.prototype.toString.call(a); | |
b = Object.prototype.toString.call(b); | |
return a == b; | |
} | |
}); // module: reporters/base.js | |
require.register("reporters/doc.js", function(module, exports, require){ | |
/** | |
* Module dependencies. | |
*/ | |
var Base = require('./base') | |
, utils = require('../utils'); | |
/** | |
* Expose `Doc`. | |
*/ | |
exports = module.exports = Doc; | |
/** | |
* Initialize a new `Doc` reporter. | |
* | |
* @param {Runner} runner | |
* @api public | |
*/ | |
function Doc(runner) { | |
Base.call(this, runner); | |
var self = this | |
, stats = this.stats | |
, total = runner.total | |
, indents = 2; | |
function indent() { | |
return Array(indents).join(' '); | |
} | |
runner.on('suite', function(suite){ | |
if (suite.root) return; | |
++indents; | |
console.log('%s<section class="suite">', indent()); | |
++indents; | |
console.log('%s<h1>%s</h1>', indent(), utils.escape(suite.title)); | |
console.log('%s<dl>', indent()); | |
}); | |
runner.on('suite end', function(suite){ | |
if (suite.root) return; | |
console.log('%s</dl>', indent()); | |
--indents; | |
console.log('%s</section>', indent()); | |
--indents; | |
}); | |
runner.on('pass', function(test){ | |
console.log('%s <dt>%s</dt>', indent(), utils.escape(test.title)); | |
var code = utils.escape(utils.clean(test.fn.toString())); | |
console.log('%s <dd><pre><code>%s</code></pre></dd>', indent(), code); | |
}); | |
} | |
}); // module: reporters/doc.js | |
require.register("reporters/dot.js", function(module, exports, require){ | |
/** | |
* Module dependencies. | |
*/ | |
var Base = require('./base') | |
, color = Base.color; | |
/** | |
* Expose `Dot`. | |
*/ | |
exports = module.exports = Dot; | |
/** | |
* Initialize a new `Dot` matrix test reporter. | |
* | |
* @param {Runner} runner | |
* @api public | |
*/ | |
function Dot(runner) { | |
Base.call(this, runner); | |
var self = this | |
, stats = this.stats | |
, width = Base.window.width * .75 | 0 | |
, n = 0; | |
runner.on('start', function(){ | |
process.stdout.write('\n '); | |
}); | |
runner.on('pending', function(test){ | |
process.stdout.write(color('pending', Base.symbols.dot)); | |
}); | |
runner.on('pass', function(test){ | |
if (++n % width == 0) process.stdout.write('\n '); | |
if ('slow' == test.speed) { | |
process.stdout.write(color('bright yellow', Base.symbols.dot)); | |
} else { | |
process.stdout.write(color(test.speed, Base.symbols.dot)); | |
} | |
}); | |
runner.on('fail', function(test, err){ | |
if (++n % width == 0) process.stdout.write('\n '); | |
process.stdout.write(color('fail', Base.symbols.dot)); | |
}); | |
runner.on('end', function(){ | |
console.log(); | |
self.epilogue(); | |
}); | |
} | |
/** | |
* Inherit from `Base.prototype`. | |
*/ | |
function F(){}; | |
F.prototype = Base.prototype; | |
Dot.prototype = new F; | |
Dot.prototype.constructor = Dot; | |
}); // module: reporters/dot.js | |
require.register("reporters/html-cov.js", function(module, exports, require){ | |
/** | |
* Module dependencies. | |
*/ | |
var JSONCov = require('./json-cov') | |
, fs = require('browser/fs'); | |
/** | |
* Expose `HTMLCov`. | |
*/ | |
exports = module.exports = HTMLCov; | |
/** | |
* Initialize a new `JsCoverage` reporter. | |
* | |
* @param {Runner} runner | |
* @api public | |
*/ | |
function HTMLCov(runner) { | |
var jade = require('jade') | |
, file = __dirname + '/templates/coverage.jade' | |
, str = fs.readFileSync(file, 'utf8') | |
, fn = jade.compile(str, { filename: file }) | |
, self = this; | |
JSONCov.call(this, runner, false); | |
runner.on('end', function(){ | |
process.stdout.write(fn({ | |
cov: self.cov | |
, coverageClass: coverageClass | |
})); | |
}); | |
} | |
/** | |
* Return coverage class for `n`. | |
* | |
* @return {String} | |
* @api private | |
*/ | |
function coverageClass(n) { | |
if (n >= 75) return 'high'; | |
if (n >= 50) return 'medium'; | |
if (n >= 25) return 'low'; | |
return 'terrible'; | |
} | |
}); // module: reporters/html-cov.js | |
require.register("reporters/html.js", function(module, exports, require){ | |
/** | |
* Module dependencies. | |
*/ | |
var Base = require('./base') | |
, utils = require('../utils') | |
, Progress = require('../browser/progress') | |
, escape = utils.escape; | |
/** | |
* Save timer references to avoid Sinon interfering (see GH-237). | |
*/ | |
var Date = global.Date | |
, setTimeout = global.setTimeout | |
, setInterval = global.setInterval | |
, clearTimeout = global.clearTimeout | |
, clearInterval = global.clearInterval; | |
/** | |
* Expose `HTML`. | |
*/ | |
exports = module.exports = HTML; | |
/** | |
* Stats template. | |
*/ | |
var statsTemplate = '<ul id="mocha-stats">' | |
+ '<li class="progress"><canvas width="40" height="40"></canvas></li>' | |
+ '<li class="passes"><a href="#">passes:</a> <em>0</em></li>' | |
+ '<li class="failures"><a href="#">failures:</a> <em>0</em></li>' | |
+ '<li class="duration">duration: <em>0</em>s</li>' | |
+ '</ul>'; | |
/** | |
* Initialize a new `HTML` reporter. | |
* | |
* @param {Runner} runner | |
* @api public | |
*/ | |
function HTML(runner, root) { | |
Base.call(this, runner); | |
var self = this | |
, stats = this.stats | |
, total = runner.total | |
, stat = fragment(statsTemplate) | |
, items = stat.getElementsByTagName('li') | |
, passes = items[1].getElementsByTagName('em')[0] | |
, passesLink = items[1].getElementsByTagName('a')[0] | |
, failures = items[2].getElementsByTagName('em')[0] | |
, failuresLink = items[2].getElementsByTagName('a')[0] | |
, duration = items[3].getElementsByTagName('em')[0] | |
, canvas = stat.getElementsByTagName('canvas')[0] | |
, report = fragment('<ul id="mocha-report"></ul>') | |
, stack = [report] | |
, progress | |
, ctx | |
root = root || document.getElementById('mocha'); | |
if (canvas.getContext) { | |
var ratio = window.devicePixelRatio || 1; | |
canvas.style.width = canvas.width; | |
canvas.style.height = canvas.height; | |
canvas.width *= ratio; | |
canvas.height *= ratio; | |
ctx = canvas.getContext('2d'); | |
ctx.scale(ratio, ratio); | |
progress = new Progress; | |
} | |
if (!root) return error('#mocha div missing, add it to your document'); | |
// pass toggle | |
on(passesLink, 'click', function(){ | |
unhide(); | |
var name = /pass/.test(report.className) ? '' : ' pass'; | |
report.className = report.className.replace(/fail|pass/g, '') + name; | |
if (report.className.trim()) hideSuitesWithout('test pass'); | |
}); | |
// failure toggle | |
on(failuresLink, 'click', function(){ | |
unhide(); | |
var name = /fail/.test(report.className) ? '' : ' fail'; | |
report.className = report.className.replace(/fail|pass/g, '') + name; | |
if (report.className.trim()) hideSuitesWithout('test fail'); | |
}); | |
root.appendChild(stat); | |
root.appendChild(report); | |
if (progress) progress.size(40); | |
runner.on('suite', function(suite){ | |
if (suite.root) return; | |
// suite | |
var url = self.suiteURL(suite); | |
var el = fragment('<li class="suite"><h1><a href="%s">%s</a></h1></li>', url, escape(suite.title)); | |
// container | |
stack[0].appendChild(el); | |
stack.unshift(document.createElement('ul')); | |
el.appendChild(stack[0]); | |
}); | |
runner.on('suite end', function(suite){ | |
if (suite.root) return; | |
stack.shift(); | |
}); | |
runner.on('fail', function(test, err){ | |
if ('hook' == test.type) runner.emit('test end', test); | |
}); | |
runner.on('test end', function(test){ | |
// TODO: add to stats | |
var percent = stats.tests / this.total * 100 | 0; | |
if (progress) progress.update(percent).draw(ctx); | |
// update stats | |
var ms = new Date - stats.start; | |
text(passes, stats.passes); | |
text(failures, stats.failures); | |
text(duration, (ms / 1000).toFixed(2)); | |
// test | |
if ('passed' == test.state) { | |
var url = self.testURL(test); | |
var el = fragment('<li class="test pass %e"><h2>%e<span class="duration">%ems</span> <a href="%s" class="replay">‣</a></h2></li>', test.speed, test.title, test.duration, url); | |
} else if (test.pending) { | |
var el = fragment('<li class="test pass pending"><h2>%e</h2></li>', test.title); | |
} else { | |
var el = fragment('<li class="test fail"><h2>%e <a href="?grep=%e" class="replay">‣</a></h2></li>', test.title, encodeURIComponent(test.fullTitle())); | |
var str = test.err.stack || test.err.toString(); | |
// FF / Opera do not add the message | |
if (!~str.indexOf(test.err.message)) { | |
str = test.err.message + '\n' + str; | |
} | |
// <=IE7 stringifies to [Object Error]. Since it can be overloaded, we | |
// check for the result of the stringifying. | |
if ('[object Error]' == str) str = test.err.message; | |
// Safari doesn't give you a stack. Let's at least provide a source line. | |
if (!test.err.stack && test.err.sourceURL && test.err.line !== undefined) { | |
str += "\n(" + test.err.sourceURL + ":" + test.err.line + ")"; | |
} | |
el.appendChild(fragment('<pre class="error">%e</pre>', str)); | |
} | |
// toggle code | |
// TODO: defer | |
if (!test.pending) { | |
var h2 = el.getElementsByTagName('h2')[0]; | |
on(h2, 'click', function(){ | |
pre.style.display = 'none' == pre.style.display | |
? 'block' | |
: 'none'; | |
}); | |
var pre = fragment('<pre><code>%e</code></pre>', utils.clean(test.fn.toString())); | |
el.appendChild(pre); | |
pre.style.display = 'none'; | |
} | |
// Don't call .appendChild if #mocha-report was already .shift()'ed off the stack. | |
if (stack[0]) stack[0].appendChild(el); | |
}); | |
} | |
/** | |
* Provide suite URL | |
* | |
* @param {Object} [suite] | |
*/ | |
HTML.prototype.suiteURL = function(suite){ | |
return '?grep=' + encodeURIComponent(suite.fullTitle()); | |
}; | |
/** | |
* Provide test URL | |
* | |
* @param {Object} [test] | |
*/ | |
HTML.prototype.testURL = function(test){ | |
return '?grep=' + encodeURIComponent(test.fullTitle()); | |
}; | |
/** | |
* Display error `msg`. | |
*/ | |
function error(msg) { | |
document.body.appendChild(fragment('<div id="mocha-error">%s</div>', msg)); | |
} | |
/** | |
* Return a DOM fragment from `html`. | |
*/ | |
function fragment(html) { | |
var args = arguments | |
, div = document.createElement('div') | |
, i = 1; | |
div.innerHTML = html.replace(/%([se])/g, function(_, type){ | |
switch (type) { | |
case 's': return String(args[i++]); | |
case 'e': return escape(args[i++]); | |
} | |
}); | |
return div.firstChild; | |
} | |
/** | |
* Check for suites that do not have elements | |
* with `classname`, and hide them. | |
*/ | |
function hideSuitesWithout(classname) { | |
var suites = document.getElementsByClassName('suite'); | |
for (var i = 0; i < suites.length; i++) { | |
var els = suites[i].getElementsByClassName(classname); | |
if (0 == els.length) suites[i].className += ' hidden'; | |
} | |
} | |
/** | |
* Unhide .hidden suites. | |
*/ | |
function unhide() { | |
var els = document.getElementsByClassName('suite hidden'); | |
for (var i = 0; i < els.length; ++i) { | |
els[i].className = els[i].className.replace('suite hidden', 'suite'); | |
} | |
} | |
/** | |
* Set `el` text to `str`. | |
*/ | |
function text(el, str) { | |
if (el.textContent) { | |
el.textContent = str; | |
} else { | |
el.innerText = str; | |
} | |
} | |
/** | |
* Listen on `event` with callback `fn`. | |
*/ | |
function on(el, event, fn) { | |
if (el.addEventListener) { | |
el.addEventListener(event, fn, false); | |
} else { | |
el.attachEvent('on' + event, fn); | |
} | |
} | |
}); // module: reporters/html.js | |
require.register("reporters/index.js", function(module, exports, require){ | |
exports.Base = require('./base'); | |
exports.Dot = require('./dot'); | |
exports.Doc = require('./doc'); | |
exports.TAP = require('./tap'); | |
exports.JSON = require('./json'); | |
exports.HTML = require('./html'); | |
exports.List = require('./list'); | |
exports.Min = require('./min'); | |
exports.Spec = require('./spec'); | |
exports.Nyan = require('./nyan'); | |
exports.XUnit = require('./xunit'); | |
exports.Markdown = require('./markdown'); | |
exports.Progress = require('./progress'); | |
exports.Landing = require('./landing'); | |
exports.JSONCov = require('./json-cov'); | |
exports.HTMLCov = require('./html-cov'); | |
exports.JSONStream = require('./json-stream'); | |
}); // module: reporters/index.js | |
require.register("reporters/json-cov.js", function(module, exports, require){ | |
/** | |
* Module dependencies. | |
*/ | |
var Base = require('./base'); | |
/** | |
* Expose `JSONCov`. | |
*/ | |
exports = module.exports = JSONCov; | |
/** | |
* Initialize a new `JsCoverage` reporter. | |
* | |
* @param {Runner} runner | |
* @param {Boolean} output | |
* @api public | |
*/ | |
function JSONCov(runner, output) { | |
var self = this | |
, output = 1 == arguments.length ? true : output; | |
Base.call(this, runner); | |
var tests = [] | |
, failures = [] | |
, passes = []; | |
runner.on('test end', function(test){ | |
tests.push(test); | |
}); | |
runner.on('pass', function(test){ | |
passes.push(test); | |
}); | |
runner.on('fail', function(test){ | |
failures.push(test); | |
}); | |
runner.on('end', function(){ | |
var cov = global._$jscoverage || {}; | |
var result = self.cov = map(cov); | |
result.stats = self.stats; | |
result.tests = tests.map(clean); | |
result.failures = failures.map(clean); | |
result.passes = passes.map(clean); | |
if (!output) return; | |
process.stdout.write(JSON.stringify(result, null, 2 )); | |
}); | |
} | |
/** | |
* Map jscoverage data to a JSON structure | |
* suitable for reporting. | |
* | |
* @param {Object} cov | |
* @return {Object} | |
* @api private | |
*/ | |
function map(cov) { | |
var ret = { | |
instrumentation: 'node-jscoverage' | |
, sloc: 0 | |
, hits: 0 | |
, misses: 0 | |
, coverage: 0 | |
, files: [] | |
}; | |
for (var filename in cov) { | |
var data = coverage(filename, cov[filename]); | |
ret.files.push(data); | |
ret.hits += data.hits; | |
ret.misses += data.misses; | |
ret.sloc += data.sloc; | |
} | |
ret.files.sort(function(a, b) { | |
return a.filename.localeCompare(b.filename); | |
}); | |
if (ret.sloc > 0) { | |
ret.coverage = (ret.hits / ret.sloc) * 100; | |
} | |
return ret; | |
}; | |
/** | |
* Map jscoverage data for a single source file | |
* to a JSON structure suitable for reporting. | |
* | |
* @param {String} filename name of the source file | |
* @param {Object} data jscoverage coverage data | |
* @return {Object} | |
* @api private | |
*/ | |
function coverage(filename, data) { | |
var ret = { | |
filename: filename, | |
coverage: 0, | |
hits: 0, | |
misses: 0, | |
sloc: 0, | |
source: {} | |
}; | |
data.source.forEach(function(line, num){ | |
num++; | |
if (data[num] === 0) { | |
ret.misses++; | |
ret.sloc++; | |
} else if (data[num] !== undefined) { | |
ret.hits++; | |
ret.sloc++; | |
} | |
ret.source[num] = { | |
source: line | |
, coverage: data[num] === undefined | |
? '' | |
: data[num] | |
}; | |
}); | |
ret.coverage = ret.hits / ret.sloc * 100; | |
return ret; | |
} | |
/** | |
* Return a plain-object representation of `test` | |
* free of cyclic properties etc. | |
* | |
* @param {Object} test | |
* @return {Object} | |
* @api private | |
*/ | |
function clean(test) { | |
return { | |
title: test.title | |
, fullTitle: test.fullTitle() | |
, duration: test.duration | |
} | |
} | |
}); // module: reporters/json-cov.js | |
require.register("reporters/json-stream.js", function(module, exports, require){ | |
/** | |
* Module dependencies. | |
*/ | |
var Base = require('./base') | |
, color = Base.color; | |
/** | |
* Expose `List`. | |
*/ | |
exports = module.exports = List; | |
/** | |
* Initialize a new `List` test reporter. | |
* | |
* @param {Runner} runner | |
* @api public | |
*/ | |
function List(runner) { | |
Base.call(this, runner); | |
var self = this | |
, stats = this.stats | |
, total = runner.total; | |
runner.on('start', function(){ | |
console.log(JSON.stringify(['start', { total: total }])); | |
}); | |
runner.on('pass', function(test){ | |
console.log(JSON.stringify(['pass', clean(test)])); | |
}); | |
runner.on('fail', function(test, err){ | |
console.log(JSON.stringify(['fail', clean(test)])); | |
}); | |
runner.on('end', function(){ | |
process.stdout.write(JSON.stringify(['end', self.stats])); | |
}); | |
} | |
/** | |
* Return a plain-object representation of `test` | |
* free of cyclic properties etc. | |
* | |
* @param {Object} test | |
* @return {Object} | |
* @api private | |
*/ | |
function clean(test) { | |
return { | |
title: test.title | |
, fullTitle: test.fullTitle() | |
, duration: test.duration | |
} | |
} | |
}); // module: reporters/json-stream.js | |
require.register("reporters/json.js", function(module, exports, require){ | |
/** | |
* Module dependencies. | |
*/ | |
var Base = require('./base') | |
, cursor = Base.cursor | |
, color = Base.color; | |
/** | |
* Expose `JSON`. | |
*/ | |
exports = module.exports = JSONReporter; | |
/** | |
* Initialize a new `JSON` reporter. | |
* | |
* @param {Runner} runner | |
* @api public | |
*/ | |
function JSONReporter(runner) { | |
var self = this; | |
Base.call(this, runner); | |
var tests = [] | |
, failures = [] | |
, passes = []; | |
runner.on('test end', function(test){ | |
tests.push(test); | |
}); | |
runner.on('pass', function(test){ | |
passes.push(test); | |
}); | |
runner.on('fail', function(test){ | |
failures.push(test); | |
}); | |
runner.on('end', function(){ | |
var obj = { | |
stats: self.stats | |
, tests: tests.map(clean) | |
, failures: failures.map(clean) | |
, passes: passes.map(clean) | |
}; | |
process.stdout.write(JSON.stringify(obj, null, 2)); | |
}); | |
} | |
/** | |
* Return a plain-object representation of `test` | |
* free of cyclic properties etc. | |
* | |
* @param {Object} test | |
* @return {Object} | |
* @api private | |
*/ | |
function clean(test) { | |
return { | |
title: test.title | |
, fullTitle: test.fullTitle() | |
, duration: test.duration | |
} | |
} | |
}); // module: reporters/json.js | |
require.register("reporters/landing.js", function(module, exports, require){ | |
/** | |
* Module dependencies. | |
*/ | |
var Base = require('./base') | |
, cursor = Base.cursor | |
, color = Base.color; | |
/** | |
* Expose `Landing`. | |
*/ | |
exports = module.exports = Landing; | |
/** | |
* Airplane color. | |
*/ | |
Base.colors.plane = 0; | |
/** | |
* Airplane crash color. | |
*/ | |
Base.colors['plane crash'] = 31; | |
/** | |
* Runway color. | |
*/ | |
Base.colors.runway = 90; | |
/** | |
* Initialize a new `Landing` reporter. | |
* | |
* @param {Runner} runner | |
* @api public | |
*/ | |
function Landing(runner) { | |
Base.call(this, runner); | |
var self = this | |
, stats = this.stats | |
, width = Base.window.width * .75 | 0 | |
, total = runner.total | |
, stream = process.stdout | |
, plane = color('plane', '✈') | |
, crashed = -1 | |
, n = 0; | |
function runway() { | |
var buf = Array(width).join('-'); | |
return ' ' + color('runway', buf); | |
} | |
runner.on('start', function(){ | |
stream.write('\n '); | |
cursor.hide(); | |
}); | |
runner.on('test end', function(test){ | |
// check if the plane crashed | |
var col = -1 == crashed | |
? width * ++n / total | 0 | |
: crashed; | |
// show the crash | |
if ('failed' == test.state) { | |
plane = color('plane crash', '✈'); | |
crashed = col; | |
} | |
// render landing strip | |
stream.write('\u001b[4F\n\n'); | |
stream.write(runway()); | |
stream.write('\n '); | |
stream.write(color('runway', Array(col).join('⋅'))); | |
stream.write(plane) | |
stream.write(color('runway', Array(width - col).join('⋅') + '\n')); | |
stream.write(runway()); | |
stream.write('\u001b[0m'); | |
}); | |
runner.on('end', function(){ | |
cursor.show(); | |
console.log(); | |
self.epilogue(); | |
}); | |
} | |
/** | |
* Inherit from `Base.prototype`. | |
*/ | |
function F(){}; | |
F.prototype = Base.prototype; | |
Landing.prototype = new F; | |
Landing.prototype.constructor = Landing; | |
}); // module: reporters/landing.js | |
require.register("reporters/list.js", function(module, exports, require){ | |
/** | |
* Module dependencies. | |
*/ | |
var Base = require('./base') | |
, cursor = Base.cursor | |
, color = Base.color; | |
/** | |
* Expose `List`. | |
*/ | |
exports = module.exports = List; | |
/** | |
* Initialize a new `List` test reporter. | |
* | |
* @param {Runner} runner | |
* @api public | |
*/ | |
function List(runner) { | |
Base.call(this, runner); | |
var self = this | |
, stats = this.stats | |
, n = 0; | |
runner.on('start', function(){ | |
console.log(); | |
}); | |
runner.on('test', function(test){ | |
process.stdout.write(color('pass', ' ' + test.fullTitle() + ': ')); | |
}); | |
runner.on('pending', function(test){ | |
var fmt = color('checkmark', ' -') | |
+ color('pending', ' %s'); | |
console.log(fmt, test.fullTitle()); | |
}); | |
runner.on('pass', function(test){ | |
var fmt = color('checkmark', ' '+Base.symbols.dot) | |
+ color('pass', ' %s: ') | |
+ color(test.speed, '%dms'); | |
cursor.CR(); | |
console.log(fmt, test.fullTitle(), test.duration); | |
}); | |
runner.on('fail', function(test, err){ | |
cursor.CR(); | |
console.log(color('fail', ' %d) %s'), ++n, test.fullTitle()); | |
}); | |
runner.on('end', self.epilogue.bind(self)); | |
} | |
/** | |
* Inherit from `Base.prototype`. | |
*/ | |
function F(){}; | |
F.prototype = Base.prototype; | |
List.prototype = new F; | |
List.prototype.constructor = List; | |
}); // module: reporters/list.js | |
require.register("reporters/markdown.js", function(module, exports, require){ | |
/** | |
* Module dependencies. | |
*/ | |
var Base = require('./base') | |
, utils = require('../utils'); | |
/** | |
* Expose `Markdown`. | |
*/ | |
exports = module.exports = Markdown; | |
/** | |
* Initialize a new `Markdown` reporter. | |
* | |
* @param {Runner} runner | |
* @api public | |
*/ | |
function Markdown(runner) { | |
Base.call(this, runner); | |
var self = this | |
, stats = this.stats | |
, level = 0 | |
, buf = ''; | |
function title(str) { | |
return Array(level).join('#') + ' ' + str; | |
} | |
function indent() { | |
return Array(level).join(' '); | |
} | |
function mapTOC(suite, obj) { | |
var ret = obj; | |
obj = obj[suite.title] = obj[suite.title] || { suite: suite }; | |
suite.suites.forEach(function(suite){ | |
mapTOC(suite, obj); | |
}); | |
return ret; | |
} | |
function stringifyTOC(obj, level) { | |
++level; | |
var buf = ''; | |
var link; | |
for (var key in obj) { | |
if ('suite' == key) continue; | |
if (key) link = ' - [' + key + '](#' + utils.slug(obj[key].suite.fullTitle()) + ')\n'; | |
if (key) buf += Array(level).join(' ') + link; | |
buf += stringifyTOC(obj[key], level); | |
} | |
--level; | |
return buf; | |
} | |
function generateTOC(suite) { | |
var obj = mapTOC(suite, {}); | |
return stringifyTOC(obj, 0); | |
} | |
generateTOC(runner.suite); | |
runner.on('suite', function(suite){ | |
++level; | |
var slug = utils.slug(suite.fullTitle()); | |
buf += '<a name="' + slug + '"></a>' + '\n'; | |
buf += title(suite.title) + '\n'; | |
}); | |
runner.on('suite end', function(suite){ | |
--level; | |
}); | |
runner.on('pass', function(test){ | |
var code = utils.clean(test.fn.toString()); | |
buf += test.title + '.\n'; | |
buf += '\n```js\n'; | |
buf += code + '\n'; | |
buf += '```\n\n'; | |
}); | |
runner.on('end', function(){ | |
process.stdout.write('# TOC\n'); | |
process.stdout.write(generateTOC(runner.suite)); | |
process.stdout.write(buf); | |
}); | |
} | |
}); // module: reporters/markdown.js | |
require.register("reporters/min.js", function(module, exports, require){ | |
/** | |
* Module dependencies. | |
*/ | |
var Base = require('./base'); | |
/** | |
* Expose `Min`. | |
*/ | |
exports = module.exports = Min; | |
/** | |
* Initialize a new `Min` minimal test reporter (best used with --watch). | |
* | |
* @param {Runner} runner | |
* @api public | |
*/ | |
function Min(runner) { | |
Base.call(this, runner); | |
runner.on('start', function(){ | |
// clear screen | |
process.stdout.write('\u001b[2J'); | |
// set cursor position | |
process.stdout.write('\u001b[1;3H'); | |
}); | |
runner.on('end', this.epilogue.bind(this)); | |
} | |
/** | |
* Inherit from `Base.prototype`. | |
*/ | |
function F(){}; | |
F.prototype = Base.prototype; | |
Min.prototype = new F; | |
Min.prototype.constructor = Min; | |
}); // module: reporters/min.js | |
require.register("reporters/nyan.js", function(module, exports, require){ | |
/** | |
* Module dependencies. | |
*/ | |
var Base = require('./base') | |
, color = Base.color; | |
/** | |
* Expose `Dot`. | |
*/ | |
exports = module.exports = NyanCat; | |
/** | |
* Initialize a new `Dot` matrix test reporter. | |
* | |
* @param {Runner} runner | |
* @api public | |
*/ | |
function NyanCat(runner) { | |
Base.call(this, runner); | |
var self = this | |
, stats = this.stats | |
, width = Base.window.width * .75 | 0 | |
, rainbowColors = this.rainbowColors = self.generateColors() | |
, colorIndex = this.colorIndex = 0 | |
, numerOfLines = this.numberOfLines = 4 | |
, trajectories = this.trajectories = [[], [], [], []] | |
, nyanCatWidth = this.nyanCatWidth = 11 | |
, trajectoryWidthMax = this.trajectoryWidthMax = (width - nyanCatWidth) | |
, scoreboardWidth = this.scoreboardWidth = 5 | |
, tick = this.tick = 0 | |
, n = 0; | |
runner.on('start', function(){ | |
Base.cursor.hide(); | |
self.draw(); | |
}); | |
runner.on('pending', function(test){ | |
self.draw(); | |
}); | |
runner.on('pass', function(test){ | |
self.draw(); | |
}); | |
runner.on('fail', function(test, err){ | |
self.draw(); | |
}); | |
runner.on('end', function(){ | |
Base.cursor.show(); | |
for (var i = 0; i < self.numberOfLines; i++) write('\n'); | |
self.epilogue(); | |
}); | |
} | |
/** | |
* Draw the nyan cat | |
* | |
* @api private | |
*/ | |
NyanCat.prototype.draw = function(){ | |
this.appendRainbow(); | |
this.drawScoreboard(); | |
this.drawRainbow(); | |
this.drawNyanCat(); | |
this.tick = !this.tick; | |
}; | |
/** | |
* Draw the "scoreboard" showing the number | |
* of passes, failures and pending tests. | |
* | |
* @api private | |
*/ | |
NyanCat.prototype.drawScoreboard = function(){ | |
var stats = this.stats; | |
var colors = Base.colors; | |
function draw(color, n) { | |
write(' '); | |
write('\u001b[' + color + 'm' + n + '\u001b[0m'); | |
write('\n'); | |
} | |
draw(colors.green, stats.passes); | |
draw(colors.fail, stats.failures); | |
draw(colors.pending, stats.pending); | |
write('\n'); | |
this.cursorUp(this.numberOfLines); | |
}; | |
/** | |
* Append the rainbow. | |
* | |
* @api private | |
*/ | |
NyanCat.prototype.appendRainbow = function(){ | |
var segment = this.tick ? '_' : '-'; | |
var rainbowified = this.rainbowify(segment); | |
for (var index = 0; index < this.numberOfLines; index++) { | |
var trajectory = this.trajectories[index]; | |
if (trajectory.length >= this.trajectoryWidthMax) trajectory.shift(); | |
trajectory.push(rainbowified); | |
} | |
}; | |
/** | |
* Draw the rainbow. | |
* | |
* @api private | |
*/ | |
NyanCat.prototype.drawRainbow = function(){ | |
var self = this; | |
this.trajectories.forEach(function(line, index) { | |
write('\u001b[' + self.scoreboardWidth + 'C'); | |
write(line.join('')); | |
write('\n'); | |
}); | |
this.cursorUp(this.numberOfLines); | |
}; | |
/** | |
* Draw the nyan cat | |
* | |
* @api private | |
*/ | |
NyanCat.prototype.drawNyanCat = function() { | |
var self = this; | |
var startWidth = this.scoreboardWidth + this.trajectories[0].length; | |
var color = '\u001b[' + startWidth + 'C'; | |
var padding = ''; | |
write(color); | |
write('_,------,'); | |
write('\n'); | |
write(color); | |
padding = self.tick ? ' ' : ' '; | |
write('_|' + padding + '/\\_/\\ '); | |
write('\n'); | |
write(color); | |
padding = self.tick ? '_' : '__'; | |
var tail = self.tick ? '~' : '^'; | |
var face; | |
write(tail + '|' + padding + this.face() + ' '); | |
write('\n'); | |
write(color); | |
padding = self.tick ? ' ' : ' '; | |
write(padding + '"" "" '); | |
write('\n'); | |
this.cursorUp(this.numberOfLines); | |
}; | |
/** | |
* Draw nyan cat face. | |
* | |
* @return {String} | |
* @api private | |
*/ | |
NyanCat.prototype.face = function() { | |
var stats = this.stats; | |
if (stats.failures) { | |
return '( x .x)'; | |
} else if (stats.pending) { | |
return '( o .o)'; | |
} else if(stats.passes) { | |
return '( ^ .^)'; | |
} else { | |
return '( - .-)'; | |
} | |
} | |
/** | |
* Move cursor up `n`. | |
* | |
* @param {Number} n | |
* @api private | |
*/ | |
NyanCat.prototype.cursorUp = function(n) { | |
write('\u001b[' + n + 'A'); | |
}; | |
/** | |
* Move cursor down `n`. | |
* | |
* @param {Number} n | |
* @api private | |
*/ | |
NyanCat.prototype.cursorDown = function(n) { | |
write('\u001b[' + n + 'B'); | |
}; | |
/** | |
* Generate rainbow colors. | |
* | |
* @return {Array} | |
* @api private | |
*/ | |
NyanCat.prototype.generateColors = function(){ | |
var colors = []; | |
for (var i = 0; i < (6 * 7); i++) { | |
var pi3 = Math.floor(Math.PI / 3); | |
var n = (i * (1.0 / 6)); | |
var r = Math.floor(3 * Math.sin(n) + 3); | |
var g = Math.floor(3 * Math.sin(n + 2 * pi3) + 3); | |
var b = Math.floor(3 * Math.sin(n + 4 * pi3) + 3); | |
colors.push(36 * r + 6 * g + b + 16); | |
} | |
return colors; | |
}; | |
/** | |
* Apply rainbow to the given `str`. | |
* | |
* @param {String} str | |
* @return {String} | |
* @api private | |
*/ | |
NyanCat.prototype.rainbowify = function(str){ | |
var color = this.rainbowColors[this.colorIndex % this.rainbowColors.length]; | |
this.colorIndex += 1; | |
return '\u001b[38;5;' + color + 'm' + str + '\u001b[0m'; | |
}; | |
/** | |
* Stdout helper. | |
*/ | |
function write(string) { | |
process.stdout.write(string); | |
} | |
/** | |
* Inherit from `Base.prototype`. | |
*/ | |
function F(){}; | |
F.prototype = Base.prototype; | |
NyanCat.prototype = new F; | |
NyanCat.prototype.constructor = NyanCat; | |
}); // module: reporters/nyan.js | |
require.register("reporters/progress.js", function(module, exports, require){ | |
/** | |
* Module dependencies. | |
*/ | |
var Base = require('./base') | |
, cursor = Base.cursor | |
, color = Base.color; | |
/** | |
* Expose `Progress`. | |
*/ | |
exports = module.exports = Progress; | |
/** | |
* General progress bar color. | |
*/ | |
Base.colors.progress = 90; | |
/** | |
* Initialize a new `Progress` bar test reporter. | |
* | |
* @param {Runner} runner | |
* @param {Object} options | |
* @api public | |
*/ | |
function Progress(runner, options) { | |
Base.call(this, runner); | |
var self = this | |
, options = options || {} | |
, stats = this.stats | |
, width = Base.window.width * .50 | 0 | |
, total = runner.total | |
, complete = 0 | |
, max = Math.max; | |
// default chars | |
options.open = options.open || '['; | |
options.complete = options.complete || '▬'; | |
options.incomplete = options.incomplete || Base.symbols.dot; | |
options.close = options.close || ']'; | |
options.verbose = false; | |
// tests started | |
runner.on('start', function(){ | |
console.log(); | |
cursor.hide(); | |
}); | |
// tests complete | |
runner.on('test end', function(){ | |
complete++; | |
var incomplete = total - complete | |
, percent = complete / total | |
, n = width * percent | 0 | |
, i = width - n; | |
cursor.CR(); | |
process.stdout.write('\u001b[J'); | |
process.stdout.write(color('progress', ' ' + options.open)); | |
process.stdout.write(Array(n).join(options.complete)); | |
process.stdout.write(Array(i).join(options.incomplete)); | |
process.stdout.write(color('progress', options.close)); | |
if (options.verbose) { | |
process.stdout.write(color('progress', ' ' + complete + ' of ' + total)); | |
} | |
}); | |
// tests are complete, output some stats | |
// and the failures if any | |
runner.on('end', function(){ | |
cursor.show(); | |
console.log(); | |
self.epilogue(); | |
}); | |
} | |
/** | |
* Inherit from `Base.prototype`. | |
*/ | |
function F(){}; | |
F.prototype = Base.prototype; | |
Progress.prototype = new F; | |
Progress.prototype.constructor = Progress; | |
}); // module: reporters/progress.js | |
require.register("reporters/spec.js", function(module, exports, require){ | |
/** | |
* Module dependencies. | |
*/ | |
var Base = require('./base') | |
, cursor = Base.cursor | |
, color = Base.color; | |
/** | |
* Expose `Spec`. | |
*/ | |
exports = module.exports = Spec; | |
/** | |
* Initialize a new `Spec` test reporter. | |
* | |
* @param {Runner} runner | |
* @api public | |
*/ | |
function Spec(runner) { | |
Base.call(this, runner); | |
var self = this | |
, stats = this.stats | |
, indents = 0 | |
, n = 0; | |
function indent() { | |
return Array(indents).join(' ') | |
} | |
runner.on('start', function(){ | |
console.log(); | |
}); | |
runner.on('suite', function(suite){ | |
++indents; | |
console.log(color('suite', '%s%s'), indent(), suite.title); | |
}); | |
runner.on('suite end', function(suite){ | |
--indents; | |
if (1 == indents) console.log(); | |
}); | |
runner.on('test', function(test){ | |
process.stdout.write(indent() + color('pass', ' ◦ ' + test.title + ': ')); | |
}); | |
runner.on('pending', function(test){ | |
var fmt = indent() + color('pending', ' - %s'); | |
console.log(fmt, test.title); | |
}); | |
runner.on('pass', function(test){ | |
if ('fast' == test.speed) { | |
var fmt = indent() | |
+ color('checkmark', ' ' + Base.symbols.ok) | |
+ color('pass', ' %s '); | |
cursor.CR(); | |
console.log(fmt, test.title); | |
} else { | |
var fmt = indent() | |
+ color('checkmark', ' ' + Base.symbols.ok) | |
+ color('pass', ' %s ') | |
+ color(test.speed, '(%dms)'); | |
cursor.CR(); | |
console.log(fmt, test.title, test.duration); | |
} | |
}); | |
runner.on('fail', function(test, err){ | |
cursor.CR(); | |
console.log(indent() + color('fail', ' %d) %s'), ++n, test.title); | |
}); | |
runner.on('end', self.epilogue.bind(self)); | |
} | |
/** | |
* Inherit from `Base.prototype`. | |
*/ | |
function F(){}; | |
F.prototype = Base.prototype; | |
Spec.prototype = new F; | |
Spec.prototype.constructor = Spec; | |
}); // module: reporters/spec.js | |
require.register("reporters/tap.js", function(module, exports, require){ | |
/** | |
* Module dependencies. | |
*/ | |
var Base = require('./base') | |
, cursor = Base.cursor | |
, color = Base.color; | |
/** | |
* Expose `TAP`. | |
*/ | |
exports = module.exports = TAP; | |
/** | |
* Initialize a new `TAP` reporter. | |
* | |
* @param {Runner} runner | |
* @api public | |
*/ | |
function TAP(runner) { | |
Base.call(this, runner); | |
var self = this | |
, stats = this.stats | |
, n = 1 | |
, passes = 0 | |
, failures = 0; | |
runner.on('start', function(){ | |
var total = runner.grepTotal(runner.suite); | |
console.log('%d..%d', 1, total); | |
}); | |
runner.on('test end', function(){ | |
++n; | |
}); | |
runner.on('pending', function(test){ | |
console.log('ok %d %s # SKIP -', n, title(test)); | |
}); | |
runner.on('pass', function(test){ | |
passes++; | |
console.log('ok %d %s', n, title(test)); | |
}); | |
runner.on('fail', function(test, err){ | |
failures++; | |
console.log('not ok %d %s', n, title(test)); | |
if (err.stack) console.log(err.stack.replace(/^/gm, ' ')); | |
}); | |
runner.on('end', function(){ | |
console.log('# tests ' + (passes + failures)); | |
console.log('# pass ' + passes); | |
console.log('# fail ' + failures); | |
}); | |
} | |
/** | |
* Return a TAP-safe title of `test` | |
* | |
* @param {Object} test | |
* @return {String} | |
* @api private | |
*/ | |
function title(test) { | |
return test.fullTitle().replace(/#/g, ''); | |
} | |
}); // module: reporters/tap.js | |
require.register("reporters/xunit.js", function(module, exports, require){ | |
/** | |
* Module dependencies. | |
*/ | |
var Base = require('./base') | |
, utils = require('../utils') | |
, escape = utils.escape; | |
/** | |
* Save timer references to avoid Sinon interfering (see GH-237). | |
*/ | |
var Date = global.Date | |
, setTimeout = global.setTimeout | |
, setInterval = global.setInterval | |
, clearTimeout = global.clearTimeout | |
, clearInterval = global.clearInterval; | |
/** | |
* Expose `XUnit`. | |
*/ | |
exports = module.exports = XUnit; | |
/** | |
* Initialize a new `XUnit` reporter. | |
* | |
* @param {Runner} runner | |
* @api public | |
*/ | |
function XUnit(runner) { | |
Base.call(this, runner); | |
var stats = this.stats | |
, tests = [] | |
, self = this; | |
runner.on('pass', function(test){ | |
tests.push(test); | |
}); | |
runner.on('fail', function(test){ | |
tests.push(test); | |
}); | |
runner.on('end', function(){ | |
console.log(tag('testsuite', { | |
name: 'Mocha Tests' | |
, tests: stats.tests | |
, failures: stats.failures | |
, errors: stats.failures | |
, skipped: stats.tests - stats.failures - stats.passes | |
, timestamp: (new Date).toUTCString() | |
, time: (stats.duration / 1000) || 0 | |
}, false)); | |
tests.forEach(test); | |
console.log('</testsuite>'); | |
}); | |
} | |
/** | |
* Inherit from `Base.prototype`. | |
*/ | |
function F(){}; | |
F.prototype = Base.prototype; | |
XUnit.prototype = new F; | |
XUnit.prototype.constructor = XUnit; | |
/** | |
* Output tag for the given `test.` | |
*/ | |
function test(test) { | |
var attrs = { | |
classname: test.parent.fullTitle() | |
, name: test.title | |
, time: test.duration / 1000 | |
}; | |
if ('failed' == test.state) { | |
var err = test.err; | |
attrs.message = escape(err.message); | |
console.log(tag('testcase', attrs, false, tag('failure', attrs, false, cdata(err.stack)))); | |
} else if (test.pending) { | |
console.log(tag('testcase', attrs, false, tag('skipped', {}, true))); | |
} else { | |
console.log(tag('testcase', attrs, true) ); | |
} | |
} | |
/** | |
* HTML tag helper. | |
*/ | |
function tag(name, attrs, close, content) { | |
var end = close ? '/>' : '>' | |
, pairs = [] | |
, tag; | |
for (var key in attrs) { | |
pairs.push(key + '="' + escape(attrs[key]) + '"'); | |
} | |
tag = '<' + name + (pairs.length ? ' ' + pairs.join(' ') : '') + end; | |
if (content) tag += content + '</' + name + end; | |
return tag; | |
} | |
/** | |
* Return cdata escaped CDATA `str`. | |
*/ | |
function cdata(str) { | |
return '<![CDATA[' + escape(str) + ']]>'; | |
} | |
}); // module: reporters/xunit.js | |
require.register("runnable.js", function(module, exports, require){ | |
/** | |
* Module dependencies. | |
*/ | |
var EventEmitter = require('browser/events').EventEmitter | |
, debug = require('browser/debug')('mocha:runnable') | |
, milliseconds = require('./ms'); | |
/** | |
* Save timer references to avoid Sinon interfering (see GH-237). | |
*/ | |
var Date = global.Date | |
, setTimeout = global.setTimeout | |
, setInterval = global.setInterval | |
, clearTimeout = global.clearTimeout | |
, clearInterval = global.clearInterval; | |
/** | |
* Object#toString(). | |
*/ | |
var toString = Object.prototype.toString; | |
/** | |
* Expose `Runnable`. | |
*/ | |
module.exports = Runnable; | |
/** | |
* Initialize a new `Runnable` with the given `title` and callback `fn`. | |
* | |
* @param {String} title | |
* @param {Function} fn | |
* @api private | |
*/ | |
function Runnable(title, fn) { | |
this.title = title; | |
this.fn = fn; | |
this.async = fn && fn.length; | |
this.sync = ! this.async; | |
this._timeout = 2000; | |
this._slow = 75; | |
this.timedOut = false; | |
} | |
/** | |
* Inherit from `EventEmitter.prototype`. | |
*/ | |
function F(){}; | |
F.prototype = EventEmitter.prototype; | |
Runnable.prototype = new F; | |
Runnable.prototype.constructor = Runnable; | |
/** | |
* Set & get timeout `ms`. | |
* | |
* @param {Number|String} ms | |
* @return {Runnable|Number} ms or self | |
* @api private | |
*/ | |
Runnable.prototype.timeout = function(ms){ | |
if (0 == arguments.length) return this._timeout; | |
if ('string' == typeof ms) ms = milliseconds(ms); | |
debug('timeout %d', ms); | |
this._timeout = ms; | |
if (this.timer) this.resetTimeout(); | |
return this; | |
}; | |
/** | |
* Set & get slow `ms`. | |
* | |
* @param {Number|String} ms | |
* @return {Runnable|Number} ms or self | |
* @api private | |
*/ | |
Runnable.prototype.slow = function(ms){ | |
if (0 === arguments.length) return this._slow; | |
if ('string' == typeof ms) ms = milliseconds(ms); | |
debug('timeout %d', ms); | |
this._slow = ms; | |
return this; | |
}; | |
/** | |
* Return the full title generated by recursively | |
* concatenating the parent's full title. | |
* | |
* @return {String} | |
* @api public | |
*/ | |
Runnable.prototype.fullTitle = function(){ | |
return this.parent.fullTitle() + ' ' + this.title; | |
}; | |
/** | |
* Clear the timeout. | |
* | |
* @api private | |
*/ | |
Runnable.prototype.clearTimeout = function(){ | |
clearTimeout(this.timer); | |
}; | |
/** | |
* Inspect the runnable void of private properties. | |
* | |
* @return {String} | |
* @api private | |
*/ | |
Runnable.prototype.inspect = function(){ | |
return JSON.stringify(this, function(key, val){ | |
if ('_' == key[0]) return; | |
if ('parent' == key) return '#<Suite>'; | |
if ('ctx' == key) return '#<Context>'; | |
return val; | |
}, 2); | |
}; | |
/** | |
* Reset the timeout. | |
* | |
* @api private | |
*/ | |
Runnable.prototype.resetTimeout = function(){ | |
var self = this; | |
var ms = this.timeout() || 1e9; | |
this.clearTimeout(); | |
this.timer = setTimeout(function(){ | |
self.callback(new Error('timeout of ' + ms + 'ms exceeded')); | |
self.timedOut = true; | |
}, ms); | |
}; | |
/** | |
* Run the test and invoke `fn(err)`. | |
* | |
* @param {Function} fn | |
* @api private | |
*/ | |
Runnable.prototype.run = function(fn){ | |
var self = this | |
, ms = this.timeout() | |
, start = new Date | |
, ctx = this.ctx | |
, finished | |
, emitted; | |
if (ctx) ctx.runnable(this); | |
// timeout | |
if (this.async) { | |
if (ms) { | |
this.timer = setTimeout(function(){ | |
done(new Error('timeout of ' + ms + 'ms exceeded')); | |
self.timedOut = true; | |
}, ms); | |
} | |
} | |
// called multiple times | |
function multiple(err) { | |
if (emitted) return; | |
emitted = true; | |
self.emit('error', err || new Error('done() called multiple times')); | |
} | |
// finished | |
function done(err) { | |
if (self.timedOut) return; | |
if (finished) return multiple(err); | |
self.clearTimeout(); | |
self.duration = new Date - start; | |
finished = true; | |
fn(err); | |
} | |
// for .resetTimeout() | |
this.callback = done; | |
// async | |
if (this.async) { | |
try { | |
this.fn.call(ctx, function(err){ | |
if (err instanceof Error || toString.call(err) === "[object Error]") return done(err); | |
if (null != err) return done(new Error('done() invoked with non-Error: ' + err)); | |
done(); | |
}); | |
} catch (err) { | |
done(err); | |
} | |
return; | |
} | |
if (this.asyncOnly) { | |
return done(new Error('--async-only option in use without declaring `done()`')); | |
} | |
// sync | |
try { | |
if (!this.pending) this.fn.call(ctx); | |
this.duration = new Date - start; | |
fn(); | |
} catch (err) { | |
fn(err); | |
} | |
}; | |
}); // module: runnable.js | |
require.register("runner.js", function(module, exports, require){ | |
/** | |
* Module dependencies. | |
*/ | |
var EventEmitter = require('browser/events').EventEmitter | |
, debug = require('browser/debug')('mocha:runner') | |
, Test = require('./test') | |
, utils = require('./utils') | |
, filter = utils.filter | |
, keys = utils.keys; | |
/** | |
* Non-enumerable globals. | |
*/ | |
var globals = [ | |
'setTimeout', | |
'clearTimeout', | |
'setInterval', | |
'clearInterval', | |
'XMLHttpRequest', | |
'Date' | |
]; | |
/** | |
* Expose `Runner`. | |
*/ | |
module.exports = Runner; | |
/** | |
* Initialize a `Runner` for the given `suite`. | |
* | |
* Events: | |
* | |
* - `start` execution started | |
* - `end` execution complete | |
* - `suite` (suite) test suite execution started | |
* - `suite end` (suite) all tests (and sub-suites) have finished | |
* - `test` (test) test execution started | |
* - `test end` (test) test completed | |
* - `hook` (hook) hook execution started | |
* - `hook end` (hook) hook complete | |
* - `pass` (test) test passed | |
* - `fail` (test, err) test failed | |
* - `pending` (test) test pending | |
* | |
* @api public | |
*/ | |
function Runner(suite) { | |
var self = this; | |
this._globals = []; | |
this.suite = suite; | |
this.total = suite.total(); | |
this.failures = 0; | |
this.on('test end', function(test){ self.checkGlobals(test); }); | |
this.on('hook end', function(hook){ self.checkGlobals(hook); }); | |
this.grep(/.*/); | |
this.globals(this.globalProps().concat(['errno'])); | |
} | |
/** | |
* Wrapper for setImmediate, process.nextTick, or browser polyfill. | |
* | |
* @param {Function} fn | |
* @api private | |
*/ | |
Runner.immediately = global.setImmediate || process.nextTick; | |
/** | |
* Inherit from `EventEmitter.prototype`. | |
*/ | |
function F(){}; | |
F.prototype = EventEmitter.prototype; | |
Runner.prototype = new F; | |
Runner.prototype.constructor = Runner; | |
/** | |
* Run tests with full titles matching `re`. Updates runner.total | |
* with number of tests matched. | |
* | |
* @param {RegExp} re | |
* @param {Boolean} invert | |
* @return {Runner} for chaining | |
* @api public | |
*/ | |
Runner.prototype.grep = function(re, invert){ | |
debug('grep %s', re); | |
this._grep = re; | |
this._invert = invert; | |
this.total = this.grepTotal(this.suite); | |
return this; | |
}; | |
/** | |
* Returns the number of tests matching the grep search for the | |
* given suite. | |
* | |
* @param {Suite} suite | |
* @return {Number} | |
* @api public | |
*/ | |
Runner.prototype.grepTotal = function(suite) { | |
var self = this; | |
var total = 0; | |
suite.eachTest(function(test){ | |
var match = self._grep.test(test.fullTitle()); | |
if (self._invert) match = !match; | |
if (match) total++; | |
}); | |
return total; | |
}; | |
/** | |
* Return a list of global properties. | |
* | |
* @return {Array} | |
* @api private | |
*/ | |
Runner.prototype.globalProps = function() { | |
var props = utils.keys(global); | |
// non-enumerables | |
for (var i = 0; i < globals.length; ++i) { | |
if (~utils.indexOf(props, globals[i])) continue; | |
props.push(globals[i]); | |
} | |
return props; | |
}; | |
/** | |
* Allow the given `arr` of globals. | |
* | |
* @param {Array} arr | |
* @return {Runner} for chaining | |
* @api public | |
*/ | |
Runner.prototype.globals = function(arr){ | |
if (0 == arguments.length) return this._globals; | |
debug('globals %j', arr); | |
utils.forEach(arr, function(arr){ | |
this._globals.push(arr); | |
}, this); | |
return this; | |
}; | |
/** | |
* Check for global variable leaks. | |
* | |
* @api private | |
*/ | |
Runner.prototype.checkGlobals = function(test){ | |
if (this.ignoreLeaks) return; | |
var ok = this._globals; | |
var globals = this.globalProps(); | |
var isNode = process.kill; | |
var leaks; | |
// check length - 2 ('errno' and 'location' globals) | |
if (isNode && 1 == ok.length - globals.length) return; | |
else if (2 == ok.length - globals.length) return; | |
if(this.prevGlobalsLength == globals.length) return; | |
this.prevGlobalsLength = globals.length; | |
leaks = filterLeaks(ok, globals); | |
this._globals = this._globals.concat(leaks); | |
if (leaks.length > 1) { | |
this.fail(test, new Error('global leaks detected: ' + leaks.join(', ') + '')); | |
} else if (leaks.length) { | |
this.fail(test, new Error('global leak detected: ' + leaks[0])); | |
} | |
}; | |
/** | |
* Fail the given `test`. | |
* | |
* @param {Test} test | |
* @param {Error} err | |
* @api private | |
*/ | |
Runner.prototype.fail = function(test, err){ | |
++this.failures; | |
test.state = 'failed'; | |
if ('string' == typeof err) { | |
err = new Error('the string "' + err + '" was thrown, throw an Error :)'); | |
} | |
this.emit('fail', test, err); | |
}; | |
/** | |
* Fail the given `hook` with `err`. | |
* | |
* Hook failures (currently) hard-end due | |
* to that fact that a failing hook will | |
* surely cause subsequent tests to fail, | |
* causing jumbled reporting. | |
* | |
* @param {Hook} hook | |
* @param {Error} err | |
* @api private | |
*/ | |
Runner.prototype.failHook = function(hook, err){ | |
this.fail(hook, err); | |
this.emit('end'); | |
}; | |
/** | |
* Run hook `name` callbacks and then invoke `fn()`. | |
* | |
* @param {String} name | |
* @param {Function} function | |
* @api private | |
*/ | |
Runner.prototype.hook = function(name, fn){ | |
var suite = this.suite | |
, hooks = suite['_' + name] | |
, self = this | |
, timer; | |
function next(i) { | |
var hook = hooks[i]; | |
if (!hook) return fn(); | |
if (self.failures && suite.bail()) return fn(); | |
self.currentRunnable = hook; | |
hook.ctx.currentTest = self.test; | |
self.emit('hook', hook); | |
hook.on('error', function(err){ | |
self.failHook(hook, err); | |
}); | |
hook.run(function(err){ | |
hook.removeAllListeners('error'); | |
var testError = hook.error(); | |
if (testError) self.fail(self.test, testError); | |
if (err) return self.failHook(hook, err); | |
self.emit('hook end', hook); | |
delete hook.ctx.currentTest; | |
next(++i); | |
}); | |
} | |
Runner.immediately(function(){ | |
next(0); | |
}); | |
}; | |
/** | |
* Run hook `name` for the given array of `suites` | |
* in order, and callback `fn(err)`. | |
* | |
* @param {String} name | |
* @param {Array} suites | |
* @param {Function} fn | |
* @api private | |
*/ | |
Runner.prototype.hooks = function(name, suites, fn){ | |
var self = this | |
, orig = this.suite; | |
function next(suite) { | |
self.suite = suite; | |
if (!suite) { | |
self.suite = orig; | |
return fn(); | |
} | |
self.hook(name, function(err){ | |
if (err) { | |
self.suite = orig; | |
return fn(err); | |
} | |
next(suites.pop()); | |
}); | |
} | |
next(suites.pop()); | |
}; | |
/** | |
* Run hooks from the top level down. | |
* | |
* @param {String} name | |
* @param {Function} fn | |
* @api private | |
*/ | |
Runner.prototype.hookUp = function(name, fn){ | |
var suites = [this.suite].concat(this.parents()).reverse(); | |
this.hooks(name, suites, fn); | |
}; | |
/** | |
* Run hooks from the bottom up. | |
* | |
* @param {String} name | |
* @param {Function} fn | |
* @api private | |
*/ | |
Runner.prototype.hookDown = function(name, fn){ | |
var suites = [this.suite].concat(this.parents()); | |
this.hooks(name, suites, fn); | |
}; | |
/** | |
* Return an array of parent Suites from | |
* closest to furthest. | |
* | |
* @return {Array} | |
* @api private | |
*/ | |
Runner.prototype.parents = function(){ | |
var suite = this.suite | |
, suites = []; | |
while (suite = suite.parent) suites.push(suite); | |
return suites; | |
}; | |
/** | |
* Run the current test and callback `fn(err)`. | |
* | |
* @param {Function} fn | |
* @api private | |
*/ | |
Runner.prototype.runTest = function(fn){ | |
var test = this.test | |
, self = this; | |
if (this.asyncOnly) test.asyncOnly = true; | |
try { | |
test.on('error', function(err){ | |
self.fail(test, err); | |
}); | |
test.run(fn); | |
} catch (err) { | |
fn(err); | |
} | |
}; | |
/** | |
* Run tests in the given `suite` and invoke | |
* the callback `fn()` when complete. | |
* | |
* @param {Suite} suite | |
* @param {Function} fn | |
* @api private | |
*/ | |
Runner.prototype.runTests = function(suite, fn){ | |
var self = this | |
, tests = suite.tests.slice() | |
, test; | |
function next(err) { | |
// if we bail after first err | |
if (self.failures && suite._bail) return fn(); | |
// next test | |
test = tests.shift(); | |
// all done | |
if (!test) return fn(); | |
// grep | |
var match = self._grep.test(test.fullTitle()); | |
if (self._invert) match = !match; | |
if (!match) return next(); | |
// pending | |
if (test.pending) { | |
self.emit('pending', test); | |
self.emit('test end', test); | |
return next(); | |
} | |
// execute test and hook(s) | |
self.emit('test', self.test = test); | |
self.hookDown('beforeEach', function(){ | |
self.currentRunnable = self.test; | |
self.runTest(function(err){ | |
test = self.test; | |
if (err) { | |
self.fail(test, err); | |
self.emit('test end', test); | |
return self.hookUp('afterEach', next); | |
} | |
test.state = 'passed'; | |
self.emit('pass', test); | |
self.emit('test end', test); | |
self.hookUp('afterEach', next); | |
}); | |
}); | |
} | |
this.next = next; | |
next(); | |
}; | |
/** | |
* Run the given `suite` and invoke the | |
* callback `fn()` when complete. | |
* | |
* @param {Suite} suite | |
* @param {Function} fn | |
* @api private | |
*/ | |
Runner.prototype.runSuite = function(suite, fn){ | |
var total = this.grepTotal(suite) | |
, self = this | |
, i = 0; | |
debug('run suite %s', suite.fullTitle()); | |
if (!total) return fn(); | |
this.emit('suite', this.suite = suite); | |
function next() { | |
var curr = suite.suites[i++]; | |
if (!curr) return done(); | |
self.runSuite(curr, next); | |
} | |
function done() { | |
self.suite = suite; | |
self.hook('afterAll', function(){ | |
self.emit('suite end', suite); | |
fn(); | |
}); | |
} | |
this.hook('beforeAll', function(){ | |
self.runTests(suite, next); | |
}); | |
}; | |
/** | |
* Handle uncaught exceptions. | |
* | |
* @param {Error} err | |
* @api private | |
*/ | |
Runner.prototype.uncaught = function(err){ | |
debug('uncaught exception %s', err.message); | |
var runnable = this.currentRunnable; | |
if (!runnable || 'failed' == runnable.state) return; | |
runnable.clearTimeout(); | |
err.uncaught = true; | |
this.fail(runnable, err); | |
// recover from test | |
if ('test' == runnable.type) { | |
this.emit('test end', runnable); | |
this.hookUp('afterEach', this.next); | |
return; | |
} | |
// bail on hooks | |
this.emit('end'); | |
}; | |
/** | |
* Run the root suite and invoke `fn(failures)` | |
* on completion. | |
* | |
* @param {Function} fn | |
* @return {Runner} for chaining | |
* @api public | |
*/ | |
Runner.prototype.run = function(fn){ | |
var self = this | |
, fn = fn || function(){}; | |
function uncaught(err){ | |
self.uncaught(err); | |
} | |
debug('start'); | |
// callback | |
this.on('end', function(){ | |
debug('end'); | |
process.removeListener('uncaughtException', uncaught); | |
fn(self.failures); | |
}); | |
// run suites | |
this.emit('start'); | |
this.runSuite(this.suite, function(){ | |
debug('finished running'); | |
self.emit('end'); | |
}); | |
// uncaught exception | |
process.on('uncaughtException', uncaught); | |
return this; | |
}; | |
/** | |
* Filter leaks with the given globals flagged as `ok`. | |
* | |
* @param {Array} ok | |
* @param {Array} globals | |
* @return {Array} | |
* @api private | |
*/ | |
function filterLeaks(ok, globals) { | |
return filter(globals, function(key){ | |
// Firefox and Chrome exposes iframes as index inside the window object | |
if (/^d+/.test(key)) return false; | |
// in firefox | |
// if runner runs in an iframe, this iframe's window.getInterface method not init at first | |
// it is assigned in some seconds | |
if (global.navigator && /^getInterface/.test(key)) return false; | |
// an iframe could be approached by window[iframeIndex] | |
// in ie6,7,8 and opera, iframeIndex is enumerable, this could cause leak | |
if (global.navigator && /^\d+/.test(key)) return false; | |
// Opera and IE expose global variables for HTML element IDs (issue #243) | |
if (/^mocha-/.test(key)) return false; | |
var matched = filter(ok, function(ok){ | |
if (~ok.indexOf('*')) return 0 == key.indexOf(ok.split('*')[0]); | |
return key == ok; | |
}); | |
return matched.length == 0 && (!global.navigator || 'onerror' !== key); | |
}); | |
} | |
}); // module: runner.js | |
require.register("suite.js", function(module, exports, require){ | |
/** | |
* Module dependencies. | |
*/ | |
var EventEmitter = require('browser/events').EventEmitter | |
, debug = require('browser/debug')('mocha:suite') | |
, milliseconds = require('./ms') | |
, utils = require('./utils') | |
, Hook = require('./hook'); | |
/** | |
* Expose `Suite`. | |
*/ | |
exports = module.exports = Suite; | |
/** | |
* Create a new `Suite` with the given `title` | |
* and parent `Suite`. When a suite with the | |
* same title is already present, that suite | |
* is returned to provide nicer reporter | |
* and more flexible meta-testing. | |
* | |
* @param {Suite} parent | |
* @param {String} title | |
* @return {Suite} | |
* @api public | |
*/ | |
exports.create = function(parent, title){ | |
var suite = new Suite(title, parent.ctx); | |
suite.parent = parent; | |
if (parent.pending) suite.pending = true; | |
title = suite.fullTitle(); | |
parent.addSuite(suite); | |
return suite; | |
}; | |
/** | |
* Initialize a new `Suite` with the given | |
* `title` and `ctx`. | |
* | |
* @param {String} title | |
* @param {Context} ctx | |
* @api private | |
*/ | |
function Suite(title, ctx) { | |
this.title = title; | |
this.ctx = ctx; | |
this.suites = []; | |
this.tests = []; | |
this.pending = false; | |
this._beforeEach = []; | |
this._beforeAll = []; | |
this._afterEach = []; | |
this._afterAll = []; | |
this.root = !title; | |
this._timeout = 2000; | |
this._slow = 75; | |
this._bail = false; | |
} | |
/** | |
* Inherit from `EventEmitter.prototype`. | |
*/ | |
function F(){}; | |
F.prototype = EventEmitter.prototype; | |
Suite.prototype = new F; | |
Suite.prototype.constructor = Suite; | |
/** | |
* Return a clone of this `Suite`. | |
* | |
* @return {Suite} | |
* @api private | |
*/ | |
Suite.prototype.clone = function(){ | |
var suite = new Suite(this.title); | |
debug('clone'); | |
suite.ctx = this.ctx; | |
suite.timeout(this.timeout()); | |
suite.slow(this.slow()); | |
suite.bail(this.bail()); | |
return suite; | |
}; | |
/** | |
* Set timeout `ms` or short-hand such as "2s". | |
* | |
* @param {Number|String} ms | |
* @return {Suite|Number} for chaining | |
* @api private | |
*/ | |
Suite.prototype.timeout = function(ms){ | |
if (0 == arguments.length) return this._timeout; | |
if ('string' == typeof ms) ms = milliseconds(ms); | |
debug('timeout %d', ms); | |
this._timeout = parseInt(ms, 10); | |
return this; | |
}; | |
/** | |
* Set slow `ms` or short-hand such as "2s". | |
* | |
* @param {Number|String} ms | |
* @return {Suite|Number} for chaining | |
* @api private | |
*/ | |
Suite.prototype.slow = function(ms){ | |
if (0 === arguments.length) return this._slow; | |
if ('string' == typeof ms) ms = milliseconds(ms); | |
debug('slow %d', ms); | |
this._slow = ms; | |
return this; | |
}; | |
/** | |
* Sets whether to bail after first error. | |
* | |
* @parma {Boolean} bail | |
* @return {Suite|Number} for chaining | |
* @api private | |
*/ | |
Suite.prototype.bail = function(bail){ | |
if (0 == arguments.length) return this._bail; | |
debug('bail %s', bail); | |
this._bail = bail; | |
return this; | |
}; | |
/** | |
* Run `fn(test[, done])` before running tests. | |
* | |
* @param {Function} fn | |
* @return {Suite} for chaining | |
* @api private | |
*/ | |
Suite.prototype.beforeAll = function(fn){ | |
if (this.pending) return this; | |
var hook = new Hook('"before all" hook', fn); | |
hook.parent = this; | |
hook.timeout(this.timeout()); | |
hook.slow(this.slow()); | |
hook.ctx = this.ctx; | |
this._beforeAll.push(hook); | |
this.emit('beforeAll', hook); | |
return this; | |
}; | |
/** | |
* Run `fn(test[, done])` after running tests. | |
* | |
* @param {Function} fn | |
* @return {Suite} for chaining | |
* @api private | |
*/ | |
Suite.prototype.afterAll = function(fn){ | |
if (this.pending) return this; | |
var hook = new Hook('"after all" hook', fn); | |
hook.parent = this; | |
hook.timeout(this.timeout()); | |
hook.slow(this.slow()); | |
hook.ctx = this.ctx; | |
this._afterAll.push(hook); | |
this.emit('afterAll', hook); | |
return this; | |
}; | |
/** | |
* Run `fn(test[, done])` before each test case. | |
* | |
* @param {Function} fn | |
* @return {Suite} for chaining | |
* @api private | |
*/ | |
Suite.prototype.beforeEach = function(fn){ | |
if (this.pending) return this; | |
var hook = new Hook('"before each" hook', fn); | |
hook.parent = this; | |
hook.timeout(this.timeout()); | |
hook.slow(this.slow()); | |
hook.ctx = this.ctx; | |
this._beforeEach.push(hook); | |
this.emit('beforeEach', hook); | |
return this; | |
}; | |
/** | |
* Run `fn(test[, done])` after each test case. | |
* | |
* @param {Function} fn | |
* @return {Suite} for chaining | |
* @api private | |
*/ | |
Suite.prototype.afterEach = function(fn){ | |
if (this.pending) return this; | |
var hook = new Hook('"after each" hook', fn); | |
hook.parent = this; | |
hook.timeout(this.timeout()); | |
hook.slow(this.slow()); | |
hook.ctx = this.ctx; | |
this._afterEach.push(hook); | |
this.emit('afterEach', hook); | |
return this; | |
}; | |
/** | |
* Add a test `suite`. | |
* | |
* @param {Suite} suite | |
* @return {Suite} for chaining | |
* @api private | |
*/ | |
Suite.prototype.addSuite = function(suite){ | |
suite.parent = this; | |
suite.timeout(this.timeout()); | |
suite.slow(this.slow()); | |
suite.bail(this.bail()); | |
this.suites.push(suite); | |
this.emit('suite', suite); | |
return this; | |
}; | |
/** | |
* Add a `test` to this suite. | |
* | |
* @param {Test} test | |
* @return {Suite} for chaining | |
* @api private | |
*/ | |
Suite.prototype.addTest = function(test){ | |
test.parent = this; | |
test.timeout(this.timeout()); | |
test.slow(this.slow()); | |
test.ctx = this.ctx; | |
this.tests.push(test); | |
this.emit('test', test); | |
return this; | |
}; | |
/** | |
* Return the full title generated by recursively | |
* concatenating the parent's full title. | |
* | |
* @return {String} | |
* @api public | |
*/ | |
Suite.prototype.fullTitle = function(){ | |
if (this.parent) { | |
var full = this.parent.fullTitle(); | |
if (full) return full + ' ' + this.title; | |
} | |
return this.title; | |
}; | |
/** | |
* Return the total number of tests. | |
* | |
* @return {Number} | |
* @api public | |
*/ | |
Suite.prototype.total = function(){ | |
return utils.reduce(this.suites, function(sum, suite){ | |
return sum + suite.total(); | |
}, 0) + this.tests.length; | |
}; | |
/** | |
* Iterates through each suite recursively to find | |
* all tests. Applies a function in the format | |
* `fn(test)`. | |
* | |
* @param {Function} fn | |
* @return {Suite} | |
* @api private | |
*/ | |
Suite.prototype.eachTest = function(fn){ | |
utils.forEach(this.tests, fn); | |
utils.forEach(this.suites, function(suite){ | |
suite.eachTest(fn); | |
}); | |
return this; | |
}; | |
}); // module: suite.js | |
require.register("test.js", function(module, exports, require){ | |
/** | |
* Module dependencies. | |
*/ | |
var Runnable = require('./runnable'); | |
/** | |
* Expose `Test`. | |
*/ | |
module.exports = Test; | |
/** | |
* Initialize a new `Test` with the given `title` and callback `fn`. | |
* | |
* @param {String} title | |
* @param {Function} fn | |
* @api private | |
*/ | |
function Test(title, fn) { | |
Runnable.call(this, title, fn); | |
this.pending = !fn; | |
this.type = 'test'; | |
} | |
/** | |
* Inherit from `Runnable.prototype`. | |
*/ | |
function F(){}; | |
F.prototype = Runnable.prototype; | |
Test.prototype = new F; | |
Test.prototype.constructor = Test; | |
}); // module: test.js | |
require.register("utils.js", function(module, exports, require){ | |
/** | |
* Module dependencies. | |
*/ | |
var fs = require('browser/fs') | |
, path = require('browser/path') | |
, join = path.join | |
, debug = require('browser/debug')('mocha:watch'); | |
/** | |
* Ignored directories. | |
*/ | |
var ignore = ['node_modules', '.git']; | |
/** | |
* Escape special characters in the given string of html. | |
* | |
* @param {String} html | |
* @return {String} | |
* @api private | |
*/ | |
exports.escape = function(html){ | |
return String(html) | |
.replace(/&/g, '&') | |
.replace(/"/g, '"') | |
.replace(/</g, '<') | |
.replace(/>/g, '>'); | |
}; | |
/** | |
* Array#forEach (<=IE8) | |
* | |
* @param {Array} array | |
* @param {Function} fn | |
* @param {Object} scope | |
* @api private | |
*/ | |
exports.forEach = function(arr, fn, scope){ | |
for (var i = 0, l = arr.length; i < l; i++) | |
fn.call(scope, arr[i], i); | |
}; | |
/** | |
* Array#indexOf (<=IE8) | |
* | |
* @parma {Array} arr | |
* @param {Object} obj to find index of | |
* @param {Number} start | |
* @api private | |
*/ | |
exports.indexOf = function(arr, obj, start){ | |
for (var i = start || 0, l = arr.length; i < l; i++) { | |
if (arr[i] === obj) | |
return i; | |
} | |
return -1; | |
}; | |
/** | |
* Array#reduce (<=IE8) | |
* | |
* @param {Array} array | |
* @param {Function} fn | |
* @param {Object} initial value | |
* @api private | |
*/ | |
exports.reduce = function(arr, fn, val){ | |
var rval = val; | |
for (var i = 0, l = arr.length; i < l; i++) { | |
rval = fn(rval, arr[i], i, arr); | |
} | |
return rval; | |
}; | |
/** | |
* Array#filter (<=IE8) | |
* | |
* @param {Array} array | |
* @param {Function} fn | |
* @api private | |
*/ | |
exports.filter = function(arr, fn){ | |
var ret = []; | |
for (var i = 0, l = arr.length; i < l; i++) { | |
var val = arr[i]; | |
if (fn(val, i, arr)) ret.push(val); | |
} | |
return ret; | |
}; | |
/** | |
* Object.keys (<=IE8) | |
* | |
* @param {Object} obj | |
* @return {Array} keys | |
* @api private | |
*/ | |
exports.keys = Object.keys || function(obj) { | |
var keys = [] | |
, has = Object.prototype.hasOwnProperty // for `window` on <=IE8 | |
for (var key in obj) { | |
if (has.call(obj, key)) { | |
keys.push(key); | |
} | |
} | |
return keys; | |
}; | |
/** | |
* Watch the given `files` for changes | |
* and invoke `fn(file)` on modification. | |
* | |
* @param {Array} files | |
* @param {Function} fn | |
* @api private | |
*/ | |
exports.watch = function(files, fn){ | |
var options = { interval: 100 }; | |
files.forEach(function(file){ | |
debug('file %s', file); | |
fs.watchFile(file, options, function(curr, prev){ | |
if (prev.mtime < curr.mtime) fn(file); | |
}); | |
}); | |
}; | |
/** | |
* Ignored files. | |
*/ | |
function ignored(path){ | |
return !~ignore.indexOf(path); | |
} | |
/** | |
* Lookup files in the given `dir`. | |
* | |
* @return {Array} | |
* @api private | |
*/ | |
exports.files = function(dir, ret){ | |
ret = ret || []; | |
fs.readdirSync(dir) | |
.filter(ignored) | |
.forEach(function(path){ | |
path = join(dir, path); | |
if (fs.statSync(path).isDirectory()) { | |
exports.files(path, ret); | |
} else if (path.match(/\.(js|coffee|litcoffee|coffee.md)$/)) { | |
ret.push(path); | |
} | |
}); | |
return ret; | |
}; | |
/** | |
* Compute a slug from the given `str`. | |
* | |
* @param {String} str | |
* @return {String} | |
* @api private | |
*/ | |
exports.slug = function(str){ | |
return str | |
.toLowerCase() | |
.replace(/ +/g, '-') | |
.replace(/[^-\w]/g, ''); | |
}; | |
/** | |
* Strip the function definition from `str`, | |
* and re-indent for pre whitespace. | |
*/ | |
exports.clean = function(str) { | |
str = str | |
.replace(/^function *\(.*\) *{/, '') | |
.replace(/\s+\}$/, ''); | |
var whitespace = str.match(/^\n?(\s*)/)[1] | |
, re = new RegExp('^' + whitespace, 'gm'); | |
str = str.replace(re, ''); | |
return exports.trim(str); | |
}; | |
/** | |
* Escape regular expression characters in `str`. | |
* | |
* @param {String} str | |
* @return {String} | |
* @api private | |
*/ | |
exports.escapeRegexp = function(str){ | |
return str.replace(/[-\\^$*+?.()|[\]{}]/g, "\\$&"); | |
}; | |
/** | |
* Trim the given `str`. | |
* | |
* @param {String} str | |
* @return {String} | |
* @api private | |
*/ | |
exports.trim = function(str){ | |
return str.replace(/^\s+|\s+$/g, ''); | |
}; | |
/** | |
* Parse the given `qs`. | |
* | |
* @param {String} qs | |
* @return {Object} | |
* @api private | |
*/ | |
exports.parseQuery = function(qs){ | |
return exports.reduce(qs.replace('?', '').split('&'), function(obj, pair){ | |
var i = pair.indexOf('=') | |
, key = pair.slice(0, i) | |
, val = pair.slice(++i); | |
obj[key] = decodeURIComponent(val); | |
return obj; | |
}, {}); | |
}; | |
/** | |
* Highlight the given string of `js`. | |
* | |
* @param {String} js | |
* @return {String} | |
* @api private | |
*/ | |
function highlight(js) { | |
return js | |
.replace(/</g, '<') | |
.replace(/>/g, '>') | |
.replace(/\/\/(.*)/gm, '<span class="comment">//$1</span>') | |
.replace(/('.*?')/gm, '<span class="string">$1</span>') | |
.replace(/(\d+\.\d+)/gm, '<span class="number">$1</span>') | |
.replace(/(\d+)/gm, '<span class="number">$1</span>') | |
.replace(/\bnew *(\w+)/gm, '<span class="keyword">new</span> <span class="init">$1</span>') | |
.replace(/\b(function|new|throw|return|var|if|else)\b/gm, '<span class="keyword">$1</span>') | |
} | |
/** | |
* Highlight the contents of tag `name`. | |
* | |
* @param {String} name | |
* @api private | |
*/ | |
exports.highlightTags = function(name) { | |
var code = document.getElementsByTagName(name); | |
for (var i = 0, len = code.length; i < len; ++i) { | |
code[i].innerHTML = highlight(code[i].innerHTML); | |
} | |
}; | |
}); // module: utils.js | |
// The global object is "self" in Web Workers. | |
global = (function() { return this; })(); | |
/** | |
* Save timer references to avoid Sinon interfering (see GH-237). | |
*/ | |
var Date = global.Date; | |
var setTimeout = global.setTimeout; | |
var setInterval = global.setInterval; | |
var clearTimeout = global.clearTimeout; | |
var clearInterval = global.clearInterval; | |
/** | |
* Node shims. | |
* | |
* These are meant only to allow | |
* mocha.js to run untouched, not | |
* to allow running node code in | |
* the browser. | |
*/ | |
var process = {}; | |
process.exit = function(status){}; | |
process.stdout = {}; | |
/** | |
* Remove uncaughtException listener. | |
*/ | |
process.removeListener = function(e){ | |
if ('uncaughtException' == e) { | |
global.onerror = function() {}; | |
} | |
}; | |
/** | |
* Implements uncaughtException listener. | |
*/ | |
process.on = function(e, fn){ | |
if ('uncaughtException' == e) { | |
global.onerror = function(err, url, line){ | |
fn(new Error(err + ' (' + url + ':' + line + ')')); | |
}; | |
} | |
}; | |
/** | |
* Expose mocha. | |
*/ | |
var Mocha = global.Mocha = require('mocha'), | |
mocha = global.mocha = new Mocha({ reporter: 'html' }); | |
var immediateQueue = [] | |
, immediateTimeout; | |
function timeslice() { | |
var immediateStart = new Date().getTime(); | |
while (immediateQueue.length && (new Date().getTime() - immediateStart) < 100) { | |
immediateQueue.shift()(); | |
} | |
if (immediateQueue.length) { | |
immediateTimeout = setTimeout(timeslice, 0); | |
} else { | |
immediateTimeout = null; | |
} | |
} | |
/** | |
* High-performance override of Runner.immediately. | |
*/ | |
Mocha.Runner.immediately = function(callback) { | |
immediateQueue.push(callback); | |
if (!immediateTimeout) { | |
immediateTimeout = setTimeout(timeslice, 0); | |
} | |
}; | |
/** | |
* Override ui to ensure that the ui functions are initialized. | |
* Normally this would happen in Mocha.prototype.loadFiles. | |
*/ | |
mocha.ui = function(ui){ | |
Mocha.prototype.ui.call(this, ui); | |
this.suite.emit('pre-require', global, null, this); | |
return this; | |
}; | |
/** | |
* Setup mocha with the given setting options. | |
*/ | |
mocha.setup = function(opts){ | |
if ('string' == typeof opts) opts = { ui: opts }; | |
for (var opt in opts) this[opt](opts[opt]); | |
return this; | |
}; | |
/** | |
* Run mocha, returning the Runner. | |
*/ | |
mocha.run = function(fn){ | |
var options = mocha.options; | |
mocha.globals('location'); | |
var query = Mocha.utils.parseQuery(global.location.search || ''); | |
if (query.grep) mocha.grep(query.grep); | |
if (query.invert) mocha.invert(); | |
return Mocha.prototype.run.call(mocha, function(){ | |
// The DOM Document is not available in Web Workers. | |
if (global.document) { | |
Mocha.utils.highlightTags('code'); | |
} | |
if (fn) fn(); | |
}); | |
}; | |
/** | |
* Expose the process shim. | |
*/ | |
Mocha.process = process; | |
})(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ | |
'use strict'; | |
var utils = require('./utils'); | |
function wrapError(callback) { | |
// provide more helpful error message | |
return function (err, res) { | |
if (err) { | |
if (err.name === 'unknown_error') { | |
err.message = (err.message || '') + | |
' Unknown error! Did you remember to enable CORS?'; | |
} | |
} | |
return callback(err, res); | |
}; | |
} | |
exports.signup = utils.toPromise(function (username, password, opts, callback) { | |
var db = this; | |
if (typeof callback === 'undefined') { | |
callback = typeof opts === 'undefined' ? (typeof password === 'undefined' ? | |
username : password) : opts; | |
opts = {}; | |
} | |
if (['http', 'https'].indexOf(db.type()) === -1) { | |
return callback(new AuthError('This plugin only works for the http/https adapter. ' + | |
'So you should use new PouchDB("http://mysite.com:5984/mydb") instead.')); | |
} else if (!username) { | |
return callback(new AuthError('You must provide a username')); | |
} else if (!password) { | |
return callback(new AuthError('You must provide a password')); | |
} | |
var userId = 'org.couchdb.user:' + username; | |
var user = { | |
name : username, | |
password : password, | |
roles : opts.roles || [], | |
type : 'user', | |
_id : userId | |
}; | |
var reservedWords = ['name', 'password', 'roles', 'type', 'salt', 'metadata']; | |
if (opts.metadata) { | |
for (var key in opts.metadata) { | |
if (opts.hasOwnProperty(key)) { | |
if (reservedWords.indexOf(key) !== -1 || key.startsWith('_')) { | |
return callback(new AuthError('cannot use reserved word in metadata: "' + key + '"')); | |
} | |
} | |
} | |
user = utils.extend(true, user, opts.metadata); | |
} | |
var url = utils.getUsersUrl(db) + '/' + encodeURIComponent(userId); | |
var ajaxOpts = utils.extend(true, { | |
method : 'PUT', | |
url : url, | |
body : user | |
}, opts.ajax || {}); | |
utils.ajax(ajaxOpts, wrapError(callback)); | |
}); | |
exports.signUp = exports.signup; | |
exports.login = utils.toPromise(function (username, password, opts, callback) { | |
var db = this; | |
if (typeof callback === 'undefined') { | |
callback = opts; | |
opts = {}; | |
} | |
if (['http', 'https'].indexOf(db.type()) === -1) { | |
return callback(new AuthError('this plugin only works for the http/https adapter')); | |
} | |
if (!username) { | |
return callback(new AuthError('you must provide a username')); | |
} else if (!password) { | |
return callback(new AuthError('you must provide a password')); | |
} | |
var ajaxOpts = utils.extend(true, { | |
method : 'POST', | |
url : utils.getSessionUrl(db), | |
body : {name : username, password : password} | |
}, opts.ajax || {}); | |
utils.ajax(ajaxOpts, wrapError(callback)); | |
}); | |
exports.logIn = exports.login; | |
exports.logout = utils.toPromise(function (opts, callback) { | |
var db = this; | |
if (typeof callback === 'undefined') { | |
callback = opts; | |
opts = {}; | |
} | |
var ajaxOpts = utils.extend(true, { | |
method : 'DELETE', | |
url : utils.getSessionUrl(db) | |
}, opts.ajax || {}); | |
utils.ajax(ajaxOpts, wrapError(callback)); | |
}); | |
exports.logOut = exports.logout; | |
exports.getSession = utils.toPromise(function (opts, callback) { | |
var db = this; | |
if (typeof callback === 'undefined') { | |
callback = opts; | |
opts = {}; | |
} | |
var url = utils.getSessionUrl(db); | |
var ajaxOpts = utils.extend(true, { | |
method : 'GET', | |
url : url | |
}, opts.ajax || {}); | |
utils.ajax(ajaxOpts, wrapError(callback)); | |
}); | |
exports.getUser = utils.toPromise(function (username, opts, callback) { | |
var db = this; | |
if (typeof callback === 'undefined') { | |
callback = typeof opts === 'undefined' ? username : opts; | |
opts = {}; | |
} | |
if (!username) { | |
return callback(new AuthError('you must provide a username')); | |
} | |
var url = utils.getUsersUrl(db); | |
var ajaxOpts = utils.extend(true, { | |
method : 'GET', | |
url : url + '/' + encodeURIComponent('org.couchdb.user:' + username) | |
}, opts.ajax || {}); | |
utils.ajax(ajaxOpts, wrapError(callback)); | |
}); | |
exports.changePassword = utils.toPromise(function (username, password, opts, callback) { | |
var db = this; | |
if (typeof callback === 'undefined') { | |
callback = typeof opts === 'undefined' ? (typeof password === 'undefined' ? | |
username : password) : opts; | |
opts = {}; | |
} | |
if (['http', 'https'].indexOf(db.type()) === -1) { | |
return callback(new AuthError('This plugin only works for the http/https adapter. ' + | |
'So you should use new PouchDB("http://mysite.com:5984/mydb") instead.')); | |
} else if (!username) { | |
return callback(new AuthError('You must provide a username')); | |
} else if (!password) { | |
return callback(new AuthError('You must provide a password')); | |
} | |
return db.getUser(username, opts, function (error, user) { | |
if (error) { | |
return callback(error); | |
} | |
user.password = password; | |
var url = utils.getUsersUrl(db) + '/' + encodeURIComponent(user._id); | |
var ajaxOpts = utils.extend(true, { | |
method : 'PUT', | |
url : url, | |
body : user | |
}, opts.ajax || {}); | |
utils.ajax(ajaxOpts, wrapError(callback)); | |
}); | |
}); | |
function AuthError(message) { | |
this.status = 400; | |
this.name = 'authentication_error'; | |
this.message = message; | |
this.error = true; | |
try { | |
Error.captureStackTrace(this, AuthError); | |
} catch (e) {} | |
} | |
utils.inherits(AuthError, Error); | |
if (typeof window !== 'undefined' && window.PouchDB) { | |
window.PouchDB.plugin(exports); | |
} | |
},{"./utils":2}],2:[function(require,module,exports){ | |
(function (process){ | |
'use strict'; | |
var Promise = require('pouchdb/extras/promise'); | |
function getBaseUrl(db) { | |
return db.getUrl().replace(/\/[^\/]+\/?$/, ''); | |
} | |
exports.getUsersUrl = function (db) { | |
return getBaseUrl(db) + '/_users'; | |
}; | |
exports.getSessionUrl = function (db) { | |
return getBaseUrl(db) + '/_session'; | |
}; | |
exports.once = function (fun) { | |
var called = false; | |
return exports.getArguments(function (args) { | |
if (called) { | |
console.trace(); | |
throw new Error('once called more than once'); | |
} else { | |
called = true; | |
fun.apply(this, args); | |
} | |
}); | |
}; | |
exports.getArguments = function (fun) { | |
return function () { | |
var len = arguments.length; | |
var args = new Array(len); | |
var i = -1; | |
while (++i < len) { | |
args[i] = arguments[i]; | |
} | |
return fun.call(this, args); | |
}; | |
}; | |
exports.toPromise = function (func) { | |
//create the function we will be returning | |
return exports.getArguments(function (args) { | |
var self = this; | |
var tempCB = (typeof args[args.length - 1] === 'function') ? args.pop() : false; | |
// if the last argument is a function, assume its a callback | |
var usedCB; | |
if (tempCB) { | |
// if it was a callback, create a new callback which calls it, | |
// but do so async so we don't trap any errors | |
usedCB = function (err, resp) { | |
process.nextTick(function () { | |
tempCB(err, resp); | |
}); | |
}; | |
} | |
var promise = new Promise(function (fulfill, reject) { | |
try { | |
var callback = exports.once(function (err, mesg) { | |
if (err) { | |
reject(err); | |
} else { | |
fulfill(mesg); | |
} | |
}); | |
// create a callback for this invocation | |
// apply the function in the orig context | |
args.push(callback); | |
func.apply(self, args); | |
} catch (e) { | |
reject(e); | |
} | |
}); | |
// if there is a callback, call it back | |
if (usedCB) { | |
promise.then(function (result) { | |
usedCB(null, result); | |
}, usedCB); | |
} | |
promise.cancel = function () { | |
return this; | |
}; | |
return promise; | |
}); | |
}; | |
exports.inherits = require('inherits'); | |
exports.extend = require('pouchdb-extend'); | |
exports.ajax = require('pouchdb/extras/ajax'); | |
exports.clone = function (obj) { | |
return exports.extend(true, {}, obj); | |
}; | |
exports.uuid = require('./uuid'); | |
exports.Promise = Promise; | |
}).call(this,require("/Users/nlawson/workspace-node/pouchdb-authentication/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js")) | |
},{"./uuid":3,"/Users/nlawson/workspace-node/pouchdb-authentication/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":46,"inherits":79,"pouchdb-extend":99,"pouchdb/extras/ajax":100,"pouchdb/extras/promise":101}],3:[function(require,module,exports){ | |
"use strict"; | |
// BEGIN Math.uuid.js | |
/*! | |
Math.uuid.js (v1.4) | |
http://www.broofa.com | |
mailto:robert@broofa.com | |
Copyright (c) 2010 Robert Kieffer | |
Dual licensed under the MIT and GPL licenses. | |
*/ | |
/* | |
* Generate a random uuid. | |
* | |
* USAGE: Math.uuid(length, radix) | |
* length - the desired number of characters | |
* radix - the number of allowable values for each character. | |
* | |
* EXAMPLES: | |
* // No arguments - returns RFC4122, version 4 ID | |
* >>> Math.uuid() | |
* "92329D39-6F5C-4520-ABFC-AAB64544E172" | |
* | |
* // One argument - returns ID of the specified length | |
* >>> Math.uuid(15) // 15 character ID (default base=62) | |
* "VcydxgltxrVZSTV" | |
* | |
* // Two arguments - returns ID of the specified length, and radix. | |
* // (Radix must be <= 62) | |
* >>> Math.uuid(8, 2) // 8 character ID (base=2) | |
* "01001010" | |
* >>> Math.uuid(8, 10) // 8 character ID (base=10) | |
* "47473046" | |
* >>> Math.uuid(8, 16) // 8 character ID (base=16) | |
* "098F4D35" | |
*/ | |
var chars = ( | |
'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ' + | |
'abcdefghijklmnopqrstuvwxyz' | |
).split(''); | |
function getValue(radix) { | |
return 0 | Math.random() * radix; | |
} | |
function uuid(len, radix) { | |
radix = radix || chars.length; | |
var out = ''; | |
var i = -1; | |
if (len) { | |
// Compact form | |
while (++i < len) { | |
out += chars[getValue(radix)]; | |
} | |
return out; | |
} | |
// rfc4122, version 4 form | |
// Fill in random data. At i==19 set the high bits of clock sequence as | |
// per rfc4122, sec. 4.1.5 | |
while (++i < 36) { | |
switch (i) { | |
case 8: | |
case 13: | |
case 18: | |
case 23: | |
out += '-'; | |
break; | |
case 19: | |
out += chars[(getValue(16) & 0x3) | 0x8]; | |
break; | |
default: | |
out += chars[getValue(16)]; | |
} | |
} | |
return out; | |
} | |
module.exports = uuid; | |
},{}],4:[function(require,module,exports){ | |
/** | |
* Copyright (c) 2014 Petka Antonov | |
* | |
* 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:</p> | |
* | |
* 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. | |
* | |
*/ | |
"use strict"; | |
module.exports = function(Promise, Promise$_CreatePromiseArray, PromiseArray) { | |
var SomePromiseArray = require("./some_promise_array.js")(PromiseArray); | |
function Promise$_Any(promises, useBound) { | |
var ret = Promise$_CreatePromiseArray( | |
promises, | |
SomePromiseArray, | |
useBound === true && promises._isBound() | |
? promises._boundTo | |
: void 0 | |
); | |
var promise = ret.promise(); | |
if (promise.isRejected()) { | |
return promise; | |
} | |
ret.setHowMany(1); | |
ret.setUnwrap(); | |
ret.init(); | |
return promise; | |
} | |
Promise.any = function Promise$Any(promises) { | |
return Promise$_Any(promises, false); | |
}; | |
Promise.prototype.any = function Promise$any() { | |
return Promise$_Any(this, true); | |
}; | |
}; | |
},{"./some_promise_array.js":36}],5:[function(require,module,exports){ | |
/** | |
* Copyright (c) 2014 Petka Antonov | |
* | |
* 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:</p> | |
* | |
* 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. | |
* | |
*/ | |
"use strict"; | |
var schedule = require("./schedule.js"); | |
var Queue = require("./queue.js"); | |
var errorObj = require("./util.js").errorObj; | |
var tryCatch1 = require("./util.js").tryCatch1; | |
var process = require("./global.js").process; | |
function Async() { | |
this._isTickUsed = false; | |
this._length = 0; | |
this._lateBuffer = new Queue(); | |
this._functionBuffer = new Queue(25000 * 3); | |
var self = this; | |
this.consumeFunctionBuffer = function Async$consumeFunctionBuffer() { | |
self._consumeFunctionBuffer(); | |
}; | |
} | |
Async.prototype.haveItemsQueued = function Async$haveItemsQueued() { | |
return this._length > 0; | |
}; | |
Async.prototype.invokeLater = function Async$invokeLater(fn, receiver, arg) { | |
if (process !== void 0 && | |
process.domain != null && | |
!fn.domain) { | |
fn = process.domain.bind(fn); | |
} | |
this._lateBuffer.push(fn, receiver, arg); | |
this._queueTick(); | |
}; | |
Async.prototype.invoke = function Async$invoke(fn, receiver, arg) { | |
if (process !== void 0 && | |
process.domain != null && | |
!fn.domain) { | |
fn = process.domain.bind(fn); | |
} | |
var functionBuffer = this._functionBuffer; | |
functionBuffer.push(fn, receiver, arg); | |
this._length = functionBuffer.length(); | |
this._queueTick(); | |
}; | |
Async.prototype._consumeFunctionBuffer = | |
function Async$_consumeFunctionBuffer() { | |
var functionBuffer = this._functionBuffer; | |
while(functionBuffer.length() > 0) { | |
var fn = functionBuffer.shift(); | |
var receiver = functionBuffer.shift(); | |
var arg = functionBuffer.shift(); | |
fn.call(receiver, arg); | |
} | |
this._reset(); | |
this._consumeLateBuffer(); | |
}; | |
Async.prototype._consumeLateBuffer = function Async$_consumeLateBuffer() { | |
var buffer = this._lateBuffer; | |
while(buffer.length() > 0) { | |
var fn = buffer.shift(); | |
var receiver = buffer.shift(); | |
var arg = buffer.shift(); | |
var res = tryCatch1(fn, receiver, arg); | |
if (res === errorObj) { | |
this._queueTick(); | |
if (fn.domain != null) { | |
fn.domain.emit("error", res.e); | |
} | |
else { | |
throw res.e; | |
} | |
} | |
} | |
}; | |
Async.prototype._queueTick = function Async$_queue() { | |
if (!this._isTickUsed) { | |
schedule(this.consumeFunctionBuffer); | |
this._isTickUsed = true; | |
} | |
}; | |
Async.prototype._reset = function Async$_reset() { | |
this._isTickUsed = false; | |
this._length = 0; | |
}; | |
module.exports = new Async(); | |
},{"./global.js":18,"./queue.js":29,"./schedule.js":32,"./util.js":40}],6:[function(require,module,exports){ | |
/** | |
* Copyright (c) 2014 Petka Antonov | |
* | |
* 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:</p> | |
* | |
* 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. | |
* | |
*/ | |
"use strict"; | |
var Promise = require("./promise.js")(); | |
module.exports = Promise; | |
},{"./promise.js":22}],7:[function(require,module,exports){ | |
/** | |
* Copyright (c) 2014 Petka Antonov | |
* | |
* 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:</p> | |
* | |
* 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. | |
* | |
*/ | |
"use strict"; | |
module.exports = function(Promise) { | |
Promise.prototype.call = function Promise$call(propertyName) { | |
var $_len = arguments.length;var args = new Array($_len - 1); for(var $_i = 1; $_i < $_len; ++$_i) {args[$_i - 1] = arguments[$_i];} | |
return this._then(function(obj) { | |
return obj[propertyName].apply(obj, args); | |
}, | |
void 0, | |
void 0, | |
void 0, | |
void 0 | |
); | |
}; | |
function Promise$getter(obj) { | |
var prop = typeof this === "string" | |
? this | |
: ("" + this); | |
return obj[prop]; | |
} | |
Promise.prototype.get = function Promise$get(propertyName) { | |
return this._then( | |
Promise$getter, | |
void 0, | |
void 0, | |
propertyName, | |
void 0 | |
); | |
}; | |
}; | |
},{}],8:[function(require,module,exports){ | |
/** | |
* Copyright (c) 2014 Petka Antonov | |
* | |
* 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:</p> | |
* | |
* 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. | |
* | |
*/ | |
"use strict"; | |
module.exports = function(Promise, INTERNAL) { | |
var errors = require("./errors.js"); | |
var async = require("./async.js"); | |
var CancellationError = errors.CancellationError; | |
Promise.prototype._cancel = function Promise$_cancel() { | |
if (!this.isCancellable()) return this; | |
var parent; | |
var promiseToReject = this; | |
while ((parent = promiseToReject._cancellationParent) !== void 0 && | |
parent.isCancellable()) { | |
promiseToReject = parent; | |
} | |
var err = new CancellationError(); | |
promiseToReject._attachExtraTrace(err); | |
promiseToReject._rejectUnchecked(err); | |
}; | |
Promise.prototype.cancel = function Promise$cancel() { | |
if (!this.isCancellable()) return this; | |
async.invokeLater(this._cancel, this, void 0); | |
return this; | |
}; | |
Promise.prototype.cancellable = function Promise$cancellable() { | |
if (this._cancellable()) return this; | |
this._setCancellable(); | |
this._cancellationParent = void 0; | |
return this; | |
}; | |
Promise.prototype.uncancellable = function Promise$uncancellable() { | |
var ret = new Promise(INTERNAL); | |
ret._setTrace(this); | |
ret._follow(this); | |
ret._unsetCancellable(); | |
if (this._isBound()) ret._setBoundTo(this._boundTo); | |
return ret; | |
}; | |
Promise.prototype.fork = | |
function Promise$fork(didFulfill, didReject, didProgress) { | |
var ret = this._then(didFulfill, didReject, didProgress, | |
void 0, void 0); | |
ret._setCancellable(); | |
ret._cancellationParent = void 0; | |
return ret; | |
}; | |
}; | |
},{"./async.js":5,"./errors.js":12}],9:[function(require,module,exports){ | |
/** | |
* Copyright (c) 2014 Petka Antonov | |
* | |
* 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:</p> | |
* | |
* 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. | |
* | |
*/ | |
"use strict"; | |
module.exports = function() { | |
var inherits = require("./util.js").inherits; | |
var defineProperty = require("./es5.js").defineProperty; | |
var rignore = new RegExp( | |
"\\b(?:[a-zA-Z0-9.]+\\$_\\w+|" + | |
"tryCatch(?:1|2|Apply)|new \\w*PromiseArray|" + | |
"\\w*PromiseArray\\.\\w*PromiseArray|" + | |
"setTimeout|CatchFilter\\$_\\w+|makeNodePromisified|processImmediate|" + | |
"process._tickCallback|nextTick|Async\\$\\w+)\\b" | |
); | |
var rtraceline = null; | |
var formatStack = null; | |
function formatNonError(obj) { | |
var str; | |
if (typeof obj === "function") { | |
str = "[function " + | |
(obj.name || "anonymous") + | |
"]"; | |
} | |
else { | |
str = obj.toString(); | |
var ruselessToString = /\[object [a-zA-Z0-9$_]+\]/; | |
if (ruselessToString.test(str)) { | |
try { | |
var newStr = JSON.stringify(obj); | |
str = newStr; | |
} | |
catch(e) { | |
} | |
} | |
if (str.length === 0) { | |
str = "(empty array)"; | |
} | |
} | |
return ("(<" + snip(str) + ">, no stack trace)"); | |
} | |
function snip(str) { | |
var maxChars = 41; | |
if (str.length < maxChars) { | |
return str; | |
} | |
return str.substr(0, maxChars - 3) + "..."; | |
} | |
function CapturedTrace(ignoreUntil, isTopLevel) { | |
this.captureStackTrace(CapturedTrace, isTopLevel); | |
} | |
inherits(CapturedTrace, Error); | |
CapturedTrace.prototype.captureStackTrace = | |
function CapturedTrace$captureStackTrace(ignoreUntil, isTopLevel) { | |
captureStackTrace(this, ignoreUntil, isTopLevel); | |
}; | |
CapturedTrace.possiblyUnhandledRejection = | |
function CapturedTrace$PossiblyUnhandledRejection(reason) { | |
if (typeof console === "object") { | |
var message; | |
if (typeof reason === "object" || typeof reason === "function") { | |
var stack = reason.stack; | |
message = "Possibly unhandled " + formatStack(stack, reason); | |
} | |
else { | |
message = "Possibly unhandled " + String(reason); | |
} | |
if (typeof console.error === "function" || | |
typeof console.error === "object") { | |
console.error(message); | |
} | |
else if (typeof console.log === "function" || | |
typeof console.log === "object") { | |
console.log(message); | |
} | |
} | |
}; | |
CapturedTrace.combine = function CapturedTrace$Combine(current, prev) { | |
var curLast = current.length - 1; | |
for (var i = prev.length - 1; i >= 0; --i) { | |
var line = prev[i]; | |
if (current[curLast] === line) { | |
current.pop(); | |
curLast--; | |
} | |
else { | |
break; | |
} | |
} | |
current.push("From previous event:"); | |
var lines = current.concat(prev); | |
var ret = []; | |
for (var i = 0, len = lines.length; i < len; ++i) { | |
if ((rignore.test(lines[i]) || | |
(i > 0 && !rtraceline.test(lines[i])) && | |
lines[i] !== "From previous event:") | |
) { | |
continue; | |
} | |
ret.push(lines[i]); | |
} | |
return ret; | |
}; | |
CapturedTrace.isSupported = function CapturedTrace$IsSupported() { | |
return typeof captureStackTrace === "function"; | |
}; | |
var captureStackTrace = (function stackDetection() { | |
if (typeof Error.stackTraceLimit === "number" && | |
typeof Error.captureStackTrace === "function") { | |
rtraceline = /^\s*at\s*/; | |
formatStack = function(stack, error) { | |
if (typeof stack === "string") return stack; | |
if (error.name !== void 0 && | |
error.message !== void 0) { | |
return error.name + ". " + error.message; | |
} | |
return formatNonError(error); | |
}; | |
var captureStackTrace = Error.captureStackTrace; | |
return function CapturedTrace$_captureStackTrace( | |
receiver, ignoreUntil) { | |
captureStackTrace(receiver, ignoreUntil); | |
}; | |
} | |
var err = new Error(); | |
if (typeof err.stack === "string" && | |
typeof "".startsWith === "function" && | |
(err.stack.startsWith("stackDetection@")) && | |
stackDetection.name === "stackDetection") { | |
defineProperty(Error, "stackTraceLimit", { | |
writable: true, | |
enumerable: false, | |
configurable: false, | |
value: 25 | |
}); | |
rtraceline = /@/; | |
var rline = /[@\n]/; | |
formatStack = function(stack, error) { | |
if (typeof stack === "string") { | |
return (error.name + ". " + error.message + "\n" + stack); | |
} | |
if (error.name !== void 0 && | |
error.message !== void 0) { | |
return error.name + ". " + error.message; | |
} | |
return formatNonError(error); | |
}; | |
return function captureStackTrace(o) { | |
var stack = new Error().stack; | |
var split = stack.split(rline); | |
var len = split.length; | |
var ret = ""; | |
for (var i = 0; i < len; i += 2) { | |
ret += split[i]; | |
ret += "@"; | |
ret += split[i + 1]; | |
ret += "\n"; | |
} | |
o.stack = ret; | |
}; | |
} | |
else { | |
formatStack = function(stack, error) { | |
if (typeof stack === "string") return stack; | |
if ((typeof error === "object" || | |
typeof error === "function") && | |
error.name !== void 0 && | |
error.message !== void 0) { | |
return error.name + ". " + error.message; | |
} | |
return formatNonError(error); | |
}; | |
return null; | |
} | |
})(); | |
return CapturedTrace; | |
}; | |
},{"./es5.js":14,"./util.js":40}],10:[function(require,module,exports){ | |
/** | |
* Copyright (c) 2014 Petka Antonov | |
* | |
* 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:</p> | |
* | |
* 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. | |
* | |
*/ | |
"use strict"; | |
module.exports = function(NEXT_FILTER) { | |
var util = require("./util.js"); | |
var errors = require("./errors.js"); | |
var tryCatch1 = util.tryCatch1; | |
var errorObj = util.errorObj; | |
var keys = require("./es5.js").keys; | |
var TypeError = errors.TypeError; | |
function CatchFilter(instances, callback, promise) { | |
this._instances = instances; | |
this._callback = callback; | |
this._promise = promise; | |
} | |
function CatchFilter$_safePredicate(predicate, e) { | |
var safeObject = {}; | |
var retfilter = tryCatch1(predicate, safeObject, e); | |
if (retfilter === errorObj) return retfilter; | |
var safeKeys = keys(safeObject); | |
if (safeKeys.length) { | |
errorObj.e = new TypeError( | |
"Catch filter must inherit from Error " | |
+ "or be a simple predicate function"); | |
return errorObj; | |
} | |
return retfilter; | |
} | |
CatchFilter.prototype.doFilter = function CatchFilter$_doFilter(e) { | |
var cb = this._callback; | |
var promise = this._promise; | |
var boundTo = promise._isBound() ? promise._boundTo : void 0; | |
for (var i = 0, len = this._instances.length; i < len; ++i) { | |
var item = this._instances[i]; | |
var itemIsErrorType = item === Error || | |
(item != null && item.prototype instanceof Error); | |
if (itemIsErrorType && e instanceof item) { | |
var ret = tryCatch1(cb, boundTo, e); | |
if (ret === errorObj) { | |
NEXT_FILTER.e = ret.e; | |
return NEXT_FILTER; | |
} | |
return ret; | |
} else if (typeof item === "function" && !itemIsErrorType) { | |
var shouldHandle = CatchFilter$_safePredicate(item, e); | |
if (shouldHandle === errorObj) { | |
var trace = errors.canAttach(errorObj.e) | |
? errorObj.e | |
: new Error(errorObj.e + ""); | |
this._promise._attachExtraTrace(trace); | |
e = errorObj.e; | |
break; | |
} else if (shouldHandle) { | |
var ret = tryCatch1(cb, boundTo, e); | |
if (ret === errorObj) { | |
NEXT_FILTER.e = ret.e; | |
return NEXT_FILTER; | |
} | |
return ret; | |
} | |
} | |
} | |
NEXT_FILTER.e = e; | |
return NEXT_FILTER; | |
}; | |
return CatchFilter; | |
}; | |
},{"./errors.js":12,"./es5.js":14,"./util.js":40}],11:[function(require,module,exports){ | |
/** | |
* Copyright (c) 2014 Petka Antonov | |
* | |
* 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:</p> | |
* | |
* 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. | |
* | |
*/ | |
"use strict"; | |
var util = require("./util.js"); | |
var isPrimitive = util.isPrimitive; | |
var wrapsPrimitiveReceiver = util.wrapsPrimitiveReceiver; | |
module.exports = function(Promise) { | |
var returner = function Promise$_returner() { | |
return this; | |
}; | |
var thrower = function Promise$_thrower() { | |
throw this; | |
}; | |
var wrapper = function Promise$_wrapper(value, action) { | |
if (action === 1) { | |
return function Promise$_thrower() { | |
throw value; | |
}; | |
} | |
else if (action === 2) { | |
return function Promise$_returner() { | |
return value; | |
}; | |
} | |
}; | |
Promise.prototype["return"] = | |
Promise.prototype.thenReturn = | |
function Promise$thenReturn(value) { | |
if (wrapsPrimitiveReceiver && isPrimitive(value)) { | |
return this._then( | |
wrapper(value, 2), | |
void 0, | |
void 0, | |
void 0, | |
void 0 | |
); | |
} | |
return this._then(returner, void 0, void 0, value, void 0); | |
}; | |
Promise.prototype["throw"] = | |
Promise.prototype.thenThrow = | |
function Promise$thenThrow(reason) { | |
if (wrapsPrimitiveReceiver && isPrimitive(reason)) { | |
return this._then( | |
wrapper(reason, 1), | |
void 0, | |
void 0, | |
void 0, | |
void 0 | |
); | |
} | |
return this._then(thrower, void 0, void 0, reason, void 0); | |
}; | |
}; | |
},{"./util.js":40}],12:[function(require,module,exports){ | |
/** | |
* Copyright (c) 2014 Petka Antonov | |
* | |
* 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:</p> | |
* | |
* 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. | |
* | |
*/ | |
"use strict"; | |
var global = require("./global.js"); | |
var Objectfreeze = require("./es5.js").freeze; | |
var util = require("./util.js"); | |
var inherits = util.inherits; | |
var notEnumerableProp = util.notEnumerableProp; | |
var Error = global.Error; | |
function markAsOriginatingFromRejection(e) { | |
try { | |
notEnumerableProp(e, "isAsync", true); | |
} | |
catch(ignore) {} | |
} | |
function originatesFromRejection(e) { | |
if (e == null) return false; | |
return ((e instanceof RejectionError) || | |
e["isAsync"] === true); | |
} | |
function isError(obj) { | |
return obj instanceof Error; | |
} | |
function canAttach(obj) { | |
return isError(obj); | |
} | |
function subError(nameProperty, defaultMessage) { | |
function SubError(message) { | |
if (!(this instanceof SubError)) return new SubError(message); | |
this.message = typeof message === "string" ? message : defaultMessage; | |
this.name = nameProperty; | |
if (Error.captureStackTrace) { | |
Error.captureStackTrace(this, this.constructor); | |
} | |
} | |
inherits(SubError, Error); | |
return SubError; | |
} | |
var TypeError = global.TypeError; | |
if (typeof TypeError !== "function") { | |
TypeError = subError("TypeError", "type error"); | |
} | |
var RangeError = global.RangeError; | |
if (typeof RangeError !== "function") { | |
RangeError = subError("RangeError", "range error"); | |
} | |
var CancellationError = subError("CancellationError", "cancellation error"); | |
var TimeoutError = subError("TimeoutError", "timeout error"); | |
function RejectionError(message) { | |
this.name = "RejectionError"; | |
this.message = message; | |
this.cause = message; | |
this.isAsync = true; | |
if (message instanceof Error) { | |
this.message = message.message; | |
this.stack = message.stack; | |
} | |
else if (Error.captureStackTrace) { | |
Error.captureStackTrace(this, this.constructor); | |
} | |
} | |
inherits(RejectionError, Error); | |
var key = "__BluebirdErrorTypes__"; | |
var errorTypes = global[key]; | |
if (!errorTypes) { | |
errorTypes = Objectfreeze({ | |
CancellationError: CancellationError, | |
TimeoutError: TimeoutError, | |
RejectionError: RejectionError | |
}); | |
notEnumerableProp(global, key, errorTypes); | |
} | |
module.exports = { | |
Error: Error, | |
TypeError: TypeError, | |
RangeError: RangeError, | |
CancellationError: errorTypes.CancellationError, | |
RejectionError: errorTypes.RejectionError, | |
TimeoutError: errorTypes.TimeoutError, | |
originatesFromRejection: originatesFromRejection, | |
markAsOriginatingFromRejection: markAsOriginatingFromRejection, | |
canAttach: canAttach | |
}; | |
},{"./es5.js":14,"./global.js":18,"./util.js":40}],13:[function(require,module,exports){ | |
/** | |
* Copyright (c) 2014 Petka Antonov | |
* | |
* 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:</p> | |
* | |
* 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. | |
* | |
*/ | |
"use strict"; | |
module.exports = function(Promise) { | |
var TypeError = require('./errors.js').TypeError; | |
function apiRejection(msg) { | |
var error = new TypeError(msg); | |
var ret = Promise.rejected(error); | |
var parent = ret._peekContext(); | |
if (parent != null) { | |
parent._attachExtraTrace(error); | |
} | |
return ret; | |
} | |
return apiRejection; | |
}; | |
},{"./errors.js":12}],14:[function(require,module,exports){ | |
/** | |
* Copyright (c) 2014 Petka Antonov | |
* | |
* 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:</p> | |
* | |
* 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. | |
* | |
*/ | |
var isES5 = (function(){ | |
"use strict"; | |
return this === void 0; | |
})(); | |
if (isES5) { | |
module.exports = { | |
freeze: Object.freeze, | |
defineProperty: Object.defineProperty, | |
keys: Object.keys, | |
getPrototypeOf: Object.getPrototypeOf, | |
isArray: Array.isArray, | |
isES5: isES5 | |
}; | |
} | |
else { | |
var has = {}.hasOwnProperty; | |
var str = {}.toString; | |
var proto = {}.constructor.prototype; | |
var ObjectKeys = function ObjectKeys(o) { | |
var ret = []; | |
for (var key in o) { | |
if (has.call(o, key)) { | |
ret.push(key); | |
} | |
} | |
return ret; | |
} | |
var ObjectDefineProperty = function ObjectDefineProperty(o, key, desc) { | |
o[key] = desc.value; | |
return o; | |
} | |
var ObjectFreeze = function ObjectFreeze(obj) { | |
return obj; | |
} | |
var ObjectGetPrototypeOf = function ObjectGetPrototypeOf(obj) { | |
try { | |
return Object(obj).constructor.prototype; | |
} | |
catch (e) { | |
return proto; | |
} | |
} | |
var ArrayIsArray = function ArrayIsArray(obj) { | |
try { | |
return str.call(obj) === "[object Array]"; | |
} | |
catch(e) { | |
return false; | |
} | |
} | |
module.exports = { | |
isArray: ArrayIsArray, | |
keys: ObjectKeys, | |
defineProperty: ObjectDefineProperty, | |
freeze: ObjectFreeze, | |
getPrototypeOf: ObjectGetPrototypeOf, | |
isES5: isES5 | |
}; | |
} | |
},{}],15:[function(require,module,exports){ | |
/** | |
* Copyright (c) 2014 Petka Antonov | |
* | |
* 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:</p> | |
* | |
* 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. | |
* | |
*/ | |
"use strict"; | |
module.exports = function(Promise) { | |
var isArray = require("./util.js").isArray; | |
function Promise$_filter(booleans) { | |
var values = this instanceof Promise ? this._settledValue : this; | |
var len = values.length; | |
var ret = new Array(len); | |
var j = 0; | |
for (var i = 0; i < len; ++i) { | |
if (booleans[i]) ret[j++] = values[i]; | |
} | |
ret.length = j; | |
return ret; | |
} | |
var ref = {ref: null}; | |
Promise.filter = function Promise$Filter(promises, fn) { | |
return Promise.map(promises, fn, ref) | |
._then(Promise$_filter, void 0, void 0, ref.ref, void 0); | |
}; | |
Promise.prototype.filter = function Promise$filter(fn) { | |
return this.map(fn, ref) | |
._then(Promise$_filter, void 0, void 0, ref.ref, void 0); | |
}; | |
}; | |
},{"./util.js":40}],16:[function(require,module,exports){ | |
/** | |
* Copyright (c) 2014 Petka Antonov | |
* | |
* 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:</p> | |
* | |
* 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. | |
* | |
*/ | |
"use strict"; | |
module.exports = function(Promise, NEXT_FILTER) { | |
var util = require("./util.js"); | |
var wrapsPrimitiveReceiver = util.wrapsPrimitiveReceiver; | |
var isPrimitive = util.isPrimitive; | |
var thrower = util.thrower; | |
function returnThis() { | |
return this; | |
} | |
function throwThis() { | |
throw this; | |
} | |
function return$(r) { | |
return function Promise$_returner() { | |
return r; | |
}; | |
} | |
function throw$(r) { | |
return function Promise$_thrower() { | |
throw r; | |
}; | |
} | |
function promisedFinally(ret, reasonOrValue, isFulfilled) { | |
var then; | |
if (wrapsPrimitiveReceiver && isPrimitive(reasonOrValue)) { | |
then = isFulfilled ? return$(reasonOrValue) : throw$(reasonOrValue); | |
} | |
else { | |
then = isFulfilled ? returnThis : throwThis; | |
} | |
return ret._then(then, thrower, void 0, reasonOrValue, void 0); | |
} | |
function finallyHandler(reasonOrValue) { | |
var promise = this.promise; | |
var handler = this.handler; | |
var ret = promise._isBound() | |
? handler.call(promise._boundTo) | |
: handler(); | |
if (ret !== void 0) { | |
var maybePromise = Promise._cast(ret, void 0); | |
if (maybePromise instanceof Promise) { | |
return promisedFinally(maybePromise, reasonOrValue, | |
promise.isFulfilled()); | |
} | |
} | |
if (promise.isRejected()) { | |
NEXT_FILTER.e = reasonOrValue; | |
return NEXT_FILTER; | |
} | |
else { | |
return reasonOrValue; | |
} | |
} | |
function tapHandler(value) { | |
var promise = this.promise; | |
var handler = this.handler; | |
var ret = promise._isBound() | |
? handler.call(promise._boundTo, value) | |
: handler(value); | |
if (ret !== void 0) { | |
var maybePromise = Promise._cast(ret, void 0); | |
if (maybePromise instanceof Promise) { | |
return promisedFinally(maybePromise, value, true); | |
} | |
} | |
return value; | |
} | |
Promise.prototype._passThroughHandler = | |
function Promise$_passThroughHandler(handler, isFinally) { | |
if (typeof handler !== "function") return this.then(); | |
var promiseAndHandler = { | |
promise: this, | |
handler: handler | |
}; | |
return this._then( | |
isFinally ? finallyHandler : tapHandler, | |
isFinally ? finallyHandler : void 0, void 0, | |
promiseAndHandler, void 0); | |
}; | |
Promise.prototype.lastly = | |
Promise.prototype["finally"] = function Promise$finally(handler) { | |
return this._passThroughHandler(handler, true); | |
}; | |
Promise.prototype.tap = function Promise$tap(handler) { | |
return this._passThroughHandler(handler, false); | |
}; | |
}; | |
},{"./util.js":40}],17:[function(require,module,exports){ | |
/** | |
* Copyright (c) 2014 Petka Antonov | |
* | |
* 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:</p> | |
* | |
* 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. | |
* | |
*/ | |
"use strict"; | |
module.exports = function(Promise, apiRejection, INTERNAL) { | |
var PromiseSpawn = require("./promise_spawn.js")(Promise, INTERNAL); | |
var errors = require("./errors.js"); | |
var TypeError = errors.TypeError; | |
var deprecated = require("./util.js").deprecated; | |
Promise.coroutine = function Promise$Coroutine(generatorFunction) { | |
if (typeof generatorFunction !== "function") { | |
throw new TypeError("generatorFunction must be a function"); | |
} | |
var PromiseSpawn$ = PromiseSpawn; | |
return function () { | |
var generator = generatorFunction.apply(this, arguments); | |
var spawn = new PromiseSpawn$(void 0, void 0); | |
spawn._generator = generator; | |
spawn._next(void 0); | |
return spawn.promise(); | |
}; | |
}; | |
Promise.coroutine.addYieldHandler = PromiseSpawn.addYieldHandler; | |
Promise.spawn = function Promise$Spawn(generatorFunction) { | |
deprecated("Promise.spawn is deprecated. Use Promise.coroutine instead."); | |
if (typeof generatorFunction !== "function") { | |
return apiRejection("generatorFunction must be a function"); | |
} | |
var spawn = new PromiseSpawn(generatorFunction, this); | |
var ret = spawn.promise(); | |
spawn._run(Promise.spawn); | |
return ret; | |
}; | |
}; | |
},{"./errors.js":12,"./promise_spawn.js":25,"./util.js":40}],18:[function(require,module,exports){ | |
(function (global){ | |
/** | |
* Copyright (c) 2014 Petka Antonov | |
* | |
* 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:</p> | |
* | |
* 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. | |
* | |
*/ | |
module.exports = (function() { | |
if (this !== void 0) return this; | |
try {return global;} | |
catch(e) {} | |
try {return window;} | |
catch(e) {} | |
try {return self;} | |
catch(e) {} | |
})(); | |
}).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
},{}],19:[function(require,module,exports){ | |
/** | |
* Copyright (c) 2014 Petka Antonov | |
* | |
* 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:</p> | |
* | |
* 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. | |
* | |
*/ | |
"use strict"; | |
module.exports = function(Promise, PromiseArray, INTERNAL, apiRejection) { | |
var all = Promise.all; | |
var util = require("./util.js"); | |
var canAttach = require("./errors.js").canAttach; | |
var isArray = util.isArray; | |
var _cast = Promise._cast; | |
function unpack(values) { | |
return Promise$_Map(values, this[0], this[1], this[2]); | |
} | |
function Promise$_Map(promises, fn, useBound, ref) { | |
if (typeof fn !== "function") { | |
return apiRejection("fn must be a function"); | |
} | |
var receiver = void 0; | |
if (useBound === true) { | |
if (promises._isBound()) { | |
receiver = promises._boundTo; | |
} | |
} | |
else if (useBound !== false) { | |
receiver = useBound; | |
} | |
var shouldUnwrapItems = ref !== void 0; | |
if (shouldUnwrapItems) ref.ref = promises; | |
if (promises instanceof Promise) { | |
var pack = [fn, receiver, ref]; | |
return promises._then(unpack, void 0, void 0, pack, void 0); | |
} | |
else if (!isArray(promises)) { | |
return apiRejection("expecting an array, a promise or a thenable"); | |
} | |
var promise = new Promise(INTERNAL); | |
if (receiver !== void 0) promise._setBoundTo(receiver); | |
promise._setTrace(void 0); | |
var mapping = new Mapping(promise, | |
fn, | |
promises, | |
receiver, | |
shouldUnwrapItems); | |
mapping.init(); | |
return promise; | |
} | |
var pending = {}; | |
function Mapping(promise, callback, items, receiver, shouldUnwrapItems) { | |
this.shouldUnwrapItems = shouldUnwrapItems; | |
this.index = 0; | |
this.items = items; | |
this.callback = callback; | |
this.receiver = receiver; | |
this.promise = promise; | |
this.result = new Array(items.length); | |
} | |
util.inherits(Mapping, PromiseArray); | |
Mapping.prototype.init = function Mapping$init() { | |
var items = this.items; | |
var len = items.length; | |
var result = this.result; | |
var isRejected = false; | |
for (var i = 0; i < len; ++i) { | |
var maybePromise = _cast(items[i], void 0); | |
if (maybePromise instanceof Promise) { | |
if (maybePromise.isPending()) { | |
result[i] = pending; | |
maybePromise._proxyPromiseArray(this, i); | |
} | |
else if (maybePromise.isFulfilled()) { | |
result[i] = maybePromise.value(); | |
} | |
else { | |
maybePromise._unsetRejectionIsUnhandled(); | |
if (!isRejected) { | |
this.reject(maybePromise.reason()); | |
isRejected = true; | |
} | |
} | |
} | |
else { | |
result[i] = maybePromise; | |
} | |
} | |
if (!isRejected) this.iterate(); | |
}; | |
Mapping.prototype.isResolved = function Mapping$isResolved() { | |
return this.promise === null; | |
}; | |
Mapping.prototype._promiseProgressed = | |
function Mapping$_promiseProgressed(value) { | |
if (this.isResolved()) return; | |
this.promise._progress(value); | |
}; | |
Mapping.prototype._promiseFulfilled = | |
function Mapping$_promiseFulfilled(value, index) { | |
if (this.isResolved()) return; | |
this.result[index] = value; | |
if (this.shouldUnwrapItems) this.items[index] = value; | |
if (this.index === index) this.iterate(); | |
}; | |
Mapping.prototype._promiseRejected = | |
function Mapping$_promiseRejected(reason) { | |
this.reject(reason); | |
}; | |
Mapping.prototype.reject = function Mapping$reject(reason) { | |
if (this.isResolved()) return; | |
var trace = canAttach(reason) ? reason : new Error(reason + ""); | |
this.promise._attachExtraTrace(trace); | |
this.promise._reject(reason, trace); | |
}; | |
Mapping.prototype.iterate = function Mapping$iterate() { | |
var i = this.index; | |
var items = this.items; | |
var result = this.result; | |
var len = items.length; | |
var result = this.result; | |
var receiver = this.receiver; | |
var callback = this.callback; | |
for (; i < len; ++i) { | |
var value = result[i]; | |
if (value === pending) { | |
this.index = i; | |
return; | |
} | |
try { result[i] = callback.call(receiver, value, i, len); } | |
catch (e) { return this.reject(e); } | |
} | |
this.promise._follow(all(result)); | |
this.items = this.result = this.callback = this.promise = null; | |
}; | |
Promise.prototype.map = function Promise$map(fn, ref) { | |
return Promise$_Map(this, fn, true, ref); | |
}; | |
Promise.map = function Promise$Map(promises, fn, ref) { | |
return Promise$_Map(promises, fn, false, ref); | |
}; | |
}; | |
},{"./errors.js":12,"./util.js":40}],20:[function(require,module,exports){ | |
/** | |
* Copyright (c) 2014 Petka Antonov | |
* | |
* 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:</p> | |
* | |
* 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. | |
* | |
*/ | |
"use strict"; | |
module.exports = function(Promise) { | |
var util = require("./util.js"); | |
var async = require("./async.js"); | |
var tryCatch2 = util.tryCatch2; | |
var tryCatch1 = util.tryCatch1; | |
var errorObj = util.errorObj; | |
function thrower(r) { | |
throw r; | |
} | |
function Promise$_successAdapter(val, receiver) { | |
var nodeback = this; | |
var ret = val === void 0 | |
? tryCatch1(nodeback, receiver, null) | |
: tryCatch2(nodeback, receiver, null, val); | |
if (ret === errorObj) { | |
async.invokeLater(thrower, void 0, ret.e); | |
} | |
} | |
function Promise$_errorAdapter(reason, receiver) { | |
var nodeback = this; | |
var ret = tryCatch1(nodeback, receiver, reason); | |
if (ret === errorObj) { | |
async.invokeLater(thrower, void 0, ret.e); | |
} | |
} | |
Promise.prototype.nodeify = function Promise$nodeify(nodeback) { | |
if (typeof nodeback == "function") { | |
this._then( | |
Promise$_successAdapter, | |
Promise$_errorAdapter, | |
void 0, | |
nodeback, | |
this._isBound() ? this._boundTo : null | |
); | |
} | |
return this; | |
}; | |
}; | |
},{"./async.js":5,"./util.js":40}],21:[function(require,module,exports){ | |
/** | |
* Copyright (c) 2014 Petka Antonov | |
* | |
* 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:</p> | |
* | |
* 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. | |
* | |
*/ | |
"use strict"; | |
module.exports = function(Promise, isPromiseArrayProxy) { | |
var util = require("./util.js"); | |
var async = require("./async.js"); | |
var errors = require("./errors.js"); | |
var tryCatch1 = util.tryCatch1; | |
var errorObj = util.errorObj; | |
Promise.prototype.progressed = function Promise$progressed(handler) { | |
return this._then(void 0, void 0, handler, void 0, void 0); | |
}; | |
Promise.prototype._progress = function Promise$_progress(progressValue) { | |
if (this._isFollowingOrFulfilledOrRejected()) return; | |
this._progressUnchecked(progressValue); | |
}; | |
Promise.prototype._progressHandlerAt = | |
function Promise$_progressHandlerAt(index) { | |
if (index === 0) return this._progressHandler0; | |
return this[index + 2 - 5]; | |
}; | |
Promise.prototype._doProgressWith = | |
function Promise$_doProgressWith(progression) { | |
var progressValue = progression.value; | |
var handler = progression.handler; | |
var promise = progression.promise; | |
var receiver = progression.receiver; | |
this._pushContext(); | |
var ret = tryCatch1(handler, receiver, progressValue); | |
this._popContext(); | |
if (ret === errorObj) { | |
if (ret.e != null && | |
ret.e.name !== "StopProgressPropagation") { | |
var trace = errors.canAttach(ret.e) | |
? ret.e : new Error(ret.e + ""); | |
promise._attachExtraTrace(trace); | |
promise._progress(ret.e); | |
} | |
} | |
else if (ret instanceof Promise) { | |
ret._then(promise._progress, null, null, promise, void 0); | |
} | |
else { | |
promise._progress(ret); | |
} | |
}; | |
Promise.prototype._progressUnchecked = | |
function Promise$_progressUnchecked(progressValue) { | |
if (!this.isPending()) return; | |
var len = this._length(); | |
var progress = this._progress; | |
for (var i = 0; i < len; i += 5) { | |
var handler = this._progressHandlerAt(i); | |
var promise = this._promiseAt(i); | |
if (!(promise instanceof Promise)) { | |
var receiver = this._receiverAt(i); | |
if (typeof handler === "function") { | |
handler.call(receiver, progressValue, promise); | |
} | |
else if (receiver instanceof Promise && receiver._isProxied()) { | |
receiver._progressUnchecked(progressValue); | |
} | |
else if (isPromiseArrayProxy(receiver, promise)) { | |
receiver._promiseProgressed(progressValue, promise); | |
} | |
continue; | |
} | |
if (typeof handler === "function") { | |
async.invoke(this._doProgressWith, this, { | |
handler: handler, | |
promise: promise, | |
receiver: this._receiverAt(i), | |
value: progressValue | |
}); | |
} | |
else { | |
async.invoke(progress, promise, progressValue); | |
} | |
} | |
}; | |
}; | |
},{"./async.js":5,"./errors.js":12,"./util.js":40}],22:[function(require,module,exports){ | |
(function (process){ | |
/** | |
* Copyright (c) 2014 Petka Antonov | |
* | |
* 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:</p> | |
* | |
* 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. | |
* | |
*/ | |
"use strict"; | |
module.exports = function() { | |
var global = require("./global.js"); | |
var util = require("./util.js"); | |
var async = require("./async.js"); | |
var errors = require("./errors.js"); | |
var INTERNAL = function(){}; | |
var APPLY = {}; | |
var NEXT_FILTER = {e: null}; | |
var PromiseArray = require("./promise_array.js")(Promise, INTERNAL); | |
var CapturedTrace = require("./captured_trace.js")(); | |
var CatchFilter = require("./catch_filter.js")(NEXT_FILTER); | |
var PromiseResolver = require("./promise_resolver.js"); | |
var isArray = util.isArray; | |
var errorObj = util.errorObj; | |
var tryCatch1 = util.tryCatch1; | |
var tryCatch2 = util.tryCatch2; | |
var tryCatchApply = util.tryCatchApply; | |
var RangeError = errors.RangeError; | |
var TypeError = errors.TypeError; | |
var CancellationError = errors.CancellationError; | |
var TimeoutError = errors.TimeoutError; | |
var RejectionError = errors.RejectionError; | |
var originatesFromRejection = errors.originatesFromRejection; | |
var markAsOriginatingFromRejection = errors.markAsOriginatingFromRejection; | |
var canAttach = errors.canAttach; | |
var thrower = util.thrower; | |
var apiRejection = require("./errors_api_rejection")(Promise); | |
var makeSelfResolutionError = function Promise$_makeSelfResolutionError() { | |
return new TypeError("circular promise resolution chain"); | |
}; | |
function isPromise(obj) { | |
if (obj === void 0) return false; | |
return obj instanceof Promise; | |
} | |
function isPromiseArrayProxy(receiver, promiseSlotValue) { | |
if (receiver instanceof PromiseArray) { | |
return promiseSlotValue >= 0; | |
} | |
return false; | |
} | |
function Promise(resolver) { | |
if (typeof resolver !== "function") { | |
throw new TypeError("the promise constructor requires a resolver function"); | |
} | |
if (this.constructor !== Promise) { | |
throw new TypeError("the promise constructor cannot be invoked directly"); | |
} | |
this._bitField = 0; | |
this._fulfillmentHandler0 = void 0; | |
this._rejectionHandler0 = void 0; | |
this._promise0 = void 0; | |
this._receiver0 = void 0; | |
this._settledValue = void 0; | |
this._boundTo = void 0; | |
if (resolver !== INTERNAL) this._resolveFromResolver(resolver); | |
} | |
Promise.prototype.bind = function Promise$bind(thisArg) { | |
var ret = new Promise(INTERNAL); | |
ret._setTrace(this); | |
ret._follow(this); | |
ret._setBoundTo(thisArg); | |
if (this._cancellable()) { | |
ret._setCancellable(); | |
ret._cancellationParent = this; | |
} | |
return ret; | |
}; | |
Promise.prototype.toString = function Promise$toString() { | |
return "[object Promise]"; | |
}; | |
Promise.prototype.caught = Promise.prototype["catch"] = | |
function Promise$catch(fn) { | |
var len = arguments.length; | |
if (len > 1) { | |
var catchInstances = new Array(len - 1), | |
j = 0, i; | |
for (i = 0; i < len - 1; ++i) { | |
var item = arguments[i]; | |
if (typeof item === "function") { | |
catchInstances[j++] = item; | |
} | |
else { | |
var catchFilterTypeError = | |
new TypeError( | |
"A catch filter must be an error constructor " | |
+ "or a filter function"); | |
this._attachExtraTrace(catchFilterTypeError); | |
async.invoke(this._reject, this, catchFilterTypeError); | |
return; | |
} | |
} | |
catchInstances.length = j; | |
fn = arguments[i]; | |
this._resetTrace(); | |
var catchFilter = new CatchFilter(catchInstances, fn, this); | |
return this._then(void 0, catchFilter.doFilter, void 0, | |
catchFilter, void 0); | |
} | |
return this._then(void 0, fn, void 0, void 0, void 0); | |
}; | |
Promise.prototype.then = | |
function Promise$then(didFulfill, didReject, didProgress) { | |
return this._then(didFulfill, didReject, didProgress, | |
void 0, void 0); | |
}; | |
Promise.prototype.done = | |
function Promise$done(didFulfill, didReject, didProgress) { | |
var promise = this._then(didFulfill, didReject, didProgress, | |
void 0, void 0); | |
promise._setIsFinal(); | |
}; | |
Promise.prototype.spread = function Promise$spread(didFulfill, didReject) { | |
return this._then(didFulfill, didReject, void 0, | |
APPLY, void 0); | |
}; | |
Promise.prototype.isCancellable = function Promise$isCancellable() { | |
return !this.isResolved() && | |
this._cancellable(); | |
}; | |
Promise.prototype.toJSON = function Promise$toJSON() { | |
var ret = { | |
isFulfilled: false, | |
isRejected: false, | |
fulfillmentValue: void 0, | |
rejectionReason: void 0 | |
}; | |
if (this.isFulfilled()) { | |
ret.fulfillmentValue = this._settledValue; | |
ret.isFulfilled = true; | |
} | |
else if (this.isRejected()) { | |
ret.rejectionReason = this._settledValue; | |
ret.isRejected = true; | |
} | |
return ret; | |
}; | |
Promise.prototype.all = function Promise$all() { | |
return Promise$_all(this, true); | |
}; | |
Promise.is = isPromise; | |
function Promise$_all(promises, useBound) { | |
return Promise$_CreatePromiseArray( | |
promises, | |
PromiseArray, | |
useBound === true && promises._isBound() | |
? promises._boundTo | |
: void 0 | |
).promise(); | |
} | |
Promise.all = function Promise$All(promises) { | |
return Promise$_all(promises, false); | |
}; | |
Promise.join = function Promise$Join() { | |
var $_len = arguments.length;var args = new Array($_len); for(var $_i = 0; $_i < $_len; ++$_i) {args[$_i] = arguments[$_i];} | |
return Promise$_CreatePromiseArray(args, PromiseArray, void 0).promise(); | |
}; | |
Promise.resolve = Promise.fulfilled = | |
function Promise$Resolve(value) { | |
var ret = new Promise(INTERNAL); | |
ret._setTrace(void 0); | |
if (ret._tryFollow(value)) { | |
return ret; | |
} | |
ret._cleanValues(); | |
ret._setFulfilled(); | |
ret._settledValue = value; | |
return ret; | |
}; | |
Promise.reject = Promise.rejected = function Promise$Reject(reason) { | |
var ret = new Promise(INTERNAL); | |
ret._setTrace(void 0); | |
markAsOriginatingFromRejection(reason); | |
ret._cleanValues(); | |
ret._setRejected(); | |
ret._settledValue = reason; | |
if (!canAttach(reason)) { | |
var trace = new Error(reason + ""); | |
ret._setCarriedStackTrace(trace); | |
} | |
ret._ensurePossibleRejectionHandled(); | |
return ret; | |
}; | |
Promise.prototype.error = function Promise$_error(fn) { | |
return this.caught(originatesFromRejection, fn); | |
}; | |
Promise.prototype._resolveFromSyncValue = | |
function Promise$_resolveFromSyncValue(value) { | |
if (value === errorObj) { | |
this._cleanValues(); | |
this._setRejected(); | |
this._settledValue = value.e; | |
this._ensurePossibleRejectionHandled(); | |
} | |
else { | |
var maybePromise = Promise._cast(value, void 0); | |
if (maybePromise instanceof Promise) { | |
this._follow(maybePromise); | |
} | |
else { | |
this._cleanValues(); | |
this._setFulfilled(); | |
this._settledValue = value; | |
} | |
} | |
}; | |
Promise.method = function Promise$_Method(fn) { | |
if (typeof fn !== "function") { | |
throw new TypeError("fn must be a function"); | |
} | |
return function Promise$_method() { | |
var value; | |
switch(arguments.length) { | |
case 0: value = tryCatch1(fn, this, void 0); break; | |
case 1: value = tryCatch1(fn, this, arguments[0]); break; | |
case 2: value = tryCatch2(fn, this, arguments[0], arguments[1]); break; | |
default: | |
var $_len = arguments.length;var args = new Array($_len); for(var $_i = 0; $_i < $_len; ++$_i) {args[$_i] = arguments[$_i];} | |
value = tryCatchApply(fn, args, this); break; | |
} | |
var ret = new Promise(INTERNAL); | |
ret._setTrace(void 0); | |
ret._resolveFromSyncValue(value); | |
return ret; | |
}; | |
}; | |
Promise.attempt = Promise["try"] = function Promise$_Try(fn, args, ctx) { | |
if (typeof fn !== "function") { | |
return apiRejection("fn must be a function"); | |
} | |
var value = isArray(args) | |
? tryCatchApply(fn, args, ctx) | |
: tryCatch1(fn, ctx, args); | |
var ret = new Promise(INTERNAL); | |
ret._setTrace(void 0); | |
ret._resolveFromSyncValue(value); | |
return ret; | |
}; | |
Promise.defer = Promise.pending = function Promise$Defer() { | |
var promise = new Promise(INTERNAL); | |
promise._setTrace(void 0); | |
return new PromiseResolver(promise); | |
}; | |
Promise.bind = function Promise$Bind(thisArg) { | |
var ret = new Promise(INTERNAL); | |
ret._setTrace(void 0); | |
ret._setFulfilled(); | |
ret._setBoundTo(thisArg); | |
return ret; | |
}; | |
Promise.cast = function Promise$_Cast(obj) { | |
var ret = Promise._cast(obj, void 0); | |
if (!(ret instanceof Promise)) { | |
return Promise.resolve(ret); | |
} | |
return ret; | |
}; | |
Promise.onPossiblyUnhandledRejection = | |
function Promise$OnPossiblyUnhandledRejection(fn) { | |
CapturedTrace.possiblyUnhandledRejection = typeof fn === "function" | |
? fn : void 0; | |
}; | |
var unhandledRejectionHandled; | |
Promise.onUnhandledRejectionHandled = | |
function Promise$onUnhandledRejectionHandled(fn) { | |
unhandledRejectionHandled = typeof fn === "function" ? fn : void 0; | |
}; | |
var debugging = false || !!( | |
typeof process !== "undefined" && | |
typeof process.execPath === "string" && | |
typeof process.env === "object" && | |
(process.env["BLUEBIRD_DEBUG"] || | |
process.env["NODE_ENV"] === "development") | |
); | |
Promise.longStackTraces = function Promise$LongStackTraces() { | |
if (async.haveItemsQueued() && | |
debugging === false | |
) { | |
throw new Error("cannot enable long stack traces after promises have been created"); | |
} | |
debugging = CapturedTrace.isSupported(); | |
}; | |
Promise.hasLongStackTraces = function Promise$HasLongStackTraces() { | |
return debugging && CapturedTrace.isSupported(); | |
}; | |
Promise.prototype._setProxyHandlers = | |
function Promise$_setProxyHandlers(receiver, promiseSlotValue) { | |
var index = this._length(); | |
if (index >= 524287 - 5) { | |
index = 0; | |
this._setLength(0); | |
} | |
if (index === 0) { | |
this._promise0 = promiseSlotValue; | |
this._receiver0 = receiver; | |
} | |
else { | |
var i = index - 5; | |
this[i + 3] = promiseSlotValue; | |
this[i + 4] = receiver; | |
this[i + 0] = | |
this[i + 1] = | |
this[i + 2] = void 0; | |
} | |
this._setLength(index + 5); | |
}; | |
Promise.prototype._proxyPromiseArray = | |
function Promise$_proxyPromiseArray(promiseArray, index) { | |
this._setProxyHandlers(promiseArray, index); | |
}; | |
Promise.prototype._proxyPromise = function Promise$_proxyPromise(promise) { | |
promise._setProxied(); | |
this._setProxyHandlers(promise, -1); | |
}; | |
Promise.prototype._then = | |
function Promise$_then( | |
didFulfill, | |
didReject, | |
didProgress, | |
receiver, | |
internalData | |
) { | |
var haveInternalData = internalData !== void 0; | |
var ret = haveInternalData ? internalData : new Promise(INTERNAL); | |
if (debugging && !haveInternalData) { | |
var haveSameContext = this._peekContext() === this._traceParent; | |
ret._traceParent = haveSameContext ? this._traceParent : this; | |
ret._setTrace(this); | |
} | |
if (!haveInternalData && this._isBound()) { | |
ret._setBoundTo(this._boundTo); | |
} | |
var callbackIndex = | |
this._addCallbacks(didFulfill, didReject, didProgress, ret, receiver); | |
if (!haveInternalData && this._cancellable()) { | |
ret._setCancellable(); | |
ret._cancellationParent = this; | |
} | |
if (this.isResolved()) { | |
async.invoke(this._queueSettleAt, this, callbackIndex); | |
} | |
return ret; | |
}; | |
Promise.prototype._length = function Promise$_length() { | |
return this._bitField & 524287; | |
}; | |
Promise.prototype._isFollowingOrFulfilledOrRejected = | |
function Promise$_isFollowingOrFulfilledOrRejected() { | |
return (this._bitField & 939524096) > 0; | |
}; | |
Promise.prototype._isFollowing = function Promise$_isFollowing() { | |
return (this._bitField & 536870912) === 536870912; | |
}; | |
Promise.prototype._setLength = function Promise$_setLength(len) { | |
this._bitField = (this._bitField & -524288) | | |
(len & 524287); | |
}; | |
Promise.prototype._setFulfilled = function Promise$_setFulfilled() { | |
this._bitField = this._bitField | 268435456; | |
}; | |
Promise.prototype._setRejected = function Promise$_setRejected() { | |
this._bitField = this._bitField | 134217728; | |
}; | |
Promise.prototype._setFollowing = function Promise$_setFollowing() { | |
this._bitField = this._bitField | 536870912; | |
}; | |
Promise.prototype._setIsFinal = function Promise$_setIsFinal() { | |
this._bitField = this._bitField | 33554432; | |
}; | |
Promise.prototype._isFinal = function Promise$_isFinal() { | |
return (this._bitField & 33554432) > 0; | |
}; | |
Promise.prototype._cancellable = function Promise$_cancellable() { | |
return (this._bitField & 67108864) > 0; | |
}; | |
Promise.prototype._setCancellable = function Promise$_setCancellable() { | |
this._bitField = this._bitField | 67108864; | |
}; | |
Promise.prototype._unsetCancellable = function Promise$_unsetCancellable() { | |
this._bitField = this._bitField & (~67108864); | |
}; | |
Promise.prototype._setRejectionIsUnhandled = | |
function Promise$_setRejectionIsUnhandled() { | |
this._bitField = this._bitField | 2097152; | |
}; | |
Promise.prototype._unsetRejectionIsUnhandled = | |
function Promise$_unsetRejectionIsUnhandled() { | |
this._bitField = this._bitField & (~2097152); | |
if (this._isUnhandledRejectionNotified()) { | |
this._unsetUnhandledRejectionIsNotified(); | |
this._notifyUnhandledRejectionIsHandled(); | |
} | |
}; | |
Promise.prototype._isRejectionUnhandled = | |
function Promise$_isRejectionUnhandled() { | |
return (this._bitField & 2097152) > 0; | |
}; | |
Promise.prototype._setUnhandledRejectionIsNotified = | |
function Promise$_setUnhandledRejectionIsNotified() { | |
this._bitField = this._bitField | 524288; | |
}; | |
Promise.prototype._unsetUnhandledRejectionIsNotified = | |
function Promise$_unsetUnhandledRejectionIsNotified() { | |
this._bitField = this._bitField & (~524288); | |
}; | |
Promise.prototype._isUnhandledRejectionNotified = | |
function Promise$_isUnhandledRejectionNotified() { | |
return (this._bitField & 524288) > 0; | |
}; | |
Promise.prototype._setCarriedStackTrace = | |
function Promise$_setCarriedStackTrace(capturedTrace) { | |
this._bitField = this._bitField | 1048576; | |
this._fulfillmentHandler0 = capturedTrace; | |
}; | |
Promise.prototype._unsetCarriedStackTrace = | |
function Promise$_unsetCarriedStackTrace() { | |
this._bitField = this._bitField & (~1048576); | |
this._fulfillmentHandler0 = void 0; | |
}; | |
Promise.prototype._isCarryingStackTrace = | |
function Promise$_isCarryingStackTrace() { | |
return (this._bitField & 1048576) > 0; | |
}; | |
Promise.prototype._getCarriedStackTrace = | |
function Promise$_getCarriedStackTrace() { | |
return this._isCarryingStackTrace() | |
? this._fulfillmentHandler0 | |
: void 0; | |
}; | |
Promise.prototype._receiverAt = function Promise$_receiverAt(index) { | |
var ret; | |
if (index === 0) { | |
ret = this._receiver0; | |
} | |
else { | |
ret = this[index + 4 - 5]; | |
} | |
if (this._isBound() && ret === void 0) { | |
return this._boundTo; | |
} | |
return ret; | |
}; | |
Promise.prototype._promiseAt = function Promise$_promiseAt(index) { | |
if (index === 0) return this._promise0; | |
return this[index + 3 - 5]; | |
}; | |
Promise.prototype._fulfillmentHandlerAt = | |
function Promise$_fulfillmentHandlerAt(index) { | |
if (index === 0) return this._fulfillmentHandler0; | |
return this[index + 0 - 5]; | |
}; | |
Promise.prototype._rejectionHandlerAt = | |
function Promise$_rejectionHandlerAt(index) { | |
if (index === 0) return this._rejectionHandler0; | |
return this[index + 1 - 5]; | |
}; | |
Promise.prototype._unsetAt = function Promise$_unsetAt(index) { | |
if (index === 0) { | |
this._rejectionHandler0 = | |
this._progressHandler0 = | |
this._promise0 = | |
this._receiver0 = void 0; | |
if (!this._isCarryingStackTrace()) { | |
this._fulfillmentHandler0 = void 0; | |
} | |
} | |
else { | |
this[index - 5 + 0] = | |
this[index - 5 + 1] = | |
this[index - 5 + 2] = | |
this[index - 5 + 3] = | |
this[index - 5 + 4] = void 0; | |
} | |
}; | |
Promise.prototype._resolveFromResolver = | |
function Promise$_resolveFromResolver(resolver) { | |
var promise = this; | |
this._setTrace(void 0); | |
this._pushContext(); | |
function Promise$_resolver(val) { | |
if (promise._tryFollow(val)) { | |
return; | |
} | |
promise._fulfill(val); | |
} | |
function Promise$_rejecter(val) { | |
var trace = canAttach(val) ? val : new Error(val + ""); | |
promise._attachExtraTrace(trace); | |
markAsOriginatingFromRejection(val); | |
promise._reject(val, trace === val ? void 0 : trace); | |
} | |
var r = tryCatch2(resolver, void 0, Promise$_resolver, Promise$_rejecter); | |
this._popContext(); | |
if (r !== void 0 && r === errorObj) { | |
var e = r.e; | |
var trace = canAttach(e) ? e : new Error(e + ""); | |
promise._reject(e, trace); | |
} | |
}; | |
Promise.prototype._addCallbacks = function Promise$_addCallbacks( | |
fulfill, | |
reject, | |
progress, | |
promise, | |
receiver | |
) { | |
var index = this._length(); | |
if (index >= 524287 - 5) { | |
index = 0; | |
this._setLength(0); | |
} | |
if (index === 0) { | |
this._promise0 = promise; | |
if (receiver !== void 0) this._receiver0 = receiver; | |
if (typeof fulfill === "function" && !this._isCarryingStackTrace()) | |
this._fulfillmentHandler0 = fulfill; | |
if (typeof reject === "function") this._rejectionHandler0 = reject; | |
if (typeof progress === "function") this._progressHandler0 = progress; | |
} | |
else { | |
var i = index - 5; | |
this[i + 3] = promise; | |
this[i + 4] = receiver; | |
this[i + 0] = typeof fulfill === "function" | |
? fulfill : void 0; | |
this[i + 1] = typeof reject === "function" | |
? reject : void 0; | |
this[i + 2] = typeof progress === "function" | |
? progress : void 0; | |
} | |
this._setLength(index + 5); | |
return index; | |
}; | |
Promise.prototype._setBoundTo = function Promise$_setBoundTo(obj) { | |
if (obj !== void 0) { | |
this._bitField = this._bitField | 8388608; | |
this._boundTo = obj; | |
} | |
else { | |
this._bitField = this._bitField & (~8388608); | |
} | |
}; | |
Promise.prototype._isBound = function Promise$_isBound() { | |
return (this._bitField & 8388608) === 8388608; | |
}; | |
Promise.prototype._spreadSlowCase = | |
function Promise$_spreadSlowCase(targetFn, promise, values, boundTo) { | |
var promiseForAll = | |
Promise$_CreatePromiseArray | |
(values, PromiseArray, boundTo) | |
.promise() | |
._then(function() { | |
return targetFn.apply(boundTo, arguments); | |
}, void 0, void 0, APPLY, void 0); | |
promise._follow(promiseForAll); | |
}; | |
Promise.prototype._callSpread = | |
function Promise$_callSpread(handler, promise, value, localDebugging) { | |
var boundTo = this._isBound() ? this._boundTo : void 0; | |
if (isArray(value)) { | |
for (var i = 0, len = value.length; i < len; ++i) { | |
if (isPromise(Promise._cast(value[i], void 0))) { | |
this._spreadSlowCase(handler, promise, value, boundTo); | |
return; | |
} | |
} | |
} | |
if (localDebugging) promise._pushContext(); | |
return tryCatchApply(handler, value, boundTo); | |
}; | |
Promise.prototype._callHandler = | |
function Promise$_callHandler( | |
handler, receiver, promise, value, localDebugging) { | |
var x; | |
if (receiver === APPLY && !this.isRejected()) { | |
x = this._callSpread(handler, promise, value, localDebugging); | |
} | |
else { | |
if (localDebugging) promise._pushContext(); | |
x = tryCatch1(handler, receiver, value); | |
} | |
if (localDebugging) promise._popContext(); | |
return x; | |
}; | |
Promise.prototype._settlePromiseFromHandler = | |
function Promise$_settlePromiseFromHandler( | |
handler, receiver, value, promise | |
) { | |
if (!isPromise(promise)) { | |
handler.call(receiver, value, promise); | |
return; | |
} | |
var localDebugging = debugging; | |
var x = this._callHandler(handler, receiver, | |
promise, value, localDebugging); | |
if (promise._isFollowing()) return; | |
if (x === errorObj || x === promise || x === NEXT_FILTER) { | |
var err = x === promise | |
? makeSelfResolutionError() | |
: x.e; | |
var trace = canAttach(err) ? err : new Error(err + ""); | |
if (x !== NEXT_FILTER) promise._attachExtraTrace(trace); | |
promise._rejectUnchecked(err, trace); | |
} | |
else { | |
var castValue = Promise._cast(x, promise); | |
if (isPromise(castValue)) { | |
if (castValue.isRejected() && | |
!castValue._isCarryingStackTrace() && | |
!canAttach(castValue._settledValue)) { | |
var trace = new Error(castValue._settledValue + ""); | |
promise._attachExtraTrace(trace); | |
castValue._setCarriedStackTrace(trace); | |
} | |
promise._follow(castValue); | |
if (castValue._cancellable()) { | |
promise._cancellationParent = castValue; | |
promise._setCancellable(); | |
} | |
} | |
else { | |
promise._fulfillUnchecked(x); | |
} | |
} | |
}; | |
Promise.prototype._follow = | |
function Promise$_follow(promise) { | |
this._setFollowing(); | |
if (promise.isPending()) { | |
if (promise._cancellable() ) { | |
this._cancellationParent = promise; | |
this._setCancellable(); | |
} | |
promise._proxyPromise(this); | |
} | |
else if (promise.isFulfilled()) { | |
this._fulfillUnchecked(promise._settledValue); | |
} | |
else { | |
this._rejectUnchecked(promise._settledValue, | |
promise._getCarriedStackTrace()); | |
} | |
if (promise._isRejectionUnhandled()) promise._unsetRejectionIsUnhandled(); | |
if (debugging && | |
promise._traceParent == null) { | |
promise._traceParent = this; | |
} | |
}; | |
Promise.prototype._tryFollow = | |
function Promise$_tryFollow(value) { | |
if (this._isFollowingOrFulfilledOrRejected() || | |
value === this) { | |
return false; | |
} | |
var maybePromise = Promise._cast(value, void 0); | |
if (!isPromise(maybePromise)) { | |
return false; | |
} | |
this._follow(maybePromise); | |
return true; | |
}; | |
Promise.prototype._resetTrace = function Promise$_resetTrace() { | |
if (debugging) { | |
this._trace = new CapturedTrace(this._peekContext() === void 0); | |
} | |
}; | |
Promise.prototype._setTrace = function Promise$_setTrace(parent) { | |
if (debugging) { | |
var context = this._peekContext(); | |
this._traceParent = context; | |
var isTopLevel = context === void 0; | |
if (parent !== void 0 && | |
parent._traceParent === context) { | |
this._trace = parent._trace; | |
} | |
else { | |
this._trace = new CapturedTrace(isTopLevel); | |
} | |
} | |
return this; | |
}; | |
Promise.prototype._attachExtraTrace = | |
function Promise$_attachExtraTrace(error) { | |
if (debugging) { | |
var promise = this; | |
var stack = error.stack; | |
stack = typeof stack === "string" | |
? stack.split("\n") : []; | |
var headerLineCount = 1; | |
while(promise != null && | |
promise._trace != null) { | |
stack = CapturedTrace.combine( | |
stack, | |
promise._trace.stack.split("\n") | |
); | |
promise = promise._traceParent; | |
} | |
var max = Error.stackTraceLimit + headerLineCount; | |
var len = stack.length; | |
if (len > max) { | |
stack.length = max; | |
} | |
if (stack.length <= headerLineCount) { | |
error.stack = "(No stack trace)"; | |
} | |
else { | |
error.stack = stack.join("\n"); | |
} | |
} | |
}; | |
Promise.prototype._cleanValues = function Promise$_cleanValues() { | |
if (this._cancellable()) { | |
this._cancellationParent = void 0; | |
} | |
}; | |
Promise.prototype._fulfill = function Promise$_fulfill(value) { | |
if (this._isFollowingOrFulfilledOrRejected()) return; | |
this._fulfillUnchecked(value); | |
}; | |
Promise.prototype._reject = | |
function Promise$_reject(reason, carriedStackTrace) { | |
if (this._isFollowingOrFulfilledOrRejected()) return; | |
this._rejectUnchecked(reason, carriedStackTrace); | |
}; | |
Promise.prototype._settlePromiseAt = function Promise$_settlePromiseAt(index) { | |
var handler = this.isFulfilled() | |
? this._fulfillmentHandlerAt(index) | |
: this._rejectionHandlerAt(index); | |
var value = this._settledValue; | |
var receiver = this._receiverAt(index); | |
var promise = this._promiseAt(index); | |
if (typeof handler === "function") { | |
this._settlePromiseFromHandler(handler, receiver, value, promise); | |
} | |
else { | |
var done = false; | |
var isFulfilled = this.isFulfilled(); | |
if (receiver !== void 0) { | |
if (receiver instanceof Promise && | |
receiver._isProxied()) { | |
receiver._unsetProxied(); | |
if (isFulfilled) receiver._fulfillUnchecked(value); | |
else receiver._rejectUnchecked(value, | |
this._getCarriedStackTrace()); | |
done = true; | |
} | |
else if (isPromiseArrayProxy(receiver, promise)) { | |
if (isFulfilled) receiver._promiseFulfilled(value, promise); | |
else receiver._promiseRejected(value, promise); | |
done = true; | |
} | |
} | |
if (!done) { | |
if (isFulfilled) promise._fulfill(value); | |
else promise._reject(value, this._getCarriedStackTrace()); | |
} | |
} | |
if (index >= 256) { | |
this._queueGC(); | |
} | |
}; | |
Promise.prototype._isProxied = function Promise$_isProxied() { | |
return (this._bitField & 4194304) === 4194304; | |
}; | |
Promise.prototype._setProxied = function Promise$_setProxied() { | |
this._bitField = this._bitField | 4194304; | |
}; | |
Promise.prototype._unsetProxied = function Promise$_unsetProxied() { | |
this._bitField = this._bitField & (~4194304); | |
}; | |
Promise.prototype._isGcQueued = function Promise$_isGcQueued() { | |
return (this._bitField & -1073741824) === -1073741824; | |
}; | |
Promise.prototype._setGcQueued = function Promise$_setGcQueued() { | |
this._bitField = this._bitField | -1073741824; | |
}; | |
Promise.prototype._unsetGcQueued = function Promise$_unsetGcQueued() { | |
this._bitField = this._bitField & (~-1073741824); | |
}; | |
Promise.prototype._queueGC = function Promise$_queueGC() { | |
if (this._isGcQueued()) return; | |
this._setGcQueued(); | |
async.invokeLater(this._gc, this, void 0); | |
}; | |
Promise.prototype._gc = function Promise$gc() { | |
var len = this._length(); | |
this._unsetAt(0); | |
for (var i = 0; i < len; i++) { | |
delete this[i]; | |
} | |
this._setLength(0); | |
this._unsetGcQueued(); | |
}; | |
Promise.prototype._queueSettleAt = function Promise$_queueSettleAt(index) { | |
if (this._isRejectionUnhandled()) this._unsetRejectionIsUnhandled(); | |
async.invoke(this._settlePromiseAt, this, index); | |
}; | |
Promise.prototype._fulfillUnchecked = | |
function Promise$_fulfillUnchecked(value) { | |
if (!this.isPending()) return; | |
if (value === this) { | |
var err = makeSelfResolutionError(); | |
this._attachExtraTrace(err); | |
return this._rejectUnchecked(err, void 0); | |
} | |
this._cleanValues(); | |
this._setFulfilled(); | |
this._settledValue = value; | |
var len = this._length(); | |
if (len > 0) { | |
async.invoke(this._settlePromises, this, len); | |
} | |
}; | |
Promise.prototype._rejectUncheckedCheckError = | |
function Promise$_rejectUncheckedCheckError(reason) { | |
var trace = canAttach(reason) ? reason : new Error(reason + ""); | |
this._rejectUnchecked(reason, trace === reason ? void 0 : trace); | |
}; | |
Promise.prototype._rejectUnchecked = | |
function Promise$_rejectUnchecked(reason, trace) { | |
if (!this.isPending()) return; | |
if (reason === this) { | |
var err = makeSelfResolutionError(); | |
this._attachExtraTrace(err); | |
return this._rejectUnchecked(err); | |
} | |
this._cleanValues(); | |
this._setRejected(); | |
this._settledValue = reason; | |
if (this._isFinal()) { | |
async.invokeLater(thrower, void 0, trace === void 0 ? reason : trace); | |
return; | |
} | |
var len = this._length(); | |
if (trace !== void 0) this._setCarriedStackTrace(trace); | |
if (len > 0) { | |
async.invoke(this._rejectPromises, this, null); | |
} | |
else { | |
this._ensurePossibleRejectionHandled(); | |
} | |
}; | |
Promise.prototype._rejectPromises = function Promise$_rejectPromises() { | |
this._settlePromises(); | |
this._unsetCarriedStackTrace(); | |
}; | |
Promise.prototype._settlePromises = function Promise$_settlePromises() { | |
var len = this._length(); | |
for (var i = 0; i < len; i+= 5) { | |
this._settlePromiseAt(i); | |
} | |
}; | |
Promise.prototype._ensurePossibleRejectionHandled = | |
function Promise$_ensurePossibleRejectionHandled() { | |
this._setRejectionIsUnhandled(); | |
if (CapturedTrace.possiblyUnhandledRejection !== void 0) { | |
async.invokeLater(this._notifyUnhandledRejection, this, void 0); | |
} | |
}; | |
Promise.prototype._notifyUnhandledRejectionIsHandled = | |
function Promise$_notifyUnhandledRejectionIsHandled() { | |
if (typeof unhandledRejectionHandled === "function") { | |
async.invokeLater(unhandledRejectionHandled, void 0, this); | |
} | |
}; | |
Promise.prototype._notifyUnhandledRejection = | |
function Promise$_notifyUnhandledRejection() { | |
if (this._isRejectionUnhandled()) { | |
var reason = this._settledValue; | |
var trace = this._getCarriedStackTrace(); | |
this._setUnhandledRejectionIsNotified(); | |
if (trace !== void 0) { | |
this._unsetCarriedStackTrace(); | |
reason = trace; | |
} | |
if (typeof CapturedTrace.possiblyUnhandledRejection === "function") { | |
CapturedTrace.possiblyUnhandledRejection(reason, this); | |
} | |
} | |
}; | |
var contextStack = []; | |
Promise.prototype._peekContext = function Promise$_peekContext() { | |
var lastIndex = contextStack.length - 1; | |
if (lastIndex >= 0) { | |
return contextStack[lastIndex]; | |
} | |
return void 0; | |
}; | |
Promise.prototype._pushContext = function Promise$_pushContext() { | |
if (!debugging) return; | |
contextStack.push(this); | |
}; | |
Promise.prototype._popContext = function Promise$_popContext() { | |
if (!debugging) return; | |
contextStack.pop(); | |
}; | |
function Promise$_CreatePromiseArray( | |
promises, PromiseArrayConstructor, boundTo) { | |
var list = null; | |
if (isArray(promises)) { | |
list = promises; | |
} | |
else { | |
list = Promise._cast(promises, void 0); | |
if (list !== promises) { | |
list._setBoundTo(boundTo); | |
} | |
else if (!isPromise(list)) { | |
list = null; | |
} | |
} | |
if (list !== null) { | |
return new PromiseArrayConstructor(list, boundTo); | |
} | |
return { | |
promise: function() {return apiRejection("expecting an array, a promise or a thenable");} | |
}; | |
} | |
var old = global.Promise; | |
Promise.noConflict = function() { | |
if (global.Promise === Promise) { | |
global.Promise = old; | |
} | |
return Promise; | |
}; | |
if (!CapturedTrace.isSupported()) { | |
Promise.longStackTraces = function(){}; | |
debugging = false; | |
} | |
Promise._makeSelfResolutionError = makeSelfResolutionError; | |
require("./finally.js")(Promise, NEXT_FILTER); | |
require("./direct_resolve.js")(Promise); | |
require("./thenables.js")(Promise, INTERNAL); | |
require("./synchronous_inspection.js")(Promise); | |
Promise.RangeError = RangeError; | |
Promise.CancellationError = CancellationError; | |
Promise.TimeoutError = TimeoutError; | |
Promise.TypeError = TypeError; | |
Promise.RejectionError = RejectionError; | |
util.toFastProperties(Promise); | |
util.toFastProperties(Promise.prototype); | |
require('./timers.js')(Promise,INTERNAL); | |
require('./any.js')(Promise,Promise$_CreatePromiseArray,PromiseArray); | |
require('./race.js')(Promise,INTERNAL); | |
require('./call_get.js')(Promise); | |
require('./filter.js')(Promise,Promise$_CreatePromiseArray,PromiseArray,apiRejection); | |
require('./generators.js')(Promise,apiRejection,INTERNAL); | |
require('./map.js')(Promise,PromiseArray,INTERNAL,apiRejection); | |
require('./nodeify.js')(Promise); | |
require('./promisify.js')(Promise,INTERNAL); | |
require('./props.js')(Promise,PromiseArray); | |
require('./reduce.js')(Promise,Promise$_CreatePromiseArray,PromiseArray,apiRejection,INTERNAL); | |
require('./settle.js')(Promise,Promise$_CreatePromiseArray,PromiseArray); | |
require('./some.js')(Promise,Promise$_CreatePromiseArray,PromiseArray,apiRejection); | |
require('./progress.js')(Promise,isPromiseArrayProxy); | |
require('./cancel.js')(Promise,INTERNAL); | |
Promise.prototype = Promise.prototype; | |
return Promise; | |
}; | |
}).call(this,require("/Users/nlawson/workspace-node/pouchdb-authentication/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js")) | |
},{"./any.js":4,"./async.js":5,"./call_get.js":7,"./cancel.js":8,"./captured_trace.js":9,"./catch_filter.js":10,"./direct_resolve.js":11,"./errors.js":12,"./errors_api_rejection":13,"./filter.js":15,"./finally.js":16,"./generators.js":17,"./global.js":18,"./map.js":19,"./nodeify.js":20,"./progress.js":21,"./promise_array.js":23,"./promise_resolver.js":24,"./promisify.js":26,"./props.js":28,"./race.js":30,"./reduce.js":31,"./settle.js":33,"./some.js":35,"./synchronous_inspection.js":37,"./thenables.js":38,"./timers.js":39,"./util.js":40,"/Users/nlawson/workspace-node/pouchdb-authentication/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":46}],23:[function(require,module,exports){ | |
/** | |
* Copyright (c) 2014 Petka Antonov | |
* | |
* 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:</p> | |
* | |
* 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. | |
* | |
*/ | |
"use strict"; | |
module.exports = function(Promise, INTERNAL) { | |
var canAttach = require("./errors.js").canAttach; | |
var util = require("./util.js"); | |
var async = require("./async.js"); | |
var hasOwn = {}.hasOwnProperty; | |
var isArray = util.isArray; | |
function toResolutionValue(val) { | |
switch(val) { | |
case -1: return void 0; | |
case -2: return []; | |
case -3: return {}; | |
} | |
} | |
function PromiseArray(values, boundTo) { | |
var promise = this._promise = new Promise(INTERNAL); | |
var parent = void 0; | |
if (values instanceof Promise) { | |
parent = values; | |
if (values._cancellable()) { | |
promise._setCancellable(); | |
promise._cancellationParent = values; | |
} | |
if (values._isBound()) { | |
promise._setBoundTo(boundTo); | |
} | |
} | |
promise._setTrace(parent); | |
this._values = values; | |
this._length = 0; | |
this._totalResolved = 0; | |
this._init(void 0, -2); | |
} | |
PromiseArray.PropertiesPromiseArray = function() {}; | |
PromiseArray.prototype.length = function PromiseArray$length() { | |
return this._length; | |
}; | |
PromiseArray.prototype.promise = function PromiseArray$promise() { | |
return this._promise; | |
}; | |
PromiseArray.prototype._init = | |
function PromiseArray$_init(_, resolveValueIfEmpty) { | |
var values = this._values; | |
if (values instanceof Promise) { | |
if (values.isFulfilled()) { | |
values = values._settledValue; | |
if (!isArray(values)) { | |
var err = new Promise.TypeError("expecting an array, a promise or a thenable"); | |
this.__hardReject__(err); | |
return; | |
} | |
this._values = values; | |
} | |
else if (values.isPending()) { | |
values._then( | |
this._init, | |
this._reject, | |
void 0, | |
this, | |
resolveValueIfEmpty | |
); | |
return; | |
} | |
else { | |
values._unsetRejectionIsUnhandled(); | |
this._reject(values._settledValue); | |
return; | |
} | |
} | |
if (values.length === 0) { | |
this._resolve(toResolutionValue(resolveValueIfEmpty)); | |
return; | |
} | |
var len = values.length; | |
var newLen = len; | |
var newValues; | |
if (this instanceof PromiseArray.PropertiesPromiseArray) { | |
newValues = this._values; | |
} | |
else { | |
newValues = new Array(len); | |
} | |
var isDirectScanNeeded = false; | |
for (var i = 0; i < len; ++i) { | |
var promise = values[i]; | |
if (promise === void 0 && !hasOwn.call(values, i)) { | |
newLen--; | |
continue; | |
} | |
var maybePromise = Promise._cast(promise, void 0); | |
if (maybePromise instanceof Promise) { | |
if (maybePromise.isPending()) { | |
maybePromise._proxyPromiseArray(this, i); | |
} | |
else { | |
maybePromise._unsetRejectionIsUnhandled(); | |
isDirectScanNeeded = true; | |
} | |
} | |
else { | |
isDirectScanNeeded = true; | |
} | |
newValues[i] = maybePromise; | |
} | |
if (newLen === 0) { | |
if (resolveValueIfEmpty === -2) { | |
this._resolve(newValues); | |
} | |
else { | |
this._resolve(toResolutionValue(resolveValueIfEmpty)); | |
} | |
return; | |
} | |
this._values = newValues; | |
this._length = newLen; | |
if (isDirectScanNeeded) { | |
var scanMethod = newLen === len | |
? this._scanDirectValues | |
: this._scanDirectValuesHoled; | |
async.invoke(scanMethod, this, len); | |
} | |
}; | |
PromiseArray.prototype._settlePromiseAt = | |
function PromiseArray$_settlePromiseAt(index) { | |
var value = this._values[index]; | |
if (!(value instanceof Promise)) { | |
this._promiseFulfilled(value, index); | |
} | |
else if (value.isFulfilled()) { | |
this._promiseFulfilled(value._settledValue, index); | |
} | |
else if (value.isRejected()) { | |
this._promiseRejected(value._settledValue, index); | |
} | |
}; | |
PromiseArray.prototype._scanDirectValuesHoled = | |
function PromiseArray$_scanDirectValuesHoled(len) { | |
for (var i = 0; i < len; ++i) { | |
if (this._isResolved()) { | |
break; | |
} | |
if (hasOwn.call(this._values, i)) { | |
this._settlePromiseAt(i); | |
} | |
} | |
}; | |
PromiseArray.prototype._scanDirectValues = | |
function PromiseArray$_scanDirectValues(len) { | |
for (var i = 0; i < len; ++i) { | |
if (this._isResolved()) { | |
break; | |
} | |
this._settlePromiseAt(i); | |
} | |
}; | |
PromiseArray.prototype._isResolved = function PromiseArray$_isResolved() { | |
return this._values === null; | |
}; | |
PromiseArray.prototype._resolve = function PromiseArray$_resolve(value) { | |
this._values = null; | |
this._promise._fulfill(value); | |
}; | |
PromiseArray.prototype.__hardReject__ = | |
PromiseArray.prototype._reject = function PromiseArray$_reject(reason) { | |
this._values = null; | |
var trace = canAttach(reason) ? reason : new Error(reason + ""); | |
this._promise._attachExtraTrace(trace); | |
this._promise._reject(reason, trace); | |
}; | |
PromiseArray.prototype._promiseProgressed = | |
function PromiseArray$_promiseProgressed(progressValue, index) { | |
if (this._isResolved()) return; | |
this._promise._progress({ | |
index: index, | |
value: progressValue | |
}); | |
}; | |
PromiseArray.prototype._promiseFulfilled = | |
function PromiseArray$_promiseFulfilled(value, index) { | |
if (this._isResolved()) return; | |
this._values[index] = value; | |
var totalResolved = ++this._totalResolved; | |
if (totalResolved >= this._length) { | |
this._resolve(this._values); | |
} | |
}; | |
PromiseArray.prototype._promiseRejected = | |
function PromiseArray$_promiseRejected(reason, index) { | |
if (this._isResolved()) return; | |
this._totalResolved++; | |
this._reject(reason); | |
}; | |
return PromiseArray; | |
}; | |
},{"./async.js":5,"./errors.js":12,"./util.js":40}],24:[function(require,module,exports){ | |
/** | |
* Copyright (c) 2014 Petka Antonov | |
* | |
* 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:</p> | |
* | |
* 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. | |
* | |
*/ | |
"use strict"; | |
var util = require("./util.js"); | |
var maybeWrapAsError = util.maybeWrapAsError; | |
var errors = require("./errors.js"); | |
var TimeoutError = errors.TimeoutError; | |
var RejectionError = errors.RejectionError; | |
var async = require("./async.js"); | |
var haveGetters = util.haveGetters; | |
var es5 = require("./es5.js"); | |
function isUntypedError(obj) { | |
return obj instanceof Error && | |
es5.getPrototypeOf(obj) === Error.prototype; | |
} | |
function wrapAsRejectionError(obj) { | |
var ret; | |
if (isUntypedError(obj)) { | |
ret = new RejectionError(obj); | |
} | |
else { | |
ret = obj; | |
} | |
errors.markAsOriginatingFromRejection(ret); | |
return ret; | |
} | |
function nodebackForPromise(promise) { | |
function PromiseResolver$_callback(err, value) { | |
if (promise === null) return; | |
if (err) { | |
var wrapped = wrapAsRejectionError(maybeWrapAsError(err)); | |
promise._attachExtraTrace(wrapped); | |
promise._reject(wrapped); | |
} | |
else { | |
if (arguments.length > 2) { | |
var $_len = arguments.length;var args = new Array($_len - 1); for(var $_i = 1; $_i < $_len; ++$_i) {args[$_i - 1] = arguments[$_i];} | |
promise._fulfill(args); | |
} | |
else { | |
promise._fulfill(value); | |
} | |
} | |
promise = null; | |
} | |
return PromiseResolver$_callback; | |
} | |
var PromiseResolver; | |
if (!haveGetters) { | |
PromiseResolver = function PromiseResolver(promise) { | |
this.promise = promise; | |
this.asCallback = nodebackForPromise(promise); | |
this.callback = this.asCallback; | |
}; | |
} | |
else { | |
PromiseResolver = function PromiseResolver(promise) { | |
this.promise = promise; | |
}; | |
} | |
if (haveGetters) { | |
var prop = { | |
get: function() { | |
return nodebackForPromise(this.promise); | |
} | |
}; | |
es5.defineProperty(PromiseResolver.prototype, "asCallback", prop); | |
es5.defineProperty(PromiseResolver.prototype, "callback", prop); | |
} | |
PromiseResolver._nodebackForPromise = nodebackForPromise; | |
PromiseResolver.prototype.toString = function PromiseResolver$toString() { | |
return "[object PromiseResolver]"; | |
}; | |
PromiseResolver.prototype.resolve = | |
PromiseResolver.prototype.fulfill = function PromiseResolver$resolve(value) { | |
var promise = this.promise; | |
if ((promise === void 0) || (promise._tryFollow === void 0)) { | |
throw new TypeError("Illegal invocation, resolver resolve/reject must be called within a resolver context. Consider using the promise constructor instead."); | |
} | |
if (promise._tryFollow(value)) { | |
return; | |
} | |
async.invoke(promise._fulfill, promise, value); | |
}; | |
PromiseResolver.prototype.reject = function PromiseResolver$reject(reason) { | |
var promise = this.promise; | |
if ((promise === void 0) || (promise._attachExtraTrace === void 0)) { | |
throw new TypeError("Illegal invocation, resolver resolve/reject must be called within a resolver context. Consider using the promise constructor instead."); | |
} | |
errors.markAsOriginatingFromRejection(reason); | |
var trace = errors.canAttach(reason) ? reason : new Error(reason + ""); | |
promise._attachExtraTrace(trace); | |
async.invoke(promise._reject, promise, reason); | |
if (trace !== reason) { | |
async.invoke(this._setCarriedStackTrace, this, trace); | |
} | |
}; | |
PromiseResolver.prototype.progress = | |
function PromiseResolver$progress(value) { | |
async.invoke(this.promise._progress, this.promise, value); | |
}; | |
PromiseResolver.prototype.cancel = function PromiseResolver$cancel() { | |
async.invoke(this.promise.cancel, this.promise, void 0); | |
}; | |
PromiseResolver.prototype.timeout = function PromiseResolver$timeout() { | |
this.reject(new TimeoutError("timeout")); | |
}; | |
PromiseResolver.prototype.isResolved = function PromiseResolver$isResolved() { | |
return this.promise.isResolved(); | |
}; | |
PromiseResolver.prototype.toJSON = function PromiseResolver$toJSON() { | |
return this.promise.toJSON(); | |
}; | |
PromiseResolver.prototype._setCarriedStackTrace = | |
function PromiseResolver$_setCarriedStackTrace(trace) { | |
if (this.promise.isRejected()) { | |
this.promise._setCarriedStackTrace(trace); | |
} | |
}; | |
module.exports = PromiseResolver; | |
},{"./async.js":5,"./errors.js":12,"./es5.js":14,"./util.js":40}],25:[function(require,module,exports){ | |
/** | |
* Copyright (c) 2014 Petka Antonov | |
* | |
* 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:</p> | |
* | |
* 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. | |
* | |
*/ | |
"use strict"; | |
module.exports = function(Promise, INTERNAL) { | |
var errors = require("./errors.js"); | |
var TypeError = errors.TypeError; | |
var util = require("./util.js"); | |
var isArray = util.isArray; | |
var errorObj = util.errorObj; | |
var tryCatch1 = util.tryCatch1; | |
var yieldHandlers = []; | |
function promiseFromYieldHandler(value) { | |
var _yieldHandlers = yieldHandlers; | |
var _errorObj = errorObj; | |
var _Promise = Promise; | |
var len = _yieldHandlers.length; | |
for (var i = 0; i < len; ++i) { | |
var result = tryCatch1(_yieldHandlers[i], void 0, value); | |
if (result === _errorObj) { | |
return _Promise.reject(_errorObj.e); | |
} | |
var maybePromise = _Promise._cast(result, | |
promiseFromYieldHandler, void 0); | |
if (maybePromise instanceof _Promise) return maybePromise; | |
} | |
return null; | |
} | |
function PromiseSpawn(generatorFunction, receiver) { | |
var promise = this._promise = new Promise(INTERNAL); | |
promise._setTrace(void 0); | |
this._generatorFunction = generatorFunction; | |
this._receiver = receiver; | |
this._generator = void 0; | |
} | |
PromiseSpawn.prototype.promise = function PromiseSpawn$promise() { | |
return this._promise; | |
}; | |
PromiseSpawn.prototype._run = function PromiseSpawn$_run() { | |
this._generator = this._generatorFunction.call(this._receiver); | |
this._receiver = | |
this._generatorFunction = void 0; | |
this._next(void 0); | |
}; | |
PromiseSpawn.prototype._continue = function PromiseSpawn$_continue(result) { | |
if (result === errorObj) { | |
this._generator = void 0; | |
var trace = errors.canAttach(result.e) | |
? result.e : new Error(result.e + ""); | |
this._promise._attachExtraTrace(trace); | |
this._promise._reject(result.e, trace); | |
return; | |
} | |
var value = result.value; | |
if (result.done === true) { | |
this._generator = void 0; | |
if (!this._promise._tryFollow(value)) { | |
this._promise._fulfill(value); | |
} | |
} | |
else { | |
var maybePromise = Promise._cast(value, PromiseSpawn$_continue, void 0); | |
if (!(maybePromise instanceof Promise)) { | |
if (isArray(maybePromise)) { | |
maybePromise = Promise.all(maybePromise); | |
} | |
else { | |
maybePromise = promiseFromYieldHandler(maybePromise); | |
} | |
if (maybePromise === null) { | |
this._throw(new TypeError("A value was yielded that could not be treated as a promise")); | |
return; | |
} | |
} | |
maybePromise._then( | |
this._next, | |
this._throw, | |
void 0, | |
this, | |
null | |
); | |
} | |
}; | |
PromiseSpawn.prototype._throw = function PromiseSpawn$_throw(reason) { | |
if (errors.canAttach(reason)) | |
this._promise._attachExtraTrace(reason); | |
this._continue( | |
tryCatch1(this._generator["throw"], this._generator, reason) | |
); | |
}; | |
PromiseSpawn.prototype._next = function PromiseSpawn$_next(value) { | |
this._continue( | |
tryCatch1(this._generator.next, this._generator, value) | |
); | |
}; | |
PromiseSpawn.addYieldHandler = function PromiseSpawn$AddYieldHandler(fn) { | |
if (typeof fn !== "function") throw new TypeError("fn must be a function"); | |
yieldHandlers.push(fn); | |
}; | |
return PromiseSpawn; | |
}; | |
},{"./errors.js":12,"./util.js":40}],26:[function(require,module,exports){ | |
/** | |
* Copyright (c) 2014 Petka Antonov | |
* | |
* 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:</p> | |
* | |
* 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. | |
* | |
*/ | |
"use strict"; | |
module.exports = function(Promise, INTERNAL) { | |
var THIS = {}; | |
var util = require("./util.js"); | |
var es5 = require("./es5.js"); | |
var nodebackForPromise = require("./promise_resolver.js") | |
._nodebackForPromise; | |
var withAppended = util.withAppended; | |
var maybeWrapAsError = util.maybeWrapAsError; | |
var canEvaluate = util.canEvaluate; | |
var deprecated = util.deprecated; | |
var TypeError = require("./errors").TypeError; | |
var rasyncSuffix = new RegExp("Async" + "$"); | |
function isPromisified(fn) { | |
return fn.__isPromisified__ === true; | |
} | |
function hasPromisified(obj, key) { | |
var containsKey = ((key + "Async") in obj); | |
return containsKey ? isPromisified(obj[key + "Async"]) | |
: false; | |
} | |
function checkValid(ret) { | |
for (var i = 0; i < ret.length; i += 2) { | |
var key = ret[i]; | |
if (rasyncSuffix.test(key)) { | |
var keyWithoutAsyncSuffix = key.replace(rasyncSuffix, ""); | |
for (var j = 0; j < ret.length; j += 2) { | |
if (ret[j] === keyWithoutAsyncSuffix) { | |
throw new TypeError("Cannot promisify an API " + | |
"that has normal methods with Async-suffix"); | |
} | |
} | |
} | |
} | |
} | |
var inheritedMethods = (function() { | |
if (es5.isES5) { | |
var create = Object.create; | |
var getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; | |
return function(cur) { | |
var ret = []; | |
var visitedKeys = create(null); | |
var original = cur; | |
while (cur !== null) { | |
var keys = es5.keys(cur); | |
for (var i = 0, len = keys.length; i < len; ++i) { | |
var key = keys[i]; | |
if (visitedKeys[key]) continue; | |
visitedKeys[key] = true; | |
var desc = getOwnPropertyDescriptor(cur, key); | |
if (desc != null && | |
typeof desc.value === "function" && | |
!isPromisified(desc.value) && | |
!hasPromisified(original, key)) { | |
ret.push(key, desc.value); | |
} | |
} | |
cur = es5.getPrototypeOf(cur); | |
} | |
checkValid(ret); | |
return ret; | |
}; | |
} | |
else { | |
return function(obj) { | |
var ret = []; | |
/*jshint forin:false */ | |
for (var key in obj) { | |
var fn = obj[key]; | |
if (typeof fn === "function" && | |
!isPromisified(fn) && | |
!hasPromisified(obj, key)) { | |
ret.push(key, fn); | |
} | |
} | |
checkValid(ret); | |
return ret; | |
}; | |
} | |
})(); | |
function switchCaseArgumentOrder(likelyArgumentCount) { | |
var ret = [likelyArgumentCount]; | |
var min = Math.max(0, likelyArgumentCount - 1 - 5); | |
for(var i = likelyArgumentCount - 1; i >= min; --i) { | |
if (i === likelyArgumentCount) continue; | |
ret.push(i); | |
} | |
for(var i = likelyArgumentCount + 1; i <= 5; ++i) { | |
ret.push(i); | |
} | |
return ret; | |
} | |
function parameterDeclaration(parameterCount) { | |
var ret = new Array(parameterCount); | |
for(var i = 0; i < ret.length; ++i) { | |
ret[i] = "_arg" + i; | |
} | |
return ret.join(", "); | |
} | |
function parameterCount(fn) { | |
if (typeof fn.length === "number") { | |
return Math.max(Math.min(fn.length, 1023 + 1), 0); | |
} | |
return 0; | |
} | |
var rident = /^[a-z$_][a-z$_0-9]*$/i; | |
function propertyAccess(id) { | |
if (rident.test(id)) { | |
return "." + id; | |
} | |
else return "['" + id.replace(/(['\\])/g, "\\$1") + "']"; | |
} | |
function makeNodePromisifiedEval(callback, receiver, originalName, fn) { | |
var newParameterCount = Math.max(0, parameterCount(fn) - 1); | |
var argumentOrder = switchCaseArgumentOrder(newParameterCount); | |
var callbackName = (typeof originalName === "string" ? | |
originalName + "Async" : | |
"promisified"); | |
function generateCallForArgumentCount(count) { | |
var args = new Array(count); | |
for (var i = 0, len = args.length; i < len; ++i) { | |
args[i] = "arguments[" + i + "]"; | |
} | |
var comma = count > 0 ? "," : ""; | |
if (typeof callback === "string" && | |
receiver === THIS) { | |
return "this" + propertyAccess(callback) + "("+args.join(",") + | |
comma +" fn);"+ | |
"break;"; | |
} | |
return (receiver === void 0 | |
? "callback("+args.join(",")+ comma +" fn);" | |
: "callback.call("+(receiver === THIS | |
? "this" | |
: "receiver")+", "+args.join(",") + comma + " fn);") + | |
"break;"; | |
} | |
if (!rident.test(callbackName)) { | |
callbackName = "promisified"; | |
} | |
function generateArgumentSwitchCase() { | |
var ret = ""; | |
for(var i = 0; i < argumentOrder.length; ++i) { | |
ret += "case " + argumentOrder[i] +":" + | |
generateCallForArgumentCount(argumentOrder[i]); | |
} | |
ret += "default: var args = new Array(len + 1);" + | |
"var i = 0;" + | |
"for (var i = 0; i < len; ++i) { " + | |
" args[i] = arguments[i];" + | |
"}" + | |
"args[i] = fn;" + | |
(typeof callback === "string" | |
? "this" + propertyAccess(callback) + ".apply(" | |
: "callback.apply(") + | |
(receiver === THIS ? "this" : "receiver") + | |
", args); break;"; | |
return ret; | |
} | |
return new Function("Promise", "callback", "receiver", | |
"withAppended", "maybeWrapAsError", "nodebackForPromise", | |
"INTERNAL", | |
"var ret = function " + callbackName + | |
"(" + parameterDeclaration(newParameterCount) + ") {\"use strict\";" + | |
"var len = arguments.length;" + | |
"var promise = new Promise(INTERNAL);"+ | |
"promise._setTrace(void 0);" + | |
"var fn = nodebackForPromise(promise);"+ | |
"try {" + | |
"switch(len) {" + | |
generateArgumentSwitchCase() + | |
"}" + | |
"}" + | |
"catch(e){ " + | |
"var wrapped = maybeWrapAsError(e);" + | |
"promise._attachExtraTrace(wrapped);" + | |
"promise._reject(wrapped);" + | |
"}" + | |
"return promise;" + | |
"" + | |
"}; ret.__isPromisified__ = true; return ret;" | |
)(Promise, callback, receiver, withAppended, | |
maybeWrapAsError, nodebackForPromise, INTERNAL); | |
} | |
function makeNodePromisifiedClosure(callback, receiver) { | |
function promisified() { | |
var _receiver = receiver; | |
if (receiver === THIS) _receiver = this; | |
if (typeof callback === "string") { | |
callback = _receiver[callback]; | |
} | |
var promise = new Promise(INTERNAL); | |
promise._setTrace(void 0); | |
var fn = nodebackForPromise(promise); | |
try { | |
callback.apply(_receiver, withAppended(arguments, fn)); | |
} | |
catch(e) { | |
var wrapped = maybeWrapAsError(e); | |
promise._attachExtraTrace(wrapped); | |
promise._reject(wrapped); | |
} | |
return promise; | |
} | |
promisified.__isPromisified__ = true; | |
return promisified; | |
} | |
var makeNodePromisified = canEvaluate | |
? makeNodePromisifiedEval | |
: makeNodePromisifiedClosure; | |
function _promisify(callback, receiver, isAll) { | |
if (isAll) { | |
var methods = inheritedMethods(callback); | |
for (var i = 0, len = methods.length; i < len; i+= 2) { | |
var key = methods[i]; | |
var fn = methods[i+1]; | |
var promisifiedKey = key + "Async"; | |
callback[promisifiedKey] = makeNodePromisified(key, THIS, key, fn); | |
} | |
util.toFastProperties(callback); | |
return callback; | |
} | |
else { | |
return makeNodePromisified(callback, receiver, void 0, callback); | |
} | |
} | |
Promise.promisify = function Promise$Promisify(fn, receiver) { | |
if (typeof fn === "object" && fn !== null) { | |
deprecated("Promise.promisify for promisifying entire objects is deprecated. Use Promise.promisifyAll instead."); | |
return _promisify(fn, receiver, true); | |
} | |
if (typeof fn !== "function") { | |
throw new TypeError("fn must be a function"); | |
} | |
if (isPromisified(fn)) { | |
return fn; | |
} | |
return _promisify( | |
fn, | |
arguments.length < 2 ? THIS : receiver, | |
false); | |
}; | |
Promise.promisifyAll = function Promise$PromisifyAll(target) { | |
if (typeof target !== "function" && typeof target !== "object") { | |
throw new TypeError("the target of promisifyAll must be an object or a function"); | |
} | |
return _promisify(target, void 0, true); | |
}; | |
}; | |
},{"./errors":12,"./es5.js":14,"./promise_resolver.js":24,"./util.js":40}],27:[function(require,module,exports){ | |
/** | |
* Copyright (c) 2014 Petka Antonov | |
* | |
* 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:</p> | |
* | |
* 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. | |
* | |
*/ | |
"use strict"; | |
module.exports = function(Promise, PromiseArray) { | |
var util = require("./util.js"); | |
var inherits = util.inherits; | |
var es5 = require("./es5.js"); | |
function PropertiesPromiseArray(obj, boundTo) { | |
var keys = es5.keys(obj); | |
var values = new Array(keys.length); | |
for (var i = 0, len = values.length; i < len; ++i) { | |
values[i] = obj[keys[i]]; | |
} | |
this.constructor$(values, boundTo); | |
if (!this._isResolved()) { | |
for (var i = 0, len = keys.length; i < len; ++i) { | |
values.push(keys[i]); | |
} | |
} | |
} | |
inherits(PropertiesPromiseArray, PromiseArray); | |
PropertiesPromiseArray.prototype._init = | |
function PropertiesPromiseArray$_init() { | |
this._init$(void 0, -3) ; | |
}; | |
PropertiesPromiseArray.prototype._promiseFulfilled = | |
function PropertiesPromiseArray$_promiseFulfilled(value, index) { | |
if (this._isResolved()) return; | |
this._values[index] = value; | |
var totalResolved = ++this._totalResolved; | |
if (totalResolved >= this._length) { | |
var val = {}; | |
var keyOffset = this.length(); | |
for (var i = 0, len = this.length(); i < len; ++i) { | |
val[this._values[i + keyOffset]] = this._values[i]; | |
} | |
this._resolve(val); | |
} | |
}; | |
PropertiesPromiseArray.prototype._promiseProgressed = | |
function PropertiesPromiseArray$_promiseProgressed(value, index) { | |
if (this._isResolved()) return; | |
this._promise._progress({ | |
key: this._values[index + this.length()], | |
value: value | |
}); | |
}; | |
PromiseArray.PropertiesPromiseArray = PropertiesPromiseArray; | |
return PropertiesPromiseArray; | |
}; | |
},{"./es5.js":14,"./util.js":40}],28:[function(require,module,exports){ | |
/** | |
* Copyright (c) 2014 Petka Antonov | |
* | |
* 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:</p> | |
* | |
* 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. | |
* | |
*/ | |
"use strict"; | |
module.exports = function(Promise, PromiseArray) { | |
var PropertiesPromiseArray = require("./properties_promise_array.js")( | |
Promise, PromiseArray); | |
var util = require("./util.js"); | |
var apiRejection = require("./errors_api_rejection")(Promise); | |
var isObject = util.isObject; | |
function Promise$_Props(promises, useBound) { | |
var ret; | |
var castValue = Promise._cast(promises, void 0); | |
if (!isObject(castValue)) { | |
return apiRejection("cannot await properties of a non-object"); | |
} | |
else if (castValue instanceof Promise) { | |
ret = castValue._then(Promise.props, void 0, void 0, | |
void 0, void 0); | |
} | |
else { | |
ret = new PropertiesPromiseArray( | |
castValue, | |
useBound === true && castValue._isBound() | |
? castValue._boundTo | |
: void 0 | |
).promise(); | |
useBound = false; | |
} | |
if (useBound === true && castValue._isBound()) { | |
ret._setBoundTo(castValue._boundTo); | |
} | |
return ret; | |
} | |
Promise.prototype.props = function Promise$props() { | |
return Promise$_Props(this, true); | |
}; | |
Promise.props = function Promise$Props(promises) { | |
return Promise$_Props(promises, false); | |
}; | |
}; | |
},{"./errors_api_rejection":13,"./properties_promise_array.js":27,"./util.js":40}],29:[function(require,module,exports){ | |
/** | |
* Copyright (c) 2014 Petka Antonov | |
* | |
* 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:</p> | |
* | |
* 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. | |
* | |
*/ | |
"use strict"; | |
function arrayCopy(src, srcIndex, dst, dstIndex, len) { | |
for (var j = 0; j < len; ++j) { | |
dst[j + dstIndex] = src[j + srcIndex]; | |
} | |
} | |
function pow2AtLeast(n) { | |
n = n >>> 0; | |
n = n - 1; | |
n = n | (n >> 1); | |
n = n | (n >> 2); | |
n = n | (n >> 4); | |
n = n | (n >> 8); | |
n = n | (n >> 16); | |
return n + 1; | |
} | |
function getCapacity(capacity) { | |
if (typeof capacity !== "number") return 16; | |
return pow2AtLeast( | |
Math.min( | |
Math.max(16, capacity), 1073741824) | |
); | |
} | |
function Queue(capacity) { | |
this._capacity = getCapacity(capacity); | |
this._length = 0; | |
this._front = 0; | |
this._makeCapacity(); | |
} | |
Queue.prototype._willBeOverCapacity = | |
function Queue$_willBeOverCapacity(size) { | |
return this._capacity < size; | |
}; | |
Queue.prototype._pushOne = function Queue$_pushOne(arg) { | |
var length = this.length(); | |
this._checkCapacity(length + 1); | |
var i = (this._front + length) & (this._capacity - 1); | |
this[i] = arg; | |
this._length = length + 1; | |
}; | |
Queue.prototype.push = function Queue$push(fn, receiver, arg) { | |
var length = this.length() + 3; | |
if (this._willBeOverCapacity(length)) { | |
this._pushOne(fn); | |
this._pushOne(receiver); | |
this._pushOne(arg); | |
return; | |
} | |
var j = this._front + length - 3; | |
this._checkCapacity(length); | |
var wrapMask = this._capacity - 1; | |
this[(j + 0) & wrapMask] = fn; | |
this[(j + 1) & wrapMask] = receiver; | |
this[(j + 2) & wrapMask] = arg; | |
this._length = length; | |
}; | |
Queue.prototype.shift = function Queue$shift() { | |
var front = this._front, | |
ret = this[front]; | |
this[front] = void 0; | |
this._front = (front + 1) & (this._capacity - 1); | |
this._length--; | |
return ret; | |
}; | |
Queue.prototype.length = function Queue$length() { | |
return this._length; | |
}; | |
Queue.prototype._makeCapacity = function Queue$_makeCapacity() { | |
var len = this._capacity; | |
for (var i = 0; i < len; ++i) { | |
this[i] = void 0; | |
} | |
}; | |
Queue.prototype._checkCapacity = function Queue$_checkCapacity(size) { | |
if (this._capacity < size) { | |
this._resizeTo(this._capacity << 3); | |
} | |
}; | |
Queue.prototype._resizeTo = function Queue$_resizeTo(capacity) { | |
var oldFront = this._front; | |
var oldCapacity = this._capacity; | |
var oldQueue = new Array(oldCapacity); | |
var length = this.length(); | |
arrayCopy(this, 0, oldQueue, 0, oldCapacity); | |
this._capacity = capacity; | |
this._makeCapacity(); | |
this._front = 0; | |
if (oldFront + length <= oldCapacity) { | |
arrayCopy(oldQueue, oldFront, this, 0, length); | |
} | |
else { var lengthBeforeWrapping = | |
length - ((oldFront + length) & (oldCapacity - 1)); | |
arrayCopy(oldQueue, oldFront, this, 0, lengthBeforeWrapping); | |
arrayCopy(oldQueue, 0, this, lengthBeforeWrapping, | |
length - lengthBeforeWrapping); | |
} | |
}; | |
module.exports = Queue; | |
},{}],30:[function(require,module,exports){ | |
/** | |
* Copyright (c) 2014 Petka Antonov | |
* | |
* 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:</p> | |
* | |
* 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. | |
* | |
*/ | |
"use strict"; | |
module.exports = function(Promise, INTERNAL) { | |
var apiRejection = require("./errors_api_rejection.js")(Promise); | |
var isArray = require("./util.js").isArray; | |
var raceLater = function Promise$_raceLater(promise) { | |
return promise.then(function(array) { | |
return Promise$_Race(array, promise); | |
}); | |
}; | |
var hasOwn = {}.hasOwnProperty; | |
function Promise$_Race(promises, parent) { | |
var maybePromise = Promise._cast(promises, void 0); | |
if (maybePromise instanceof Promise) { | |
return raceLater(maybePromise); | |
} | |
else if (!isArray(promises)) { | |
return apiRejection("expecting an array, a promise or a thenable"); | |
} | |
var ret = new Promise(INTERNAL); | |
ret._setTrace(parent); | |
if (parent !== void 0) { | |
if (parent._isBound()) { | |
ret._setBoundTo(parent._boundTo); | |
} | |
if (parent._cancellable()) { | |
ret._setCancellable(); | |
ret._cancellationParent = parent; | |
} | |
} | |
var fulfill = ret._fulfill; | |
var reject = ret._reject; | |
for (var i = 0, len = promises.length; i < len; ++i) { | |
var val = promises[i]; | |
if (val === void 0 && !(hasOwn.call(promises, i))) { | |
continue; | |
} | |
Promise.cast(val)._then( | |
fulfill, | |
reject, | |
void 0, | |
ret, | |
null | |
); | |
} | |
return ret; | |
} | |
Promise.race = function Promise$Race(promises) { | |
return Promise$_Race(promises, void 0); | |
}; | |
Promise.prototype.race = function Promise$race() { | |
return Promise$_Race(this, void 0); | |
}; | |
}; | |
},{"./errors_api_rejection.js":13,"./util.js":40}],31:[function(require,module,exports){ | |
/** | |
* Copyright (c) 2014 Petka Antonov | |
* | |
* 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:</p> | |
* | |
* 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. | |
* | |
*/ | |
"use strict"; | |
module.exports = function( | |
Promise, Promise$_CreatePromiseArray, | |
PromiseArray, apiRejection, INTERNAL) { | |
function Reduction(callback, index, accum, items, receiver) { | |
this.promise = new Promise(INTERNAL); | |
this.index = index; | |
this.length = items.length; | |
this.items = items; | |
this.callback = callback; | |
this.receiver = receiver; | |
this.accum = accum; | |
} | |
Reduction.prototype.reject = function Reduction$reject(e) { | |
this.promise._reject(e); | |
}; | |
Reduction.prototype.fulfill = function Reduction$fulfill(value, index) { | |
this.accum = value; | |
this.index = index + 1; | |
this.iterate(); | |
}; | |
Reduction.prototype.iterate = function Reduction$iterate() { | |
var i = this.index; | |
var len = this.length; | |
var items = this.items; | |
var result = this.accum; | |
var receiver = this.receiver; | |
var callback = this.callback; | |
for (; i < len; ++i) { | |
result = callback.call(receiver, result, items[i], i, len); | |
result = Promise._cast(result, void 0); | |
if (result instanceof Promise) { | |
result._then( | |
this.fulfill, this.reject, void 0, this, i); | |
return; | |
} | |
} | |
this.promise._fulfill(result); | |
}; | |
function Promise$_reducer(fulfilleds, initialValue) { | |
var fn = this; | |
var receiver = void 0; | |
if (typeof fn !== "function") { | |
receiver = fn.receiver; | |
fn = fn.fn; | |
} | |
var len = fulfilleds.length; | |
var accum = void 0; | |
var startIndex = 0; | |
if (initialValue !== void 0) { | |
accum = initialValue; | |
startIndex = 0; | |
} | |
else { | |
startIndex = 1; | |
if (len > 0) accum = fulfilleds[0]; | |
} | |
var i = startIndex; | |
if (i >= len) { | |
return accum; | |
} | |
var reduction = new Reduction(fn, i, accum, fulfilleds, receiver); | |
reduction.iterate(); | |
return reduction.promise; | |
} | |
function Promise$_unpackReducer(fulfilleds) { | |
var fn = this.fn; | |
var initialValue = this.initialValue; | |
return Promise$_reducer.call(fn, fulfilleds, initialValue); | |
} | |
function Promise$_slowReduce( | |
promises, fn, initialValue, useBound) { | |
return initialValue._then(function(initialValue) { | |
return Promise$_Reduce( | |
promises, fn, initialValue, useBound); | |
}, void 0, void 0, void 0, void 0); | |
} | |
function Promise$_Reduce(promises, fn, initialValue, useBound) { | |
if (typeof fn !== "function") { | |
return apiRejection("fn must be a function"); | |
} | |
if (useBound === true && promises._isBound()) { | |
fn = { | |
fn: fn, | |
receiver: promises._boundTo | |
}; | |
} | |
if (initialValue !== void 0) { | |
if (initialValue instanceof Promise) { | |
if (initialValue.isFulfilled()) { | |
initialValue = initialValue._settledValue; | |
} | |
else { | |
return Promise$_slowReduce(promises, | |
fn, initialValue, useBound); | |
} | |
} | |
return Promise$_CreatePromiseArray(promises, PromiseArray, | |
useBound === true && promises._isBound() | |
? promises._boundTo | |
: void 0) | |
.promise() | |
._then(Promise$_unpackReducer, void 0, void 0, { | |
fn: fn, | |
initialValue: initialValue | |
}, void 0); | |
} | |
return Promise$_CreatePromiseArray(promises, PromiseArray, | |
useBound === true && promises._isBound() | |
? promises._boundTo | |
: void 0).promise() | |
._then(Promise$_reducer, void 0, void 0, fn, void 0); | |
} | |
Promise.reduce = function Promise$Reduce(promises, fn, initialValue) { | |
return Promise$_Reduce(promises, fn, initialValue, false); | |
}; | |
Promise.prototype.reduce = function Promise$reduce(fn, initialValue) { | |
return Promise$_Reduce(this, fn, initialValue, true); | |
}; | |
}; | |
},{}],32:[function(require,module,exports){ | |
(function (process){ | |
/** | |
* Copyright (c) 2014 Petka Antonov | |
* | |
* 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:</p> | |
* | |
* 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. | |
* | |
*/ | |
"use strict"; | |
var global = require("./global.js"); | |
var schedule; | |
if (typeof process !== "undefined" && process !== null && | |
typeof process.cwd === "function" && | |
typeof process.nextTick === "function" && | |
typeof process.version === "string") { | |
schedule = function Promise$_Scheduler(fn) { | |
process.nextTick(fn); | |
}; | |
} | |
else if ((typeof global.MutationObserver === "function" || | |
typeof global.WebkitMutationObserver === "function" || | |
typeof global.WebKitMutationObserver === "function") && | |
typeof document !== "undefined" && | |
typeof document.createElement === "function") { | |
schedule = (function(){ | |
var MutationObserver = global.MutationObserver || | |
global.WebkitMutationObserver || | |
global.WebKitMutationObserver; | |
var div = document.createElement("div"); | |
var queuedFn = void 0; | |
var observer = new MutationObserver( | |
function Promise$_Scheduler() { | |
var fn = queuedFn; | |
queuedFn = void 0; | |
fn(); | |
} | |
); | |
observer.observe(div, { | |
attributes: true | |
}); | |
return function Promise$_Scheduler(fn) { | |
queuedFn = fn; | |
div.setAttribute("class", "foo"); | |
}; | |
})(); | |
} | |
else if (typeof global.postMessage === "function" && | |
typeof global.importScripts !== "function" && | |
typeof global.addEventListener === "function" && | |
typeof global.removeEventListener === "function") { | |
var MESSAGE_KEY = "bluebird_message_key_" + Math.random(); | |
schedule = (function(){ | |
var queuedFn = void 0; | |
function Promise$_Scheduler(e) { | |
if (e.source === global && | |
e.data === MESSAGE_KEY) { | |
var fn = queuedFn; | |
queuedFn = void 0; | |
fn(); | |
} | |
} | |
global.addEventListener("message", Promise$_Scheduler, false); | |
return function Promise$_Scheduler(fn) { | |
queuedFn = fn; | |
global.postMessage( | |
MESSAGE_KEY, "*" | |
); | |
}; | |
})(); | |
} | |
else if (typeof global.MessageChannel === "function") { | |
schedule = (function(){ | |
var queuedFn = void 0; | |
var channel = new global.MessageChannel(); | |
channel.port1.onmessage = function Promise$_Scheduler() { | |
var fn = queuedFn; | |
queuedFn = void 0; | |
fn(); | |
}; | |
return function Promise$_Scheduler(fn) { | |
queuedFn = fn; | |
channel.port2.postMessage(null); | |
}; | |
})(); | |
} | |
else if (global.setTimeout) { | |
schedule = function Promise$_Scheduler(fn) { | |
setTimeout(fn, 4); | |
}; | |
} | |
else { | |
schedule = function Promise$_Scheduler(fn) { | |
fn(); | |
}; | |
} | |
module.exports = schedule; | |
}).call(this,require("/Users/nlawson/workspace-node/pouchdb-authentication/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js")) | |
},{"./global.js":18,"/Users/nlawson/workspace-node/pouchdb-authentication/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":46}],33:[function(require,module,exports){ | |
/** | |
* Copyright (c) 2014 Petka Antonov | |
* | |
* 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:</p> | |
* | |
* 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. | |
* | |
*/ | |
"use strict"; | |
module.exports = | |
function(Promise, Promise$_CreatePromiseArray, PromiseArray) { | |
var SettledPromiseArray = require("./settled_promise_array.js")( | |
Promise, PromiseArray); | |
function Promise$_Settle(promises, useBound) { | |
return Promise$_CreatePromiseArray( | |
promises, | |
SettledPromiseArray, | |
useBound === true && promises._isBound() | |
? promises._boundTo | |
: void 0 | |
).promise(); | |
} | |
Promise.settle = function Promise$Settle(promises) { | |
return Promise$_Settle(promises, false); | |
}; | |
Promise.prototype.settle = function Promise$settle() { | |
return Promise$_Settle(this, true); | |
}; | |
}; | |
},{"./settled_promise_array.js":34}],34:[function(require,module,exports){ | |
/** | |
* Copyright (c) 2014 Petka Antonov | |
* | |
* 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:</p> | |
* | |
* 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. | |
* | |
*/ | |
"use strict"; | |
module.exports = function(Promise, PromiseArray) { | |
var PromiseInspection = Promise.PromiseInspection; | |
var util = require("./util.js"); | |
var inherits = util.inherits; | |
function SettledPromiseArray(values, boundTo) { | |
this.constructor$(values, boundTo); | |
} | |
inherits(SettledPromiseArray, PromiseArray); | |
SettledPromiseArray.prototype._promiseResolved = | |
function SettledPromiseArray$_promiseResolved(index, inspection) { | |
this._values[index] = inspection; | |
var totalResolved = ++this._totalResolved; | |
if (totalResolved >= this._length) { | |
this._resolve(this._values); | |
} | |
}; | |
SettledPromiseArray.prototype._promiseFulfilled = | |
function SettledPromiseArray$_promiseFulfilled(value, index) { | |
if (this._isResolved()) return; | |
var ret = new PromiseInspection(); | |
ret._bitField = 268435456; | |
ret._settledValue = value; | |
this._promiseResolved(index, ret); | |
}; | |
SettledPromiseArray.prototype._promiseRejected = | |
function SettledPromiseArray$_promiseRejected(reason, index) { | |
if (this._isResolved()) return; | |
var ret = new PromiseInspection(); | |
ret._bitField = 134217728; | |
ret._settledValue = reason; | |
this._promiseResolved(index, ret); | |
}; | |
return SettledPromiseArray; | |
}; | |
},{"./util.js":40}],35:[function(require,module,exports){ | |
/** | |
* Copyright (c) 2014 Petka Antonov | |
* | |
* 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:</p> | |
* | |
* 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. | |
* | |
*/ | |
"use strict"; | |
module.exports = | |
function(Promise, Promise$_CreatePromiseArray, PromiseArray, apiRejection) { | |
var SomePromiseArray = require("./some_promise_array.js")(PromiseArray); | |
function Promise$_Some(promises, howMany, useBound) { | |
if ((howMany | 0) !== howMany || howMany < 0) { | |
return apiRejection("expecting a positive integer"); | |
} | |
var ret = Promise$_CreatePromiseArray( | |
promises, | |
SomePromiseArray, | |
useBound === true && promises._isBound() | |
? promises._boundTo | |
: void 0 | |
); | |
var promise = ret.promise(); | |
if (promise.isRejected()) { | |
return promise; | |
} | |
ret.setHowMany(howMany); | |
ret.init(); | |
return promise; | |
} | |
Promise.some = function Promise$Some(promises, howMany) { | |
return Promise$_Some(promises, howMany, false); | |
}; | |
Promise.prototype.some = function Promise$some(count) { | |
return Promise$_Some(this, count, true); | |
}; | |
}; | |
},{"./some_promise_array.js":36}],36:[function(require,module,exports){ | |
/** | |
* Copyright (c) 2014 Petka Antonov | |
* | |
* 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:</p> | |
* | |
* 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. | |
* | |
*/ | |
"use strict"; | |
module.exports = function (PromiseArray) { | |
var util = require("./util.js"); | |
var RangeError = require("./errors.js").RangeError; | |
var inherits = util.inherits; | |
var isArray = util.isArray; | |
function SomePromiseArray(values, boundTo) { | |
this.constructor$(values, boundTo); | |
this._howMany = 0; | |
this._unwrap = false; | |
this._initialized = false; | |
} | |
inherits(SomePromiseArray, PromiseArray); | |
SomePromiseArray.prototype._init = function SomePromiseArray$_init() { | |
if (!this._initialized) { | |
return; | |
} | |
if (this._howMany === 0) { | |
this._resolve([]); | |
return; | |
} | |
this._init$(void 0, -2); | |
var isArrayResolved = isArray(this._values); | |
this._holes = isArrayResolved ? this._values.length - this.length() : 0; | |
if (!this._isResolved() && | |
isArrayResolved && | |
this._howMany > this._canPossiblyFulfill()) { | |
var message = "(Promise.some) input array contains less than " + | |
this._howMany + " promises"; | |
this._reject(new RangeError(message)); | |
} | |
}; | |
SomePromiseArray.prototype.init = function SomePromiseArray$init() { | |
this._initialized = true; | |
this._init(); | |
}; | |
SomePromiseArray.prototype.setUnwrap = function SomePromiseArray$setUnwrap() { | |
this._unwrap = true; | |
}; | |
SomePromiseArray.prototype.howMany = function SomePromiseArray$howMany() { | |
return this._howMany; | |
}; | |
SomePromiseArray.prototype.setHowMany = | |
function SomePromiseArray$setHowMany(count) { | |
if (this._isResolved()) return; | |
this._howMany = count; | |
}; | |
SomePromiseArray.prototype._promiseFulfilled = | |
function SomePromiseArray$_promiseFulfilled(value) { | |
if (this._isResolved()) return; | |
this._addFulfilled(value); | |
if (this._fulfilled() === this.howMany()) { | |
this._values.length = this.howMany(); | |
if (this.howMany() === 1 && this._unwrap) { | |
this._resolve(this._values[0]); | |
} | |
else { | |
this._resolve(this._values); | |
} | |
} | |
}; | |
SomePromiseArray.prototype._promiseRejected = | |
function SomePromiseArray$_promiseRejected(reason) { | |
if (this._isResolved()) return; | |
this._addRejected(reason); | |
if (this.howMany() > this._canPossiblyFulfill()) { | |
if (this._values.length === this.length()) { | |
this._reject([]); | |
} | |
else { | |
this._reject(this._values.slice(this.length() + this._holes)); | |
} | |
} | |
}; | |
SomePromiseArray.prototype._fulfilled = function SomePromiseArray$_fulfilled() { | |
return this._totalResolved; | |
}; | |
SomePromiseArray.prototype._rejected = function SomePromiseArray$_rejected() { | |
return this._values.length - this.length() - this._holes; | |
}; | |
SomePromiseArray.prototype._addRejected = | |
function SomePromiseArray$_addRejected(reason) { | |
this._values.push(reason); | |
}; | |
SomePromiseArray.prototype._addFulfilled = | |
function SomePromiseArray$_addFulfilled(value) { | |
this._values[this._totalResolved++] = value; | |
}; | |
SomePromiseArray.prototype._canPossiblyFulfill = | |
function SomePromiseArray$_canPossiblyFulfill() { | |
return this.length() - this._rejected(); | |
}; | |
return SomePromiseArray; | |
}; | |
},{"./errors.js":12,"./util.js":40}],37:[function(require,module,exports){ | |
/** | |
* Copyright (c) 2014 Petka Antonov | |
* | |
* 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:</p> | |
* | |
* 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. | |
* | |
*/ | |
"use strict"; | |
module.exports = function(Promise) { | |
function PromiseInspection(promise) { | |
if (promise !== void 0) { | |
this._bitField = promise._bitField; | |
this._settledValue = promise.isResolved() | |
? promise._settledValue | |
: void 0; | |
} | |
else { | |
this._bitField = 0; | |
this._settledValue = void 0; | |
} | |
} | |
PromiseInspection.prototype.isFulfilled = | |
Promise.prototype.isFulfilled = function Promise$isFulfilled() { | |
return (this._bitField & 268435456) > 0; | |
}; | |
PromiseInspection.prototype.isRejected = | |
Promise.prototype.isRejected = function Promise$isRejected() { | |
return (this._bitField & 134217728) > 0; | |
}; | |
PromiseInspection.prototype.isPending = | |
Promise.prototype.isPending = function Promise$isPending() { | |
return (this._bitField & 402653184) === 0; | |
}; | |
PromiseInspection.prototype.value = | |
Promise.prototype.value = function Promise$value() { | |
if (!this.isFulfilled()) { | |
throw new TypeError("cannot get fulfillment value of a non-fulfilled promise"); | |
} | |
return this._settledValue; | |
}; | |
PromiseInspection.prototype.error = | |
Promise.prototype.reason = function Promise$reason() { | |
if (!this.isRejected()) { | |
throw new TypeError("cannot get rejection reason of a non-rejected promise"); | |
} | |
return this._settledValue; | |
}; | |
PromiseInspection.prototype.isResolved = | |
Promise.prototype.isResolved = function Promise$isResolved() { | |
return (this._bitField & 402653184) > 0; | |
}; | |
Promise.prototype.inspect = function Promise$inspect() { | |
return new PromiseInspection(this); | |
}; | |
Promise.PromiseInspection = PromiseInspection; | |
}; | |
},{}],38:[function(require,module,exports){ | |
/** | |
* Copyright (c) 2014 Petka Antonov | |
* | |
* 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:</p> | |
* | |
* 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. | |
* | |
*/ | |
"use strict"; | |
module.exports = function(Promise, INTERNAL) { | |
var util = require("./util.js"); | |
var canAttach = require("./errors.js").canAttach; | |
var errorObj = util.errorObj; | |
var isObject = util.isObject; | |
function getThen(obj) { | |
try { | |
return obj.then; | |
} | |
catch(e) { | |
errorObj.e = e; | |
return errorObj; | |
} | |
} | |
function Promise$_Cast(obj, originalPromise) { | |
if (isObject(obj)) { | |
if (obj instanceof Promise) { | |
return obj; | |
} | |
else if (isAnyBluebirdPromise(obj)) { | |
var ret = new Promise(INTERNAL); | |
ret._setTrace(void 0); | |
obj._then( | |
ret._fulfillUnchecked, | |
ret._rejectUncheckedCheckError, | |
ret._progressUnchecked, | |
ret, | |
null | |
); | |
ret._setFollowing(); | |
return ret; | |
} | |
var then = getThen(obj); | |
if (then === errorObj) { | |
if (originalPromise !== void 0 && canAttach(then.e)) { | |
originalPromise._attachExtraTrace(then.e); | |
} | |
return Promise.reject(then.e); | |
} | |
else if (typeof then === "function") { | |
return Promise$_doThenable(obj, then, originalPromise); | |
} | |
} | |
return obj; | |
} | |
var hasProp = {}.hasOwnProperty; | |
function isAnyBluebirdPromise(obj) { | |
return hasProp.call(obj, "_promise0"); | |
} | |
function Promise$_doThenable(x, then, originalPromise) { | |
var resolver = Promise.defer(); | |
var called = false; | |
try { | |
then.call( | |
x, | |
Promise$_resolveFromThenable, | |
Promise$_rejectFromThenable, | |
Promise$_progressFromThenable | |
); | |
} | |
catch(e) { | |
if (!called) { | |
called = true; | |
var trace = canAttach(e) ? e : new Error(e + ""); | |
if (originalPromise !== void 0) { | |
originalPromise._attachExtraTrace(trace); | |
} | |
resolver.promise._reject(e, trace); | |
} | |
} | |
return resolver.promise; | |
function Promise$_resolveFromThenable(y) { | |
if (called) return; | |
called = true; | |
if (x === y) { | |
var e = Promise._makeSelfResolutionError(); | |
if (originalPromise !== void 0) { | |
originalPromise._attachExtraTrace(e); | |
} | |
resolver.promise._reject(e, void 0); | |
return; | |
} | |
resolver.resolve(y); | |
} | |
function Promise$_rejectFromThenable(r) { | |
if (called) return; | |
called = true; | |
var trace = canAttach(r) ? r : new Error(r + ""); | |
if (originalPromise !== void 0) { | |
originalPromise._attachExtraTrace(trace); | |
} | |
resolver.promise._reject(r, trace); | |
} | |
function Promise$_progressFromThenable(v) { | |
if (called) return; | |
var promise = resolver.promise; | |
if (typeof promise._progress === "function") { | |
promise._progress(v); | |
} | |
} | |
} | |
Promise._cast = Promise$_Cast; | |
}; | |
},{"./errors.js":12,"./util.js":40}],39:[function(require,module,exports){ | |
/** | |
* Copyright (c) 2014 Petka Antonov | |
* | |
* 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:</p> | |
* | |
* 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. | |
* | |
*/ | |
"use strict"; | |
var global = require("./global.js"); | |
var setTimeout = function(fn, ms) { | |
var $_len = arguments.length;var args = new Array($_len - 2); for(var $_i = 2; $_i < $_len; ++$_i) {args[$_i - 2] = arguments[$_i];} | |
global.setTimeout(function(){ | |
fn.apply(void 0, args); | |
}, ms); | |
}; | |
module.exports = function(Promise, INTERNAL) { | |
var util = require("./util.js"); | |
var errors = require("./errors.js"); | |
var apiRejection = require("./errors_api_rejection")(Promise); | |
var TimeoutError = Promise.TimeoutError; | |
var afterTimeout = function Promise$_afterTimeout(promise, message, ms) { | |
if (!promise.isPending()) return; | |
if (typeof message !== "string") { | |
message = "operation timed out after" + " " + ms + " ms" | |
} | |
var err = new TimeoutError(message); | |
errors.markAsOriginatingFromRejection(err); | |
promise._attachExtraTrace(err); | |
promise._rejectUnchecked(err); | |
}; | |
var afterDelay = function Promise$_afterDelay(value, promise) { | |
promise._fulfill(value); | |
}; | |
var delay = Promise.delay = function Promise$Delay(value, ms) { | |
if (ms === void 0) { | |
ms = value; | |
value = void 0; | |
} | |
ms = +ms; | |
var maybePromise = Promise._cast(value, void 0); | |
var promise = new Promise(INTERNAL); | |
if (maybePromise instanceof Promise) { | |
if (maybePromise._isBound()) { | |
promise._setBoundTo(maybePromise._boundTo); | |
} | |
if (maybePromise._cancellable()) { | |
promise._setCancellable(); | |
promise._cancellationParent = maybePromise; | |
} | |
promise._setTrace(maybePromise); | |
promise._follow(maybePromise); | |
return promise.then(function(value) { | |
return Promise.delay(value, ms); | |
}); | |
} | |
else { | |
promise._setTrace(void 0); | |
setTimeout(afterDelay, ms, value, promise); | |
} | |
return promise; | |
}; | |
Promise.prototype.delay = function Promise$delay(ms) { | |
return delay(this, ms); | |
}; | |
Promise.prototype.timeout = function Promise$timeout(ms, message) { | |
ms = +ms; | |
var ret = new Promise(INTERNAL); | |
ret._setTrace(this); | |
if (this._isBound()) ret._setBoundTo(this._boundTo); | |
if (this._cancellable()) { | |
ret._setCancellable(); | |
ret._cancellationParent = this; | |
} | |
ret._follow(this); | |
setTimeout(afterTimeout, ms, ret, message, ms); | |
return ret; | |
}; | |
}; | |
},{"./errors.js":12,"./errors_api_rejection":13,"./global.js":18,"./util.js":40}],40:[function(require,module,exports){ | |
/** | |
* Copyright (c) 2014 Petka Antonov | |
* | |
* 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:</p> | |
* | |
* 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. | |
* | |
*/ | |
"use strict"; | |
var global = require("./global.js"); | |
var es5 = require("./es5.js"); | |
var haveGetters = (function(){ | |
try { | |
var o = {}; | |
es5.defineProperty(o, "f", { | |
get: function () { | |
return 3; | |
} | |
}); | |
return o.f === 3; | |
} | |
catch (e) { | |
return false; | |
} | |
})(); | |
var canEvaluate = (function() { | |
if (typeof window !== "undefined" && window !== null && | |
typeof window.document !== "undefined" && | |
typeof navigator !== "undefined" && navigator !== null && | |
typeof navigator.appName === "string" && | |
window === global) { | |
return false; | |
} | |
return true; | |
})(); | |
function deprecated(msg) { | |
if (typeof console !== "undefined" && console !== null && | |
typeof console.warn === "function") { | |
console.warn("Bluebird: " + msg); | |
} | |
} | |
var errorObj = {e: {}}; | |
function tryCatch1(fn, receiver, arg) { | |
try { | |
return fn.call(receiver, arg); | |
} | |
catch (e) { | |
errorObj.e = e; | |
return errorObj; | |
} | |
} | |
function tryCatch2(fn, receiver, arg, arg2) { | |
try { | |
return fn.call(receiver, arg, arg2); | |
} | |
catch (e) { | |
errorObj.e = e; | |
return errorObj; | |
} | |
} | |
function tryCatchApply(fn, args, receiver) { | |
try { | |
return fn.apply(receiver, args); | |
} | |
catch (e) { | |
errorObj.e = e; | |
return errorObj; | |
} | |
} | |
var inherits = function(Child, Parent) { | |
var hasProp = {}.hasOwnProperty; | |
function T() { | |
this.constructor = Child; | |
this.constructor$ = Parent; | |
for (var propertyName in Parent.prototype) { | |
if (hasProp.call(Parent.prototype, propertyName) && | |
propertyName.charAt(propertyName.length-1) !== "$" | |
) { | |
this[propertyName + "$"] = Parent.prototype[propertyName]; | |
} | |
} | |
} | |
T.prototype = Parent.prototype; | |
Child.prototype = new T(); | |
return Child.prototype; | |
}; | |
function asString(val) { | |
return typeof val === "string" ? val : ("" + val); | |
} | |
function isPrimitive(val) { | |
return val == null || val === true || val === false || | |
typeof val === "string" || typeof val === "number"; | |
} | |
function isObject(value) { | |
return !isPrimitive(value); | |
} | |
function maybeWrapAsError(maybeError) { | |
if (!isPrimitive(maybeError)) return maybeError; | |
return new Error(asString(maybeError)); | |
} | |
function withAppended(target, appendee) { | |
var len = target.length; | |
var ret = new Array(len + 1); | |
var i; | |
for (i = 0; i < len; ++i) { | |
ret[i] = target[i]; | |
} | |
ret[i] = appendee; | |
return ret; | |
} | |
function notEnumerableProp(obj, name, value) { | |
if (isPrimitive(obj)) return obj; | |
var descriptor = { | |
value: value, | |
configurable: true, | |
enumerable: false, | |
writable: true | |
}; | |
es5.defineProperty(obj, name, descriptor); | |
return obj; | |
} | |
var wrapsPrimitiveReceiver = (function() { | |
return this !== "string"; | |
}).call("string"); | |
function thrower(r) { | |
throw r; | |
} | |
function toFastProperties(obj) { | |
/*jshint -W027*/ | |
function f() {} | |
f.prototype = obj; | |
return f; | |
eval(obj); | |
} | |
var ret = { | |
thrower: thrower, | |
isArray: es5.isArray, | |
haveGetters: haveGetters, | |
notEnumerableProp: notEnumerableProp, | |
isPrimitive: isPrimitive, | |
isObject: isObject, | |
canEvaluate: canEvaluate, | |
deprecated: deprecated, | |
errorObj: errorObj, | |
tryCatch1: tryCatch1, | |
tryCatch2: tryCatch2, | |
tryCatchApply: tryCatchApply, | |
inherits: inherits, | |
withAppended: withAppended, | |
asString: asString, | |
maybeWrapAsError: maybeWrapAsError, | |
wrapsPrimitiveReceiver: wrapsPrimitiveReceiver, | |
toFastProperties: toFastProperties | |
}; | |
module.exports = ret; | |
},{"./es5.js":14,"./global.js":18}],41:[function(require,module,exports){ | |
},{}],42:[function(require,module,exports){ | |
/*! | |
* The buffer module from node.js, for the browser. | |
* | |
* @author Feross Aboukhadijeh <feross@feross.org> <http://feross.org> | |
* @license MIT | |
*/ | |
var base64 = require('base64-js') | |
var ieee754 = require('ieee754') | |
exports.Buffer = Buffer | |
exports.SlowBuffer = Buffer | |
exports.INSPECT_MAX_BYTES = 50 | |
Buffer.poolSize = 8192 | |
/** | |
* If `Buffer._useTypedArrays`: | |
* === true Use Uint8Array implementation (fastest) | |
* === false Use Object implementation (compatible down to IE6) | |
*/ | |
Buffer._useTypedArrays = (function () { | |
// Detect if browser supports Typed Arrays. Supported browsers are IE 10+, Firefox 4+, | |
// Chrome 7+, Safari 5.1+, Opera 11.6+, iOS 4.2+. If the browser does not support adding | |
// properties to `Uint8Array` instances, then that's the same as no `Uint8Array` support | |
// because we need to be able to add all the node Buffer API methods. This is an issue | |
// in Firefox 4-29. Now fixed: https://bugzilla.mozilla.org/show_bug.cgi?id=695438 | |
try { | |
var buf = new ArrayBuffer(0) | |
var arr = new Uint8Array(buf) | |
arr.foo = function () { return 42 } | |
return 42 === arr.foo() && | |
typeof arr.subarray === 'function' // Chrome 9-10 lack `subarray` | |
} catch (e) { | |
return false | |
} | |
})() | |
/** | |
* Class: Buffer | |
* ============= | |
* | |
* The Buffer constructor returns instances of `Uint8Array` that are augmented | |
* with function properties for all the node `Buffer` API functions. We use | |
* `Uint8Array` so that square bracket notation works as expected -- it returns | |
* a single octet. | |
* | |
* By augmenting the instances, we can avoid modifying the `Uint8Array` | |
* prototype. | |
*/ | |
function Buffer (subject, encoding, noZero) { | |
if (!(this instanceof Buffer)) | |
return new Buffer(subject, encoding, noZero) | |
var type = typeof subject | |
// Workaround: node's base64 implementation allows for non-padded strings | |
// while base64-js does not. | |
if (encoding === 'base64' && type === 'string') { | |
subject = stringtrim(subject) | |
while (subject.length % 4 !== 0) { | |
subject = subject + '=' | |
} | |
} | |
// Find the length | |
var length | |
if (type === 'number') | |
length = coerce(subject) | |
else if (type === 'string') | |
length = Buffer.byteLength(subject, encoding) | |
else if (type === 'object') | |
length = coerce(subject.length) // assume that object is array-like | |
else | |
throw new Error('First argument needs to be a number, array or string.') | |
var buf | |
if (Buffer._useTypedArrays) { | |
// Preferred: Return an augmented `Uint8Array` instance for best performance | |
buf = Buffer._augment(new Uint8Array(length)) | |
} else { | |
// Fallback: Return THIS instance of Buffer (created by `new`) | |
buf = this | |
buf.length = length | |
buf._isBuffer = true | |
} | |
var i | |
if (Buffer._useTypedArrays && typeof subject.byteLength === 'number') { | |
// Speed optimization -- use set if we're copying from a typed array | |
buf._set(subject) | |
} else if (isArrayish(subject)) { | |
// Treat array-ish objects as a byte array | |
for (i = 0; i < length; i++) { | |
if (Buffer.isBuffer(subject)) | |
buf[i] = subject.readUInt8(i) | |
else | |
buf[i] = subject[i] | |
} | |
} else if (type === 'string') { | |
buf.write(subject, 0, encoding) | |
} else if (type === 'number' && !Buffer._useTypedArrays && !noZero) { | |
for (i = 0; i < length; i++) { | |
buf[i] = 0 | |
} | |
} | |
return buf | |
} | |
// STATIC METHODS | |
// ============== | |
Buffer.isEncoding = function (encoding) { | |
switch (String(encoding).toLowerCase()) { | |
case 'hex': | |
case 'utf8': | |
case 'utf-8': | |
case 'ascii': | |
case 'binary': | |
case 'base64': | |
case 'raw': | |
case 'ucs2': | |
case 'ucs-2': | |
case 'utf16le': | |
case 'utf-16le': | |
return true | |
default: | |
return false | |
} | |
} | |
Buffer.isBuffer = function (b) { | |
return !!(b !== null && b !== undefined && b._isBuffer) | |
} | |
Buffer.byteLength = function (str, encoding) { | |
var ret | |
str = str + '' | |
switch (encoding || 'utf8') { | |
case 'hex': | |
ret = str.length / 2 | |
break | |
case 'utf8': | |
case 'utf-8': | |
ret = utf8ToBytes(str).length | |
break | |
case 'ascii': | |
case 'binary': | |
case 'raw': | |
ret = str.length | |
break | |
case 'base64': | |
ret = base64ToBytes(str).length | |
break | |
case 'ucs2': | |
case 'ucs-2': | |
case 'utf16le': | |
case 'utf-16le': | |
ret = str.length * 2 | |
break | |
default: | |
throw new Error('Unknown encoding') | |
} | |
return ret | |
} | |
Buffer.concat = function (list, totalLength) { | |
assert(isArray(list), 'Usage: Buffer.concat(list, [totalLength])\n' + | |
'list should be an Array.') | |
if (list.length === 0) { | |
return new Buffer(0) | |
} else if (list.length === 1) { | |
return list[0] | |
} | |
var i | |
if (typeof totalLength !== 'number') { | |
totalLength = 0 | |
for (i = 0; i < list.length; i++) { | |
totalLength += list[i].length | |
} | |
} | |
var buf = new Buffer(totalLength) | |
var pos = 0 | |
for (i = 0; i < list.length; i++) { | |
var item = list[i] | |
item.copy(buf, pos) | |
pos += item.length | |
} | |
return buf | |
} | |
// BUFFER INSTANCE METHODS | |
// ======================= | |
function _hexWrite (buf, string, offset, length) { | |
offset = Number(offset) || 0 | |
var remaining = buf.length - offset | |
if (!length) { | |
length = remaining | |
} else { | |
length = Number(length) | |
if (length > remaining) { | |
length = remaining | |
} | |
} | |
// must be an even number of digits | |
var strLen = string.length | |
assert(strLen % 2 === 0, 'Invalid hex string') | |
if (length > strLen / 2) { | |
length = strLen / 2 | |
} | |
for (var i = 0; i < length; i++) { | |
var byte = parseInt(string.substr(i * 2, 2), 16) | |
assert(!isNaN(byte), 'Invalid hex string') | |
buf[offset + i] = byte | |
} | |
Buffer._charsWritten = i * 2 | |
return i | |
} | |
function _utf8Write (buf, string, offset, length) { | |
var charsWritten = Buffer._charsWritten = | |
blitBuffer(utf8ToBytes(string), buf, offset, length) | |
return charsWritten | |
} | |
function _asciiWrite (buf, string, offset, length) { | |
var charsWritten = Buffer._charsWritten = | |
blitBuffer(asciiToBytes(string), buf, offset, length) | |
return charsWritten | |
} | |
function _binaryWrite (buf, string, offset, length) { | |
return _asciiWrite(buf, string, offset, length) | |
} | |
function _base64Write (buf, string, offset, length) { | |
var charsWritten = Buffer._charsWritten = | |
blitBuffer(base64ToBytes(string), buf, offset, length) | |
return charsWritten | |
} | |
function _utf16leWrite (buf, string, offset, length) { | |
var charsWritten = Buffer._charsWritten = | |
blitBuffer(utf16leToBytes(string), buf, offset, length) | |
return charsWritten | |
} | |
Buffer.prototype.write = function (string, offset, length, encoding) { | |
// Support both (string, offset, length, encoding) | |
// and the legacy (string, encoding, offset, length) | |
if (isFinite(offset)) { | |
if (!isFinite(length)) { | |
encoding = length | |
length = undefined | |
} | |
} else { // legacy | |
var swap = encoding | |
encoding = offset | |
offset = length | |
length = swap | |
} | |
offset = Number(offset) || 0 | |
var remaining = this.length - offset | |
if (!length) { | |
length = remaining | |
} else { | |
length = Number(length) | |
if (length > remaining) { | |
length = remaining | |
} | |
} | |
encoding = String(encoding || 'utf8').toLowerCase() | |
var ret | |
switch (encoding) { | |
case 'hex': | |
ret = _hexWrite(this, string, offset, length) | |
break | |
case 'utf8': | |
case 'utf-8': | |
ret = _utf8Write(this, string, offset, length) | |
break | |
case 'ascii': | |
ret = _asciiWrite(this, string, offset, length) | |
break | |
case 'binary': | |
ret = _binaryWrite(this, string, offset, length) | |
break | |
case 'base64': | |
ret = _base64Write(this, string, offset, length) | |
break | |
case 'ucs2': | |
case 'ucs-2': | |
case 'utf16le': | |
case 'utf-16le': | |
ret = _utf16leWrite(this, string, offset, length) | |
break | |
default: | |
throw new Error('Unknown encoding') | |
} | |
return ret | |
} | |
Buffer.prototype.toString = function (encoding, start, end) { | |
var self = this | |
encoding = String(encoding || 'utf8').toLowerCase() | |
start = Number(start) || 0 | |
end = (end !== undefined) | |
? Number(end) | |
: end = self.length | |
// Fastpath empty strings | |
if (end === start) | |
return '' | |
var ret | |
switch (encoding) { | |
case 'hex': | |
ret = _hexSlice(self, start, end) | |
break | |
case 'utf8': | |
case 'utf-8': | |
ret = _utf8Slice(self, start, end) | |
break | |
case 'ascii': | |
ret = _asciiSlice(self, start, end) | |
break | |
case 'binary': | |
ret = _binarySlice(self, start, end) | |
break | |
case 'base64': | |
ret = _base64Slice(self, start, end) | |
break | |
case 'ucs2': | |
case 'ucs-2': | |
case 'utf16le': | |
case 'utf-16le': | |
ret = _utf16leSlice(self, start, end) | |
break | |
default: | |
throw new Error('Unknown encoding') | |
} | |
return ret | |
} | |
Buffer.prototype.toJSON = function () { | |
return { | |
type: 'Buffer', | |
data: Array.prototype.slice.call(this._arr || this, 0) | |
} | |
} | |
// copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length) | |
Buffer.prototype.copy = function (target, target_start, start, end) { | |
var source = this | |
if (!start) start = 0 | |
if (!end && end !== 0) end = this.length | |
if (!target_start) target_start = 0 | |
// Copy 0 bytes; we're done | |
if (end === start) return | |
if (target.length === 0 || source.length === 0) return | |
// Fatal error conditions | |
assert(end >= start, 'sourceEnd < sourceStart') | |
assert(target_start >= 0 && target_start < target.length, | |
'targetStart out of bounds') | |
assert(start >= 0 && start < source.length, 'sourceStart out of bounds') | |
assert(end >= 0 && end <= source.length, 'sourceEnd out of bounds') | |
// Are we oob? | |
if (end > this.length) | |
end = this.length | |
if (target.length - target_start < end - start) | |
end = target.length - target_start + start | |
var len = end - start | |
if (len < 100 || !Buffer._useTypedArrays) { | |
for (var i = 0; i < len; i++) | |
target[i + target_start] = this[i + start] | |
} else { | |
target._set(this.subarray(start, start + len), target_start) | |
} | |
} | |
function _base64Slice (buf, start, end) { | |
if (start === 0 && end === buf.length) { | |
return base64.fromByteArray(buf) | |
} else { | |
return base64.fromByteArray(buf.slice(start, end)) | |
} | |
} | |
function _utf8Slice (buf, start, end) { | |
var res = '' | |
var tmp = '' | |
end = Math.min(buf.length, end) | |
for (var i = start; i < end; i++) { | |
if (buf[i] <= 0x7F) { | |
res += decodeUtf8Char(tmp) + String.fromCharCode(buf[i]) | |
tmp = '' | |
} else { | |
tmp += '%' + buf[i].toString(16) | |
} | |
} | |
return res + decodeUtf8Char(tmp) | |
} | |
function _asciiSlice (buf, start, end) { | |
var ret = '' | |
end = Math.min(buf.length, end) | |
for (var i = start; i < end; i++) | |
ret += String.fromCharCode(buf[i]) | |
return ret | |
} | |
function _binarySlice (buf, start, end) { | |
return _asciiSlice(buf, start, end) | |
} | |
function _hexSlice (buf, start, end) { | |
var len = buf.length | |
if (!start || start < 0) start = 0 | |
if (!end || end < 0 || end > len) end = len | |
var out = '' | |
for (var i = start; i < end; i++) { | |
out += toHex(buf[i]) | |
} | |
return out | |
} | |
function _utf16leSlice (buf, start, end) { | |
var bytes = buf.slice(start, end) | |
var res = '' | |
for (var i = 0; i < bytes.length; i += 2) { | |
res += String.fromCharCode(bytes[i] + bytes[i+1] * 256) | |
} | |
return res | |
} | |
Buffer.prototype.slice = function (start, end) { | |
var len = this.length | |
start = clamp(start, len, 0) | |
end = clamp(end, len, len) | |
if (Buffer._useTypedArrays) { | |
return Buffer._augment(this.subarray(start, end)) | |
} else { | |
var sliceLen = end - start | |
var newBuf = new Buffer(sliceLen, undefined, true) | |
for (var i = 0; i < sliceLen; i++) { | |
newBuf[i] = this[i + start] | |
} | |
return newBuf | |
} | |
} | |
// `get` will be removed in Node 0.13+ | |
Buffer.prototype.get = function (offset) { | |
console.log('.get() is deprecated. Access using array indexes instead.') | |
return this.readUInt8(offset) | |
} | |
// `set` will be removed in Node 0.13+ | |
Buffer.prototype.set = function (v, offset) { | |
console.log('.set() is deprecated. Access using array indexes instead.') | |
return this.writeUInt8(v, offset) | |
} | |
Buffer.prototype.readUInt8 = function (offset, noAssert) { | |
if (!noAssert) { | |
assert(offset !== undefined && offset !== null, 'missing offset') | |
assert(offset < this.length, 'Trying to read beyond buffer length') | |
} | |
if (offset >= this.length) | |
return | |
return this[offset] | |
} | |
function _readUInt16 (buf, offset, littleEndian, noAssert) { | |
if (!noAssert) { | |
assert(typeof littleEndian === 'boolean', 'missing or invalid endian') | |
assert(offset !== undefined && offset !== null, 'missing offset') | |
assert(offset + 1 < buf.length, 'Trying to read beyond buffer length') | |
} | |
var len = buf.length | |
if (offset >= len) | |
return | |
var val | |
if (littleEndian) { | |
val = buf[offset] | |
if (offset + 1 < len) | |
val |= buf[offset + 1] << 8 | |
} else { | |
val = buf[offset] << 8 | |
if (offset + 1 < len) | |
val |= buf[offset + 1] | |
} | |
return val | |
} | |
Buffer.prototype.readUInt16LE = function (offset, noAssert) { | |
return _readUInt16(this, offset, true, noAssert) | |
} | |
Buffer.prototype.readUInt16BE = function (offset, noAssert) { | |
return _readUInt16(this, offset, false, noAssert) | |
} | |
function _readUInt32 (buf, offset, littleEndian, noAssert) { | |
if (!noAssert) { | |
assert(typeof littleEndian === 'boolean', 'missing or invalid endian') | |
assert(offset !== undefined && offset !== null, 'missing offset') | |
assert(offset + 3 < buf.length, 'Trying to read beyond buffer length') | |
} | |
var len = buf.length | |
if (offset >= len) | |
return | |
var val | |
if (littleEndian) { | |
if (offset + 2 < len) | |
val = buf[offset + 2] << 16 | |
if (offset + 1 < len) | |
val |= buf[offset + 1] << 8 | |
val |= buf[offset] | |
if (offset + 3 < len) | |
val = val + (buf[offset + 3] << 24 >>> 0) | |
} else { | |
if (offset + 1 < len) | |
val = buf[offset + 1] << 16 | |
if (offset + 2 < len) | |
val |= buf[offset + 2] << 8 | |
if (offset + 3 < len) | |
val |= buf[offset + 3] | |
val = val + (buf[offset] << 24 >>> 0) | |
} | |
return val | |
} | |
Buffer.prototype.readUInt32LE = function (offset, noAssert) { | |
return _readUInt32(this, offset, true, noAssert) | |
} | |
Buffer.prototype.readUInt32BE = function (offset, noAssert) { | |
return _readUInt32(this, offset, false, noAssert) | |
} | |
Buffer.prototype.readInt8 = function (offset, noAssert) { | |
if (!noAssert) { | |
assert(offset !== undefined && offset !== null, | |
'missing offset') | |
assert(offset < this.length, 'Trying to read beyond buffer length') | |
} | |
if (offset >= this.length) | |
return | |
var neg = this[offset] & 0x80 | |
if (neg) | |
return (0xff - this[offset] + 1) * -1 | |
else | |
return this[offset] | |
} | |
function _readInt16 (buf, offset, littleEndian, noAssert) { | |
if (!noAssert) { | |
assert(typeof littleEndian === 'boolean', 'missing or invalid endian') | |
assert(offset !== undefined && offset !== null, 'missing offset') | |
assert(offset + 1 < buf.length, 'Trying to read beyond buffer length') | |
} | |
var len = buf.length | |
if (offset >= len) | |
return | |
var val = _readUInt16(buf, offset, littleEndian, true) | |
var neg = val & 0x8000 | |
if (neg) | |
return (0xffff - val + 1) * -1 | |
else | |
return val | |
} | |
Buffer.prototype.readInt16LE = function (offset, noAssert) { | |
return _readInt16(this, offset, true, noAssert) | |
} | |
Buffer.prototype.readInt16BE = function (offset, noAssert) { | |
return _readInt16(this, offset, false, noAssert) | |
} | |
function _readInt32 (buf, offset, littleEndian, noAssert) { | |
if (!noAssert) { | |
assert(typeof littleEndian === 'boolean', 'missing or invalid endian') | |
assert(offset !== undefined && offset !== null, 'missing offset') | |
assert(offset + 3 < buf.length, 'Trying to read beyond buffer length') | |
} | |
var len = buf.length | |
if (offset >= len) | |
return | |
var val = _readUInt32(buf, offset, littleEndian, true) | |
var neg = val & 0x80000000 | |
if (neg) | |
return (0xffffffff - val + 1) * -1 | |
else | |
return val | |
} | |
Buffer.prototype.readInt32LE = function (offset, noAssert) { | |
return _readInt32(this, offset, true, noAssert) | |
} | |
Buffer.prototype.readInt32BE = function (offset, noAssert) { | |
return _readInt32(this, offset, false, noAssert) | |
} | |
function _readFloat (buf, offset, littleEndian, noAssert) { | |
if (!noAssert) { | |
assert(typeof littleEndian === 'boolean', 'missing or invalid endian') | |
assert(offset + 3 < buf.length, 'Trying to read beyond buffer length') | |
} | |
return ieee754.read(buf, offset, littleEndian, 23, 4) | |
} | |
Buffer.prototype.readFloatLE = function (offset, noAssert) { | |
return _readFloat(this, offset, true, noAssert) | |
} | |
Buffer.prototype.readFloatBE = function (offset, noAssert) { | |
return _readFloat(this, offset, false, noAssert) | |
} | |
function _readDouble (buf, offset, littleEndian, noAssert) { | |
if (!noAssert) { | |
assert(typeof littleEndian === 'boolean', 'missing or invalid endian') | |
assert(offset + 7 < buf.length, 'Trying to read beyond buffer length') | |
} | |
return ieee754.read(buf, offset, littleEndian, 52, 8) | |
} | |
Buffer.prototype.readDoubleLE = function (offset, noAssert) { | |
return _readDouble(this, offset, true, noAssert) | |
} | |
Buffer.prototype.readDoubleBE = function (offset, noAssert) { | |
return _readDouble(this, offset, false, noAssert) | |
} | |
Buffer.prototype.writeUInt8 = function (value, offset, noAssert) { | |
if (!noAssert) { | |
assert(value !== undefined && value !== null, 'missing value') | |
assert(offset !== undefined && offset !== null, 'missing offset') | |
assert(offset < this.length, 'trying to write beyond buffer length') | |
verifuint(value, 0xff) | |
} | |
if (offset >= this.length) return | |
this[offset] = value | |
} | |
function _writeUInt16 (buf, value, offset, littleEndian, noAssert) { | |
if (!noAssert) { | |
assert(value !== undefined && value !== null, 'missing value') | |
assert(typeof littleEndian === 'boolean', 'missing or invalid endian') | |
assert(offset !== undefined && offset !== null, 'missing offset') | |
assert(offset + 1 < buf.length, 'trying to write beyond buffer length') | |
verifuint(value, 0xffff) | |
} | |
var len = buf.length | |
if (offset >= len) | |
return | |
for (var i = 0, j = Math.min(len - offset, 2); i < j; i++) { | |
buf[offset + i] = | |
(value & (0xff << (8 * (littleEndian ? i : 1 - i)))) >>> | |
(littleEndian ? i : 1 - i) * 8 | |
} | |
} | |
Buffer.prototype.writeUInt16LE = function (value, offset, noAssert) { | |
_writeUInt16(this, value, offset, true, noAssert) | |
} | |
Buffer.prototype.writeUInt16BE = function (value, offset, noAssert) { | |
_writeUInt16(this, value, offset, false, noAssert) | |
} | |
function _writeUInt32 (buf, value, offset, littleEndian, noAssert) { | |
if (!noAssert) { | |
assert(value !== undefined && value !== null, 'missing value') | |
assert(typeof littleEndian === 'boolean', 'missing or invalid endian') | |
assert(offset !== undefined && offset !== null, 'missing offset') | |
assert(offset + 3 < buf.length, 'trying to write beyond buffer length') | |
verifuint(value, 0xffffffff) | |
} | |
var len = buf.length | |
if (offset >= len) | |
return | |
for (var i = 0, j = Math.min(len - offset, 4); i < j; i++) { | |
buf[offset + i] = | |
(value >>> (littleEndian ? i : 3 - i) * 8) & 0xff | |
} | |
} | |
Buffer.prototype.writeUInt32LE = function (value, offset, noAssert) { | |
_writeUInt32(this, value, offset, true, noAssert) | |
} | |
Buffer.prototype.writeUInt32BE = function (value, offset, noAssert) { | |
_writeUInt32(this, value, offset, false, noAssert) | |
} | |
Buffer.prototype.writeInt8 = function (value, offset, noAssert) { | |
if (!noAssert) { | |
assert(value !== undefined && value !== null, 'missing value') | |
assert(offset !== undefined && offset !== null, 'missing offset') | |
assert(offset < this.length, 'Trying to write beyond buffer length') | |
verifsint(value, 0x7f, -0x80) | |
} | |
if (offset >= this.length) | |
return | |
if (value >= 0) | |
this.writeUInt8(value, offset, noAssert) | |
else | |
this.writeUInt8(0xff + value + 1, offset, noAssert) | |
} | |
function _writeInt16 (buf, value, offset, littleEndian, noAssert) { | |
if (!noAssert) { | |
assert(value !== undefined && value !== null, 'missing value') | |
assert(typeof littleEndian === 'boolean', 'missing or invalid endian') | |
assert(offset !== undefined && offset !== null, 'missing offset') | |
assert(offset + 1 < buf.length, 'Trying to write beyond buffer length') | |
verifsint(value, 0x7fff, -0x8000) | |
} | |
var len = buf.length | |
if (offset >= len) | |
return | |
if (value >= 0) | |
_writeUInt16(buf, value, offset, littleEndian, noAssert) | |
else | |
_writeUInt16(buf, 0xffff + value + 1, offset, littleEndian, noAssert) | |
} | |
Buffer.prototype.writeInt16LE = function (value, offset, noAssert) { | |
_writeInt16(this, value, offset, true, noAssert) | |
} | |
Buffer.prototype.writeInt16BE = function (value, offset, noAssert) { | |
_writeInt16(this, value, offset, false, noAssert) | |
} | |
function _writeInt32 (buf, value, offset, littleEndian, noAssert) { | |
if (!noAssert) { | |
assert(value !== undefined && value !== null, 'missing value') | |
assert(typeof littleEndian === 'boolean', 'missing or invalid endian') | |
assert(offset !== undefined && offset !== null, 'missing offset') | |
assert(offset + 3 < buf.length, 'Trying to write beyond buffer length') | |
verifsint(value, 0x7fffffff, -0x80000000) | |
} | |
var len = buf.length | |
if (offset >= len) | |
return | |
if (value >= 0) | |
_writeUInt32(buf, value, offset, littleEndian, noAssert) | |
else | |
_writeUInt32(buf, 0xffffffff + value + 1, offset, littleEndian, noAssert) | |
} | |
Buffer.prototype.writeInt32LE = function (value, offset, noAssert) { | |
_writeInt32(this, value, offset, true, noAssert) | |
} | |
Buffer.prototype.writeInt32BE = function (value, offset, noAssert) { | |
_writeInt32(this, value, offset, false, noAssert) | |
} | |
function _writeFloat (buf, value, offset, littleEndian, noAssert) { | |
if (!noAssert) { | |
assert(value !== undefined && value !== null, 'missing value') | |
assert(typeof littleEndian === 'boolean', 'missing or invalid endian') | |
assert(offset !== undefined && offset !== null, 'missing offset') | |
assert(offset + 3 < buf.length, 'Trying to write beyond buffer length') | |
verifIEEE754(value, 3.4028234663852886e+38, -3.4028234663852886e+38) | |
} | |
var len = buf.length | |
if (offset >= len) | |
return | |
ieee754.write(buf, value, offset, littleEndian, 23, 4) | |
} | |
Buffer.prototype.writeFloatLE = function (value, offset, noAssert) { | |
_writeFloat(this, value, offset, true, noAssert) | |
} | |
Buffer.prototype.writeFloatBE = function (value, offset, noAssert) { | |
_writeFloat(this, value, offset, false, noAssert) | |
} | |
function _writeDouble (buf, value, offset, littleEndian, noAssert) { | |
if (!noAssert) { | |
assert(value !== undefined && value !== null, 'missing value') | |
assert(typeof littleEndian === 'boolean', 'missing or invalid endian') | |
assert(offset !== undefined && offset !== null, 'missing offset') | |
assert(offset + 7 < buf.length, | |
'Trying to write beyond buffer length') | |
verifIEEE754(value, 1.7976931348623157E+308, -1.7976931348623157E+308) | |
} | |
var len = buf.length | |
if (offset >= len) | |
return | |
ieee754.write(buf, value, offset, littleEndian, 52, 8) | |
} | |
Buffer.prototype.writeDoubleLE = function (value, offset, noAssert) { | |
_writeDouble(this, value, offset, true, noAssert) | |
} | |
Buffer.prototype.writeDoubleBE = function (value, offset, noAssert) { | |
_writeDouble(this, value, offset, false, noAssert) | |
} | |
// fill(value, start=0, end=buffer.length) | |
Buffer.prototype.fill = function (value, start, end) { | |
if (!value) value = 0 | |
if (!start) start = 0 | |
if (!end) end = this.length | |
if (typeof value === 'string') { | |
value = value.charCodeAt(0) | |
} | |
assert(typeof value === 'number' && !isNaN(value), 'value is not a number') | |
assert(end >= start, 'end < start') | |
// Fill 0 bytes; we're done | |
if (end === start) return | |
if (this.length === 0) return | |
assert(start >= 0 && start < this.length, 'start out of bounds') | |
assert(end >= 0 && end <= this.length, 'end out of bounds') | |
for (var i = start; i < end; i++) { | |
this[i] = value | |
} | |
} | |
Buffer.prototype.inspect = function () { | |
var out = [] | |
var len = this.length | |
for (var i = 0; i < len; i++) { | |
out[i] = toHex(this[i]) | |
if (i === exports.INSPECT_MAX_BYTES) { | |
out[i + 1] = '...' | |
break | |
} | |
} | |
return '<Buffer ' + out.join(' ') + '>' | |
} | |
/** | |
* Creates a new `ArrayBuffer` with the *copied* memory of the buffer instance. | |
* Added in Node 0.12. Only available in browsers that support ArrayBuffer. | |
*/ | |
Buffer.prototype.toArrayBuffer = function () { | |
if (typeof Uint8Array !== 'undefined') { | |
if (Buffer._useTypedArrays) { | |
return (new Buffer(this)).buffer | |
} else { | |
var buf = new Uint8Array(this.length) | |
for (var i = 0, len = buf.length; i < len; i += 1) | |
buf[i] = this[i] | |
return buf.buffer | |
} | |
} else { | |
throw new Error('Buffer.toArrayBuffer not supported in this browser') | |
} | |
} | |
// HELPER FUNCTIONS | |
// ================ | |
function stringtrim (str) { | |
if (str.trim) return str.trim() | |
return str.replace(/^\s+|\s+$/g, '') | |
} | |
var BP = Buffer.prototype | |
/** | |
* Augment a Uint8Array *instance* (not the Uint8Array class!) with Buffer methods | |
*/ | |
Buffer._augment = function (arr) { | |
arr._isBuffer = true | |
// save reference to original Uint8Array get/set methods before overwriting | |
arr._get = arr.get | |
arr._set = arr.set | |
// deprecated, will be removed in node 0.13+ | |
arr.get = BP.get | |
arr.set = BP.set | |
arr.write = BP.write | |
arr.toString = BP.toString | |
arr.toLocaleString = BP.toString | |
arr.toJSON = BP.toJSON | |
arr.copy = BP.copy | |
arr.slice = BP.slice | |
arr.readUInt8 = BP.readUInt8 | |
arr.readUInt16LE = BP.readUInt16LE | |
arr.readUInt16BE = BP.readUInt16BE | |
arr.readUInt32LE = BP.readUInt32LE | |
arr.readUInt32BE = BP.readUInt32BE | |
arr.readInt8 = BP.readInt8 | |
arr.readInt16LE = BP.readInt16LE | |
arr.readInt16BE = BP.readInt16BE | |
arr.readInt32LE = BP.readInt32LE | |
arr.readInt32BE = BP.readInt32BE | |
arr.readFloatLE = BP.readFloatLE | |
arr.readFloatBE = BP.readFloatBE | |
arr.readDoubleLE = BP.readDoubleLE | |
arr.readDoubleBE = BP.readDoubleBE | |
arr.writeUInt8 = BP.writeUInt8 | |
arr.writeUInt16LE = BP.writeUInt16LE | |
arr.writeUInt16BE = BP.writeUInt16BE | |
arr.writeUInt32LE = BP.writeUInt32LE | |
arr.writeUInt32BE = BP.writeUInt32BE | |
arr.writeInt8 = BP.writeInt8 | |
arr.writeInt16LE = BP.writeInt16LE | |
arr.writeInt16BE = BP.writeInt16BE | |
arr.writeInt32LE = BP.writeInt32LE | |
arr.writeInt32BE = BP.writeInt32BE | |
arr.writeFloatLE = BP.writeFloatLE | |
arr.writeFloatBE = BP.writeFloatBE | |
arr.writeDoubleLE = BP.writeDoubleLE | |
arr.writeDoubleBE = BP.writeDoubleBE | |
arr.fill = BP.fill | |
arr.inspect = BP.inspect | |
arr.toArrayBuffer = BP.toArrayBuffer | |
return arr | |
} | |
// slice(start, end) | |
function clamp (index, len, defaultValue) { | |
if (typeof index !== 'number') return defaultValue | |
index = ~~index; // Coerce to integer. | |
if (index >= len) return len | |
if (index >= 0) return index | |
index += len | |
if (index >= 0) return index | |
return 0 | |
} | |
function coerce (length) { | |
// Coerce length to a number (possibly NaN), round up | |
// in case it's fractional (e.g. 123.456) then do a | |
// double negate to coerce a NaN to 0. Easy, right? | |
length = ~~Math.ceil(+length) | |
return length < 0 ? 0 : length | |
} | |
function isArray (subject) { | |
return (Array.isArray || function (subject) { | |
return Object.prototype.toString.call(subject) === '[object Array]' | |
})(subject) | |
} | |
function isArrayish (subject) { | |
return isArray(subject) || Buffer.isBuffer(subject) || | |
subject && typeof subject === 'object' && | |
typeof subject.length === 'number' | |
} | |
function toHex (n) { | |
if (n < 16) return '0' + n.toString(16) | |
return n.toString(16) | |
} | |
function utf8ToBytes (str) { | |
var byteArray = [] | |
for (var i = 0; i < str.length; i++) { | |
var b = str.charCodeAt(i) | |
if (b <= 0x7F) | |
byteArray.push(str.charCodeAt(i)) | |
else { | |
var start = i | |
if (b >= 0xD800 && b <= 0xDFFF) i++ | |
var h = encodeURIComponent(str.slice(start, i+1)).substr(1).split('%') | |
for (var j = 0; j < h.length; j++) | |
byteArray.push(parseInt(h[j], 16)) | |
} | |
} | |
return byteArray | |
} | |
function asciiToBytes (str) { | |
var byteArray = [] | |
for (var i = 0; i < str.length; i++) { | |
// Node's code seems to be doing this and not & 0x7F.. | |
byteArray.push(str.charCodeAt(i) & 0xFF) | |
} | |
return byteArray | |
} | |
function utf16leToBytes (str) { | |
var c, hi, lo | |
var byteArray = [] | |
for (var i = 0; i < str.length; i++) { | |
c = str.charCodeAt(i) | |
hi = c >> 8 | |
lo = c % 256 | |
byteArray.push(lo) | |
byteArray.push(hi) | |
} | |
return byteArray | |
} | |
function base64ToBytes (str) { | |
return base64.toByteArray(str) | |
} | |
function blitBuffer (src, dst, offset, length) { | |
var pos | |
for (var i = 0; i < length; i++) { | |
if ((i + offset >= dst.length) || (i >= src.length)) | |
break | |
dst[i + offset] = src[i] | |
} | |
return i | |
} | |
function decodeUtf8Char (str) { | |
try { | |
return decodeURIComponent(str) | |
} catch (err) { | |
return String.fromCharCode(0xFFFD) // UTF 8 invalid char | |
} | |
} | |
/* | |
* We have to make sure that the value is a valid integer. This means that it | |
* is non-negative. It has no fractional component and that it does not | |
* exceed the maximum allowed value. | |
*/ | |
function verifuint (value, max) { | |
assert(typeof value === 'number', 'cannot write a non-number as a number') | |
assert(value >= 0, 'specified a negative value for writing an unsigned value') | |
assert(value <= max, 'value is larger than maximum value for type') | |
assert(Math.floor(value) === value, 'value has a fractional component') | |
} | |
function verifsint (value, max, min) { | |
assert(typeof value === 'number', 'cannot write a non-number as a number') | |
assert(value <= max, 'value larger than maximum allowed value') | |
assert(value >= min, 'value smaller than minimum allowed value') | |
assert(Math.floor(value) === value, 'value has a fractional component') | |
} | |
function verifIEEE754 (value, max, min) { | |
assert(typeof value === 'number', 'cannot write a non-number as a number') | |
assert(value <= max, 'value larger than maximum allowed value') | |
assert(value >= min, 'value smaller than minimum allowed value') | |
} | |
function assert (test, message) { | |
if (!test) throw new Error(message || 'Failed assertion') | |
} | |
},{"base64-js":43,"ieee754":44}],43:[function(require,module,exports){ | |
var lookup = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; | |
;(function (exports) { | |
'use strict'; | |
var Arr = (typeof Uint8Array !== 'undefined') | |
? Uint8Array | |
: Array | |
var PLUS = '+'.charCodeAt(0) | |
var SLASH = '/'.charCodeAt(0) | |
var NUMBER = '0'.charCodeAt(0) | |
var LOWER = 'a'.charCodeAt(0) | |
var UPPER = 'A'.charCodeAt(0) | |
var PLUS_URL_SAFE = '-'.charCodeAt(0) | |
var SLASH_URL_SAFE = '_'.charCodeAt(0) | |
function decode (elt) { | |
var code = elt.charCodeAt(0) | |
if (code === PLUS || | |
code === PLUS_URL_SAFE) | |
return 62 // '+' | |
if (code === SLASH || | |
code === SLASH_URL_SAFE) | |
return 63 // '/' | |
if (code < NUMBER) | |
return -1 //no match | |
if (code < NUMBER + 10) | |
return code - NUMBER + 26 + 26 | |
if (code < UPPER + 26) | |
return code - UPPER | |
if (code < LOWER + 26) | |
return code - LOWER + 26 | |
} | |
function b64ToByteArray (b64) { | |
var i, j, l, tmp, placeHolders, arr | |
if (b64.length % 4 > 0) { | |
throw new Error('Invalid string. Length must be a multiple of 4') | |
} | |
// the number of equal signs (place holders) | |
// if there are two placeholders, than the two characters before it | |
// represent one byte | |
// if there is only one, then the three characters before it represent 2 bytes | |
// this is just a cheap hack to not do indexOf twice | |
var len = b64.length | |
placeHolders = '=' === b64.charAt(len - 2) ? 2 : '=' === b64.charAt(len - 1) ? 1 : 0 | |
// base64 is 4/3 + up to two characters of the original data | |
arr = new Arr(b64.length * 3 / 4 - placeHolders) | |
// if there are placeholders, only get up to the last complete 4 chars | |
l = placeHolders > 0 ? b64.length - 4 : b64.length | |
var L = 0 | |
function push (v) { | |
arr[L++] = v | |
} | |
for (i = 0, j = 0; i < l; i += 4, j += 3) { | |
tmp = (decode(b64.charAt(i)) << 18) | (decode(b64.charAt(i + 1)) << 12) | (decode(b64.charAt(i + 2)) << 6) | decode(b64.charAt(i + 3)) | |
push((tmp & 0xFF0000) >> 16) | |
push((tmp & 0xFF00) >> 8) | |
push(tmp & 0xFF) | |
} | |
if (placeHolders === 2) { | |
tmp = (decode(b64.charAt(i)) << 2) | (decode(b64.charAt(i + 1)) >> 4) | |
push(tmp & 0xFF) | |
} else if (placeHolders === 1) { | |
tmp = (decode(b64.charAt(i)) << 10) | (decode(b64.charAt(i + 1)) << 4) | (decode(b64.charAt(i + 2)) >> 2) | |
push((tmp >> 8) & 0xFF) | |
push(tmp & 0xFF) | |
} | |
return arr | |
} | |
function uint8ToBase64 (uint8) { | |
var i, | |
extraBytes = uint8.length % 3, // if we have 1 byte left, pad 2 bytes | |
output = "", | |
temp, length | |
function encode (num) { | |
return lookup.charAt(num) | |
} | |
function tripletToBase64 (num) { | |
return encode(num >> 18 & 0x3F) + encode(num >> 12 & 0x3F) + encode(num >> 6 & 0x3F) + encode(num & 0x3F) | |
} | |
// go through the array every three bytes, we'll deal with trailing stuff later | |
for (i = 0, length = uint8.length - extraBytes; i < length; i += 3) { | |
temp = (uint8[i] << 16) + (uint8[i + 1] << 8) + (uint8[i + 2]) | |
output += tripletToBase64(temp) | |
} | |
// pad the end with zeros, but make sure to not forget the extra bytes | |
switch (extraBytes) { | |
case 1: | |
temp = uint8[uint8.length - 1] | |
output += encode(temp >> 2) | |
output += encode((temp << 4) & 0x3F) | |
output += '==' | |
break | |
case 2: | |
temp = (uint8[uint8.length - 2] << 8) + (uint8[uint8.length - 1]) | |
output += encode(temp >> 10) | |
output += encode((temp >> 4) & 0x3F) | |
output += encode((temp << 2) & 0x3F) | |
output += '=' | |
break | |
} | |
return output | |
} | |
exports.toByteArray = b64ToByteArray | |
exports.fromByteArray = uint8ToBase64 | |
}(typeof exports === 'undefined' ? (this.base64js = {}) : exports)) | |
},{}],44:[function(require,module,exports){ | |
exports.read = function (buffer, offset, isLE, mLen, nBytes) { | |
var e, m, | |
eLen = nBytes * 8 - mLen - 1, | |
eMax = (1 << eLen) - 1, | |
eBias = eMax >> 1, | |
nBits = -7, | |
i = isLE ? (nBytes - 1) : 0, | |
d = isLE ? -1 : 1, | |
s = buffer[offset + i] | |
i += d | |
e = s & ((1 << (-nBits)) - 1) | |
s >>= (-nBits) | |
nBits += eLen | |
for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8) {} | |
m = e & ((1 << (-nBits)) - 1) | |
e >>= (-nBits) | |
nBits += mLen | |
for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8) {} | |
if (e === 0) { | |
e = 1 - eBias | |
} else if (e === eMax) { | |
return m ? NaN : ((s ? -1 : 1) * Infinity) | |
} else { | |
m = m + Math.pow(2, mLen) | |
e = e - eBias | |
} | |
return (s ? -1 : 1) * m * Math.pow(2, e - mLen) | |
} | |
exports.write = function (buffer, value, offset, isLE, mLen, nBytes) { | |
var e, m, c, | |
eLen = nBytes * 8 - mLen - 1, | |
eMax = (1 << eLen) - 1, | |
eBias = eMax >> 1, | |
rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0), | |
i = isLE ? 0 : (nBytes - 1), | |
d = isLE ? 1 : -1, | |
s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0 | |
value = Math.abs(value) | |
if (isNaN(value) || value === Infinity) { | |
m = isNaN(value) ? 1 : 0 | |
e = eMax | |
} else { | |
e = Math.floor(Math.log(value) / Math.LN2) | |
if (value * (c = Math.pow(2, -e)) < 1) { | |
e-- | |
c *= 2 | |
} | |
if (e + eBias >= 1) { | |
value += rt / c | |
} else { | |
value += rt * Math.pow(2, 1 - eBias) | |
} | |
if (value * c >= 2) { | |
e++ | |
c /= 2 | |
} | |
if (e + eBias >= eMax) { | |
m = 0 | |
e = eMax | |
} else if (e + eBias >= 1) { | |
m = (value * c - 1) * Math.pow(2, mLen) | |
e = e + eBias | |
} else { | |
m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen) | |
e = 0 | |
} | |
} | |
for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {} | |
e = (e << mLen) | m | |
eLen += mLen | |
for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {} | |
buffer[offset + i - d] |= s * 128 | |
} | |
},{}],45:[function(require,module,exports){ | |
// Copyright Joyent, Inc. and other Node contributors. | |
// | |
// 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 EventEmitter() { | |
this._events = this._events || {}; | |
this._maxListeners = this._maxListeners || undefined; | |
} | |
module.exports = EventEmitter; | |
// Backwards-compat with node 0.10.x | |
EventEmitter.EventEmitter = EventEmitter; | |
EventEmitter.prototype._events = undefined; | |
EventEmitter.prototype._maxListeners = undefined; | |
// By default EventEmitters will print a warning if more than 10 listeners are | |
// added to it. This is a useful default which helps finding memory leaks. | |
EventEmitter.defaultMaxListeners = 10; | |
// Obviously not all Emitters should be limited to 10. This function allows | |
// that to be increased. Set to zero for unlimited. | |
EventEmitter.prototype.setMaxListeners = function(n) { | |
if (!isNumber(n) || n < 0 || isNaN(n)) | |
throw TypeError('n must be a positive number'); | |
this._maxListeners = n; | |
return this; | |
}; | |
EventEmitter.prototype.emit = function(type) { | |
var er, handler, len, args, i, listeners; | |
if (!this._events) | |
this._events = {}; | |
// If there is no 'error' event listener then throw. | |
if (type === 'error') { | |
if (!this._events.error || | |
(isObject(this._events.error) && !this._events.error.length)) { | |
er = arguments[1]; | |
if (er instanceof Error) { | |
throw er; // Unhandled 'error' event | |
} | |
throw TypeError('Uncaught, unspecified "error" event.'); | |
} | |
} | |
handler = this._events[type]; | |
if (isUndefined(handler)) | |
return false; | |
if (isFunction(handler)) { | |
switch (arguments.length) { | |
// fast cases | |
case 1: | |
handler.call(this); | |
break; | |
case 2: | |
handler.call(this, arguments[1]); | |
break; | |
case 3: | |
handler.call(this, arguments[1], arguments[2]); | |
break; | |
// slower | |
default: | |
len = arguments.length; | |
args = new Array(len - 1); | |
for (i = 1; i < len; i++) | |
args[i - 1] = arguments[i]; | |
handler.apply(this, args); | |
} | |
} else if (isObject(handler)) { | |
len = arguments.length; | |
args = new Array(len - 1); | |
for (i = 1; i < len; i++) | |
args[i - 1] = arguments[i]; | |
listeners = handler.slice(); | |
len = listeners.length; | |
for (i = 0; i < len; i++) | |
listeners[i].apply(this, args); | |
} | |
return true; | |
}; | |
EventEmitter.prototype.addListener = function(type, listener) { | |
var m; | |
if (!isFunction(listener)) | |
throw TypeError('listener must be a function'); | |
if (!this._events) | |
this._events = {}; | |
// To avoid recursion in the case that type === "newListener"! Before | |
// adding it to the listeners, first emit "newListener". | |
if (this._events.newListener) | |
this.emit('newListener', type, | |
isFunction(listener.listener) ? | |
listener.listener : listener); | |
if (!this._events[type]) | |
// Optimize the case of one listener. Don't need the extra array object. | |
this._events[type] = listener; | |
else if (isObject(this._events[type])) | |
// If we've already got an array, just append. | |
this._events[type].push(listener); | |
else | |
// Adding the second element, need to change to array. | |
this._events[type] = [this._events[type], listener]; | |
// Check for listener leak | |
if (isObject(this._events[type]) && !this._events[type].warned) { | |
var m; | |
if (!isUndefined(this._maxListeners)) { | |
m = this._maxListeners; | |
} else { | |
m = EventEmitter.defaultMaxListeners; | |
} | |
if (m && m > 0 && this._events[type].length > m) { | |
this._events[type].warned = true; | |
console.error('(node) warning: possible EventEmitter memory ' + | |
'leak detected. %d listeners added. ' + | |
'Use emitter.setMaxListeners() to increase limit.', | |
this._events[type].length); | |
if (typeof console.trace === 'function') { | |
// not supported in IE 10 | |
console.trace(); | |
} | |
} | |
} | |
return this; | |
}; | |
EventEmitter.prototype.on = EventEmitter.prototype.addListener; | |
EventEmitter.prototype.once = function(type, listener) { | |
if (!isFunction(listener)) | |
throw TypeError('listener must be a function'); | |
var fired = false; | |
function g() { | |
this.removeListener(type, g); | |
if (!fired) { | |
fired = true; | |
listener.apply(this, arguments); | |
} | |
} | |
g.listener = listener; | |
this.on(type, g); | |
return this; | |
}; | |
// emits a 'removeListener' event iff the listener was removed | |
EventEmitter.prototype.removeListener = function(type, listener) { | |
var list, position, length, i; | |
if (!isFunction(listener)) | |
throw TypeError('listener must be a function'); | |
if (!this._events || !this._events[type]) | |
return this; | |
list = this._events[type]; | |
length = list.length; | |
position = -1; | |
if (list === listener || | |
(isFunction(list.listener) && list.listener === listener)) { | |
delete this._events[type]; | |
if (this._events.removeListener) | |
this.emit('removeListener', type, listener); | |
} else if (isObject(list)) { | |
for (i = length; i-- > 0;) { | |
if (list[i] === listener || | |
(list[i].listener && list[i].listener === listener)) { | |
position = i; | |
break; | |
} | |
} | |
if (position < 0) | |
return this; | |
if (list.length === 1) { | |
list.length = 0; | |
delete this._events[type]; | |
} else { | |
list.splice(position, 1); | |
} | |
if (this._events.removeListener) | |
this.emit('removeListener', type, listener); | |
} | |
return this; | |
}; | |
EventEmitter.prototype.removeAllListeners = function(type) { | |
var key, listeners; | |
if (!this._events) | |
return this; | |
// not listening for removeListener, no need to emit | |
if (!this._events.removeListener) { | |
if (arguments.length === 0) | |
this._events = {}; | |
else if (this._events[type]) | |
delete this._events[type]; | |
return this; | |
} | |
// emit removeListener for all listeners on all events | |
if (arguments.length === 0) { | |
for (key in this._events) { | |
if (key === 'removeListener') continue; | |
this.removeAllListeners(key); | |
} | |
this.removeAllListeners('removeListener'); | |
this._events = {}; | |
return this; | |
} | |
listeners = this._events[type]; | |
if (isFunction(listeners)) { | |
this.removeListener(type, listeners); | |
} else { | |
// LIFO order | |
while (listeners.length) | |
this.removeListener(type, listeners[listeners.length - 1]); | |
} | |
delete this._events[type]; | |
return this; | |
}; | |
EventEmitter.prototype.listeners = function(type) { | |
var ret; | |
if (!this._events || !this._events[type]) | |
ret = []; | |
else if (isFunction(this._events[type])) | |
ret = [this._events[type]]; | |
else | |
ret = this._events[type].slice(); | |
return ret; | |
}; | |
EventEmitter.listenerCount = function(emitter, type) { | |
var ret; | |
if (!emitter._events || !emitter._events[type]) | |
ret = 0; | |
else if (isFunction(emitter._events[type])) | |
ret = 1; | |
else | |
ret = emitter._events[type].length; | |
return ret; | |
}; | |
function isFunction(arg) { | |
return typeof arg === 'function'; | |
} | |
function isNumber(arg) { | |
return typeof arg === 'number'; | |
} | |
function isObject(arg) { | |
return typeof arg === 'object' && arg !== null; | |
} | |
function isUndefined(arg) { | |
return arg === void 0; | |
} | |
},{}],46:[function(require,module,exports){ | |
// shim for using process in browser | |
var process = module.exports = {}; | |
process.nextTick = (function () { | |
var canSetImmediate = typeof window !== 'undefined' | |
&& window.setImmediate; | |
var canPost = typeof window !== 'undefined' | |
&& window.postMessage && window.addEventListener | |
; | |
if (canSetImmediate) { | |
return function (f) { return window.setImmediate(f) }; | |
} | |
if (canPost) { | |
var queue = []; | |
window.addEventListener('message', function (ev) { | |
var source = ev.source; | |
if ((source === window || source === null) && ev.data === 'process-tick') { | |
ev.stopPropagation(); | |
if (queue.length > 0) { | |
var fn = queue.shift(); | |
fn(); | |
} | |
} | |
}, true); | |
return function nextTick(fn) { | |
queue.push(fn); | |
window.postMessage('process-tick', '*'); | |
}; | |
} | |
return function nextTick(fn) { | |
setTimeout(fn, 0); | |
}; | |
})(); | |
process.title = 'browser'; | |
process.browser = true; | |
process.env = {}; | |
process.argv = []; | |
function noop() {} | |
process.on = noop; | |
process.once = noop; | |
process.off = noop; | |
process.emit = noop; | |
process.binding = function (name) { | |
throw new Error('process.binding is not supported'); | |
} | |
// TODO(shtylman) | |
process.cwd = function () { return '/' }; | |
process.chdir = function (dir) { | |
throw new Error('process.chdir is not supported'); | |
}; | |
},{}],47:[function(require,module,exports){ | |
(function (process){ | |
// Copyright Joyent, Inc. and other Node contributors. | |
// | |
// 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. | |
// resolves . and .. elements in a path array with directory names there | |
// must be no slashes, empty elements, or device names (c:\) in the array | |
// (so also no leading and trailing slashes - it does not distinguish | |
// relative and absolute paths) | |
function normalizeArray(parts, allowAboveRoot) { | |
// if the path tries to go above the root, `up` ends up > 0 | |
var up = 0; | |
for (var i = parts.length - 1; i >= 0; i--) { | |
var last = parts[i]; | |
if (last === '.') { | |
parts.splice(i, 1); | |
} else if (last === '..') { | |
parts.splice(i, 1); | |
up++; | |
} else if (up) { | |
parts.splice(i, 1); | |
up--; | |
} | |
} | |
// if the path is allowed to go above the root, restore leading ..s | |
if (allowAboveRoot) { | |
for (; up--; up) { | |
parts.unshift('..'); | |
} | |
} | |
return parts; | |
} | |
// Split a filename into [root, dir, basename, ext], unix version | |
// 'root' is just a slash, or nothing. | |
var splitPathRe = | |
/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/; | |
var splitPath = function(filename) { | |
return splitPathRe.exec(filename).slice(1); | |
}; | |
// path.resolve([from ...], to) | |
// posix version | |
exports.resolve = function() { | |
var resolvedPath = '', | |
resolvedAbsolute = false; | |
for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) { | |
var path = (i >= 0) ? arguments[i] : process.cwd(); | |
// Skip empty and invalid entries | |
if (typeof path !== 'string') { | |
throw new TypeError('Arguments to path.resolve must be strings'); | |
} else if (!path) { | |
continue; | |
} | |
resolvedPath = path + '/' + resolvedPath; | |
resolvedAbsolute = path.charAt(0) === '/'; | |
} | |
// At this point the path should be resolved to a full absolute path, but | |
// handle relative paths to be safe (might happen when process.cwd() fails) | |
// Normalize the path | |
resolvedPath = normalizeArray(filter(resolvedPath.split('/'), function(p) { | |
return !!p; | |
}), !resolvedAbsolute).join('/'); | |
return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.'; | |
}; | |
// path.normalize(path) | |
// posix version | |
exports.normalize = function(path) { | |
var isAbsolute = exports.isAbsolute(path), | |
trailingSlash = substr(path, -1) === '/'; | |
// Normalize the path | |
path = normalizeArray(filter(path.split('/'), function(p) { | |
return !!p; | |
}), !isAbsolute).join('/'); | |
if (!path && !isAbsolute) { | |
path = '.'; | |
} | |
if (path && trailingSlash) { | |
path += '/'; | |
} | |
return (isAbsolute ? '/' : '') + path; | |
}; | |
// posix version | |
exports.isAbsolute = function(path) { | |
return path.charAt(0) === '/'; | |
}; | |
// posix version | |
exports.join = function() { | |
var paths = Array.prototype.slice.call(arguments, 0); | |
return exports.normalize(filter(paths, function(p, index) { | |
if (typeof p !== 'string') { | |
throw new TypeError('Arguments to path.join must be strings'); | |
} | |
return p; | |
}).join('/')); | |
}; | |
// path.relative(from, to) | |
// posix version | |
exports.relative = function(from, to) { | |
from = exports.resolve(from).substr(1); | |
to = exports.resolve(to).substr(1); | |
function trim(arr) { | |
var start = 0; | |
for (; start < arr.length; start++) { | |
if (arr[start] !== '') break; | |
} | |
var end = arr.length - 1; | |
for (; end >= 0; end--) { | |
if (arr[end] !== '') break; | |
} | |
if (start > end) return []; | |
return arr.slice(start, end - start + 1); | |
} | |
var fromParts = trim(from.split('/')); | |
var toParts = trim(to.split('/')); | |
var length = Math.min(fromParts.length, toParts.length); | |
var samePartsLength = length; | |
for (var i = 0; i < length; i++) { | |
if (fromParts[i] !== toParts[i]) { | |
samePartsLength = i; | |
break; | |
} | |
} | |
var outputParts = []; | |
for (var i = samePartsLength; i < fromParts.length; i++) { | |
outputParts.push('..'); | |
} | |
outputParts = outputParts.concat(toParts.slice(samePartsLength)); | |
return outputParts.join('/'); | |
}; | |
exports.sep = '/'; | |
exports.delimiter = ':'; | |
exports.dirname = function(path) { | |
var result = splitPath(path), | |
root = result[0], | |
dir = result[1]; | |
if (!root && !dir) { | |
// No dirname whatsoever | |
return '.'; | |
} | |
if (dir) { | |
// It has a dirname, strip trailing slash | |
dir = dir.substr(0, dir.length - 1); | |
} | |
return root + dir; | |
}; | |
exports.basename = function(path, ext) { | |
var f = splitPath(path)[2]; | |
// TODO: make this comparison case-insensitive on windows? | |
if (ext && f.substr(-1 * ext.length) === ext) { | |
f = f.substr(0, f.length - ext.length); | |
} | |
return f; | |
}; | |
exports.extname = function(path) { | |
return splitPath(path)[3]; | |
}; | |
function filter (xs, f) { | |
if (xs.filter) return xs.filter(f); | |
var res = []; | |
for (var i = 0; i < xs.length; i++) { | |
if (f(xs[i], i, xs)) res.push(xs[i]); | |
} | |
return res; | |
} | |
// String.prototype.substr - negative index don't work in IE8 | |
var substr = 'ab'.substr(-1) === 'b' | |
? function (str, start, len) { return str.substr(start, len) } | |
: function (str, start, len) { | |
if (start < 0) start = str.length + start; | |
return str.substr(start, len); | |
} | |
; | |
}).call(this,require("/Users/nlawson/workspace-node/pouchdb-authentication/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js")) | |
},{"/Users/nlawson/workspace-node/pouchdb-authentication/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":46}],48:[function(require,module,exports){ | |
(function () { | |
"use strict"; | |
// Module systems magic dance. | |
if (typeof require === "function" && typeof exports === "object" && typeof module === "object") { | |
// NodeJS | |
module.exports = chaiAsPromised; | |
} else if (typeof define === "function" && define.amd) { | |
// AMD | |
define(function () { | |
return chaiAsPromised; | |
}); | |
} else { | |
/*global self: false */ | |
// Other environment (usually <script> tag): plug in to global chai instance directly. | |
chai.use(chaiAsPromised); | |
// Expose as a property of the global object so that consumers can configure the `transferPromiseness` property. | |
self.chaiAsPromised = chaiAsPromised; | |
} | |
chaiAsPromised.transferPromiseness = function (assertion, promise) { | |
assertion.then = promise.then.bind(promise); | |
}; | |
function chaiAsPromised(chai, utils) { | |
var Assertion = chai.Assertion; | |
var assert = chai.assert; | |
function isJQueryPromise(thenable) { | |
return typeof thenable.always === "function" && | |
typeof thenable.done === "function" && | |
typeof thenable.fail === "function" && | |
typeof thenable.pipe === "function" && | |
typeof thenable.progress === "function" && | |
typeof thenable.state === "function"; | |
} | |
function assertIsAboutPromise(assertion) { | |
if (typeof assertion._obj.then !== "function") { | |
throw new TypeError(utils.inspect(assertion._obj) + " is not a thenable."); | |
} | |
if (isJQueryPromise(assertion._obj)) { | |
throw new TypeError("Chai as Promised is incompatible with jQuery's thenables, sorry! Please use a " + | |
"Promises/A+ compatible library (see http://promisesaplus.com/)."); | |
} | |
} | |
function method(name, asserter) { | |
utils.addMethod(Assertion.prototype, name, function () { | |
assertIsAboutPromise(this); | |
return asserter.apply(this, arguments); | |
}); | |
} | |
function property(name, asserter) { | |
utils.addProperty(Assertion.prototype, name, function () { | |
assertIsAboutPromise(this); | |
return asserter.apply(this, arguments); | |
}); | |
} | |
function doNotify(promise, done) { | |
promise.then(function () { done(); }, done); | |
} | |
// These are for clarity and to bypass Chai refusing to allow `undefined` as actual when used with `assert`. | |
function assertIfNegated(assertion, message, extra) { | |
assertion.assert(true, null, message, extra.expected, extra.actual); | |
} | |
function assertIfNotNegated(assertion, message, extra) { | |
assertion.assert(false, message, null, extra.expected, extra.actual); | |
} | |
function getBasePromise(assertion) { | |
// We need to chain subsequent asserters on top of ones in the chain already (consider | |
// `eventually.have.property("foo").that.equals("bar")`), only running them after the existing ones pass. | |
// So the first base-promise is `assertion._obj`, but after that we use the assertions themselves, i.e. | |
// previously derived promises, to chain off of. | |
return typeof assertion.then === "function" ? assertion : assertion._obj; | |
} | |
// Grab these first, before we modify `Assertion.prototype`. | |
var propertyNames = Object.getOwnPropertyNames(Assertion.prototype); | |
var propertyDescs = {}; | |
propertyNames.forEach(function (name) { | |
propertyDescs[name] = Object.getOwnPropertyDescriptor(Assertion.prototype, name); | |
}); | |
property("fulfilled", function () { | |
var that = this; | |
var derivedPromise = getBasePromise(that).then( | |
function (value) { | |
that._obj = value; | |
assertIfNegated(that, | |
"expected promise not to be fulfilled but it was fulfilled with #{act}", | |
{ actual: value }); | |
return value; | |
}, | |
function (reason) { | |
assertIfNotNegated(that, | |
"expected promise to be fulfilled but it was rejected with #{act}", | |
{ actual: reason }); | |
} | |
); | |
chaiAsPromised.transferPromiseness(that, derivedPromise); | |
}); | |
property("rejected", function () { | |
var that = this; | |
var derivedPromise = getBasePromise(that).then( | |
function (value) { | |
that._obj = value; | |
assertIfNotNegated(that, | |
"expected promise to be rejected but it was fulfilled with #{act}", | |
{ actual: value }); | |
return value; | |
}, | |
function (reason) { | |
assertIfNegated(that, | |
"expected promise not to be rejected but it was rejected with #{act}", | |
{ actual: reason }); | |
// Return the reason, transforming this into a fulfillment, to allow further assertions, e.g. | |
// `promise.should.be.rejected.and.eventually.equal("reason")`. | |
return reason; | |
} | |
); | |
chaiAsPromised.transferPromiseness(that, derivedPromise); | |
}); | |
method("rejectedWith", function (Constructor, message) { | |
var desiredReason = null; | |
var constructorName = null; | |
if (Constructor instanceof RegExp || typeof Constructor === "string") { | |
message = Constructor; | |
Constructor = null; | |
} else if (Constructor && Constructor instanceof Error) { | |
desiredReason = Constructor; | |
Constructor = null; | |
message = null; | |
} else if (typeof Constructor === "function") { | |
constructorName = (new Constructor()).name; | |
} else { | |
Constructor = null; | |
} | |
var that = this; | |
var derivedPromise = getBasePromise(that).then( | |
function (value) { | |
var assertionMessage = null; | |
var expected = null; | |
if (Constructor) { | |
assertionMessage = "expected promise to be rejected with #{exp} but it was fulfilled with " + | |
"#{act}"; | |
expected = constructorName; | |
} else if (message) { | |
var verb = message instanceof RegExp ? "matching" : "including"; | |
assertionMessage = "expected promise to be rejected with an error " + verb + " #{exp} but it " + | |
"was fulfilled with #{act}"; | |
expected = message; | |
} else if (desiredReason) { | |
assertionMessage = "expected promise to be rejected with #{exp} but it was fulfilled with " + | |
"#{act}"; | |
expected = desiredReason; | |
} | |
that._obj = value; | |
assertIfNotNegated(that, assertionMessage, { expected: expected, actual: value }); | |
}, | |
function (reason) { | |
if (Constructor) { | |
that.assert(reason instanceof Constructor, | |
"expected promise to be rejected with #{exp} but it was rejected with #{act}", | |
"expected promise not to be rejected with #{exp} but it was rejected with #{act}", | |
constructorName, | |
reason); | |
} | |
var reasonMessage = utils.type(reason) === "object" && "message" in reason ? | |
reason.message : | |
"" + reason; | |
if (message && reasonMessage !== null && reasonMessage !== undefined) { | |
if (message instanceof RegExp) { | |
that.assert(message.test(reasonMessage), | |
"expected promise to be rejected with an error matching #{exp} but got #{act}", | |
"expected promise not to be rejected with an error matching #{exp}", | |
message, | |
reasonMessage); | |
} | |
if (typeof message === "string") { | |
that.assert(reasonMessage.indexOf(message) !== -1, | |
"expected promise to be rejected with an error including #{exp} but got #{act}", | |
"expected promise not to be rejected with an error including #{exp}", | |
message, | |
reasonMessage); | |
} | |
} | |
if (desiredReason) { | |
that.assert(reason === desiredReason, | |
"expected promise to be rejected with #{exp} but it was rejected with #{act}", | |
"expected promise not to be rejected with #{exp}", | |
desiredReason, | |
reason); | |
} | |
} | |
); | |
chaiAsPromised.transferPromiseness(that, derivedPromise); | |
}); | |
property("eventually", function () { | |
utils.flag(this, "eventually", true); | |
}); | |
method("notify", function (done) { | |
doNotify(getBasePromise(this), done); | |
}); | |
method("become", function (value) { | |
return this.eventually.deep.equal(value); | |
}); | |
//////// | |
// `eventually` | |
// We need to be careful not to trigger any getters, thus `Object.getOwnPropertyDescriptor` usage. | |
var methodNames = propertyNames.filter(function (name) { | |
return name !== "assert" && typeof propertyDescs[name].value === "function"; | |
}); | |
methodNames.forEach(function (methodName) { | |
Assertion.overwriteMethod(methodName, function (originalMethod) { | |
return function () { | |
doAsserterAsyncAndAddThen(originalMethod, this, arguments); | |
}; | |
}); | |
}); | |
var getterNames = propertyNames.filter(function (name) { | |
return name !== "_obj" && typeof propertyDescs[name].get === "function"; | |
}); | |
getterNames.forEach(function (getterName) { | |
var propertyDesc = propertyDescs[getterName]; | |
// Chainable methods are things like `an`, which can work both for `.should.be.an.instanceOf` and as | |
// `should.be.an("object")`. We need to handle those specially. | |
var isChainableMethod = false; | |
try { | |
isChainableMethod = typeof propertyDesc.get.call({}) === "function"; | |
} catch (e) { } | |
if (isChainableMethod) { | |
Assertion.addChainableMethod( | |
getterName, | |
function () { | |
var assertion = this; | |
function originalMethod() { | |
return propertyDesc.get.call(assertion).apply(assertion, arguments); | |
} | |
doAsserterAsyncAndAddThen(originalMethod, this, arguments); | |
}, | |
function () { | |
var originalGetter = propertyDesc.get; | |
doAsserterAsyncAndAddThen(originalGetter, this); | |
} | |
); | |
} else { | |
Assertion.overwriteProperty(getterName, function (originalGetter) { | |
return function () { | |
doAsserterAsyncAndAddThen(originalGetter, this); | |
}; | |
}); | |
} | |
}); | |
function doAsserterAsyncAndAddThen(asserter, assertion, args) { | |
// Since we're intercepting all methods/properties, we need to just pass through if they don't want | |
// `eventually`, or if we've already fulfilled the promise (see below). | |
if (!utils.flag(assertion, "eventually")) { | |
return asserter.apply(assertion, args); | |
} | |
var derivedPromise = getBasePromise(assertion).then(function (value) { | |
// Set up the environment for the asserter to actually run: `_obj` should be the fulfillment value, and | |
// now that we have the value, we're no longer in "eventually" mode, so we won't run any of this code, | |
// just the base Chai code that we get to via the short-circuit above. | |
assertion._obj = value; | |
utils.flag(assertion, "eventually", false); | |
asserter.apply(assertion, args); | |
// Because asserters, for example `property`, can change the value of `_obj` (i.e. change the "object" | |
// flag), we need to communicate this value change to subsequent chained asserters. Since we build a | |
// promise chain paralleling the asserter chain, we can use it to communicate such changes. | |
return assertion._obj; | |
}); | |
chaiAsPromised.transferPromiseness(assertion, derivedPromise); | |
} | |
/////// | |
// Now use the `Assertion` framework to build an `assert` interface. | |
var originalAssertMethods = Object.getOwnPropertyNames(assert).filter(function (propName) { | |
return typeof assert[propName] === "function"; | |
}); | |
assert.isFulfilled = function (promise, message) { | |
return (new Assertion(promise, message)).to.be.fulfilled; | |
}; | |
assert.isRejected = function (promise, toTestAgainst, message) { | |
if (typeof toTestAgainst === "string") { | |
message = toTestAgainst; | |
toTestAgainst = undefined; | |
} | |
var assertion = (new Assertion(promise, message)); | |
return toTestAgainst !== undefined ? assertion.to.be.rejectedWith(toTestAgainst) : assertion.to.be.rejected; | |
}; | |
assert.becomes = function (promise, value, message) { | |
return assert.eventually.deepEqual(promise, value, message); | |
}; | |
assert.doesNotBecome = function (promise, value, message) { | |
return assert.eventually.notDeepEqual(promise, value, message); | |
}; | |
assert.eventually = {}; | |
originalAssertMethods.forEach(function (assertMethodName) { | |
assert.eventually[assertMethodName] = function (promise) { | |
var otherArgs = Array.prototype.slice.call(arguments, 1); | |
var customRejectionHandler; | |
var message = arguments[assert[assertMethodName].length - 1]; | |
if (typeof message === "string") { | |
customRejectionHandler = function (reason) { | |
throw new chai.AssertionError(message + "\n\nOriginal reason: " + utils.inspect(reason)); | |
}; | |
} | |
var returnedPromise = promise.then( | |
function (fulfillmentValue) { | |
return assert[assertMethodName].apply(assert, [fulfillmentValue].concat(otherArgs)); | |
}, | |
customRejectionHandler | |
); | |
returnedPromise.notify = function (done) { | |
doNotify(returnedPromise, done); | |
}; | |
return returnedPromise; | |
}; | |
}); | |
} | |
}()); | |
},{}],49:[function(require,module,exports){ | |
module.exports = require('./lib/chai'); | |
},{"./lib/chai":50}],50:[function(require,module,exports){ | |
/*! | |
* chai | |
* Copyright(c) 2011-2013 Jake Luer <jake@alogicalparadox.com> | |
* MIT Licensed | |
*/ | |
var used = [] | |
, exports = module.exports = {}; | |
/*! | |
* Chai version | |
*/ | |
exports.version = '1.8.1'; | |
/*! | |
* Assertion Error | |
*/ | |
exports.AssertionError = require('assertion-error'); | |
/*! | |
* Utils for plugins (not exported) | |
*/ | |
var util = require('./chai/utils'); | |
/** | |
* # .use(function) | |
* | |
* Provides a way to extend the internals of Chai | |
* | |
* @param {Function} | |
* @returns {this} for chaining | |
* @api public | |
*/ | |
exports.use = function (fn) { | |
if (!~used.indexOf(fn)) { | |
fn(this, util); | |
used.push(fn); | |
} | |
return this; | |
}; | |
/*! | |
* Primary `Assertion` prototype | |
*/ | |
var assertion = require('./chai/assertion'); | |
exports.use(assertion); | |
/*! | |
* Core Assertions | |
*/ | |
var core = require('./chai/core/assertions'); | |
exports.use(core); | |
/*! | |
* Expect interface | |
*/ | |
var expect = require('./chai/interface/expect'); | |
exports.use(expect); | |
/*! | |
* Should interface | |
*/ | |
var should = require('./chai/interface/should'); | |
exports.use(should); | |
/*! | |
* Assert interface | |
*/ | |
var assert = require('./chai/interface/assert'); | |
exports.use(assert); | |
},{"./chai/assertion":51,"./chai/core/assertions":52,"./chai/interface/assert":53,"./chai/interface/expect":54,"./chai/interface/should":55,"./chai/utils":66,"assertion-error":74}],51:[function(require,module,exports){ | |
/*! | |
* chai | |
* http://chaijs.com | |
* Copyright(c) 2011-2013 Jake Luer <jake@alogicalparadox.com> | |
* MIT Licensed | |
*/ | |
module.exports = function (_chai, util) { | |
/*! | |
* Module dependencies. | |
*/ | |
var AssertionError = _chai.AssertionError | |
, flag = util.flag; | |
/*! | |
* Module export. | |
*/ | |
_chai.Assertion = Assertion; | |
/*! | |
* Assertion Constructor | |
* | |
* Creates object for chaining. | |
* | |
* @api private | |
*/ | |
function Assertion (obj, msg, stack) { | |
flag(this, 'ssfi', stack || arguments.callee); | |
flag(this, 'object', obj); | |
flag(this, 'message', msg); | |
} | |
/*! | |
* ### Assertion.includeStack | |
* | |
* User configurable property, influences whether stack trace | |
* is included in Assertion error message. Default of false | |
* suppresses stack trace in the error message | |
* | |
* Assertion.includeStack = true; // enable stack on error | |
* | |
* @api public | |
*/ | |
Assertion.includeStack = false; | |
/*! | |
* ### Assertion.showDiff | |
* | |
* User configurable property, influences whether or not | |
* the `showDiff` flag should be included in the thrown | |
* AssertionErrors. `false` will always be `false`; `true` | |
* will be true when the assertion has requested a diff | |
* be shown. | |
* | |
* @api public | |
*/ | |
Assertion.showDiff = true; | |
Assertion.addProperty = function (name, fn) { | |
util.addProperty(this.prototype, name, fn); | |
}; | |
Assertion.addMethod = function (name, fn) { | |
util.addMethod(this.prototype, name, fn); | |
}; | |
Assertion.addChainableMethod = function (name, fn, chainingBehavior) { | |
util.addChainableMethod(this.prototype, name, fn, chainingBehavior); | |
}; | |
Assertion.overwriteProperty = function (name, fn) { | |
util.overwriteProperty(this.prototype, name, fn); | |
}; | |
Assertion.overwriteMethod = function (name, fn) { | |
util.overwriteMethod(this.prototype, name, fn); | |
}; | |
/*! | |
* ### .assert(expression, message, negateMessage, expected, actual) | |
* | |
* Executes an expression and check expectations. Throws AssertionError for reporting if test doesn't pass. | |
* | |
* @name assert | |
* @param {Philosophical} expression to be tested | |
* @param {String} message to display if fails | |
* @param {String} negatedMessage to display if negated expression fails | |
* @param {Mixed} expected value (remember to check for negation) | |
* @param {Mixed} actual (optional) will default to `this.obj` | |
* @api private | |
*/ | |
Assertion.prototype.assert = function (expr, msg, negateMsg, expected, _actual, showDiff) { | |
var ok = util.test(this, arguments); | |
if (true !== showDiff) showDiff = false; | |
if (true !== Assertion.showDiff) showDiff = false; | |
if (!ok) { | |
var msg = util.getMessage(this, arguments) | |
, actual = util.getActual(this, arguments); | |
throw new AssertionError(msg, { | |
actual: actual | |
, expected: expected | |
, showDiff: showDiff | |
}, (Assertion.includeStack) ? this.assert : flag(this, 'ssfi')); | |
} | |
}; | |
/*! | |
* ### ._obj | |
* | |
* Quick reference to stored `actual` value for plugin developers. | |
* | |
* @api private | |
*/ | |
Object.defineProperty(Assertion.prototype, '_obj', | |
{ get: function () { | |
return flag(this, 'object'); | |
} | |
, set: function (val) { | |
flag(this, 'object', val); | |
} | |
}); | |
}; | |
},{}],52:[function(require,module,exports){ | |
/*! | |
* chai | |
* http://chaijs.com | |
* Copyright(c) 2011-2013 Jake Luer <jake@alogicalparadox.com> | |
* MIT Licensed | |
*/ | |
module.exports = function (chai, _) { | |
var Assertion = chai.Assertion | |
, toString = Object.prototype.toString | |
, flag = _.flag; | |
/** | |
* ### Language Chains | |
* | |
* The following are provide as chainable getters to | |
* improve the readability of your assertions. They | |
* do not provide an testing capability unless they | |
* have been overwritten by a plugin. | |
* | |
* **Chains** | |
* | |
* - to | |
* - be | |
* - been | |
* - is | |
* - that | |
* - and | |
* - have | |
* - with | |
* - at | |
* - of | |
* - same | |
* | |
* @name language chains | |
* @api public | |
*/ | |
[ 'to', 'be', 'been' | |
, 'is', 'and', 'have' | |
, 'with', 'that', 'at' | |
, 'of', 'same' ].forEach(function (chain) { | |
Assertion.addProperty(chain, function () { | |
return this; | |
}); | |
}); | |
/** | |
* ### .not | |
* | |
* Negates any of assertions following in the chain. | |
* | |
* expect(foo).to.not.equal('bar'); | |
* expect(goodFn).to.not.throw(Error); | |
* expect({ foo: 'baz' }).to.have.property('foo') | |
* .and.not.equal('bar'); | |
* | |
* @name not | |
* @api public | |
*/ | |
Assertion.addProperty('not', function () { | |
flag(this, 'negate', true); | |
}); | |
/** | |
* ### .deep | |
* | |
* Sets the `deep` flag, later used by the `equal` and | |
* `property` assertions. | |
* | |
* expect(foo).to.deep.equal({ bar: 'baz' }); | |
* expect({ foo: { bar: { baz: 'quux' } } }) | |
* .to.have.deep.property('foo.bar.baz', 'quux'); | |
* | |
* @name deep | |
* @api public | |
*/ | |
Assertion.addProperty('deep', function () { | |
flag(this, 'deep', true); | |
}); | |
/** | |
* ### .a(type) | |
* | |
* The `a` and `an` assertions are aliases that can be | |
* used either as language chains or to assert a value's | |
* type. | |
* | |
* // typeof | |
* expect('test').to.be.a('string'); | |
* expect({ foo: 'bar' }).to.be.an('object'); | |
* expect(null).to.be.a('null'); | |
* expect(undefined).to.be.an('undefined'); | |
* | |
* // language chain | |
* expect(foo).to.be.an.instanceof(Foo); | |
* | |
* @name a | |
* @alias an | |
* @param {String} type | |
* @param {String} message _optional_ | |
* @api public | |
*/ | |
function an (type, msg) { | |
if (msg) flag(this, 'message', msg); | |
type = type.toLowerCase(); | |
var obj = flag(this, 'object') | |
, article = ~[ 'a', 'e', 'i', 'o', 'u' ].indexOf(type.charAt(0)) ? 'an ' : 'a '; | |
this.assert( | |
type === _.type(obj) | |
, 'expected #{this} to be ' + article + type | |
, 'expected #{this} not to be ' + article + type | |
); | |
} | |
Assertion.addChainableMethod('an', an); | |
Assertion.addChainableMethod('a', an); | |
/** | |
* ### .include(value) | |
* | |
* The `include` and `contain` assertions can be used as either property | |
* based language chains or as methods to assert the inclusion of an object | |
* in an array or a substring in a string. When used as language chains, | |
* they toggle the `contain` flag for the `keys` assertion. | |
* | |
* expect([1,2,3]).to.include(2); | |
* expect('foobar').to.contain('foo'); | |
* expect({ foo: 'bar', hello: 'universe' }).to.include.keys('foo'); | |
* | |
* @name include | |
* @alias contain | |
* @param {Object|String|Number} obj | |
* @param {String} message _optional_ | |
* @api public | |
*/ | |
function includeChainingBehavior () { | |
flag(this, 'contains', true); | |
} | |
function include (val, msg) { | |
if (msg) flag(this, 'message', msg); | |
var obj = flag(this, 'object') | |
this.assert( | |
~obj.indexOf(val) | |
, 'expected #{this} to include ' + _.inspect(val) | |
, 'expected #{this} to not include ' + _.inspect(val)); | |
} | |
Assertion.addChainableMethod('include', include, includeChainingBehavior); | |
Assertion.addChainableMethod('contain', include, includeChainingBehavior); | |
/** | |
* ### .ok | |
* | |
* Asserts that the target is truthy. | |
* | |
* expect('everthing').to.be.ok; | |
* expect(1).to.be.ok; | |
* expect(false).to.not.be.ok; | |
* expect(undefined).to.not.be.ok; | |
* expect(null).to.not.be.ok; | |
* | |
* @name ok | |
* @api public | |
*/ | |
Assertion.addProperty('ok', function () { | |
this.assert( | |
flag(this, 'object') | |
, 'expected #{this} to be truthy' | |
, 'expected #{this} to be falsy'); | |
}); | |
/** | |
* ### .true | |
* | |
* Asserts that the target is `true`. | |
* | |
* expect(true).to.be.true; | |
* expect(1).to.not.be.true; | |
* | |
* @name true | |
* @api public | |
*/ | |
Assertion.addProperty('true', function () { | |
this.assert( | |
true === flag(this, 'object') | |
, 'expected #{this} to be true' | |
, 'expected #{this} to be false' | |
, this.negate ? false : true | |
); | |
}); | |
/** | |
* ### .false | |
* | |
* Asserts that the target is `false`. | |
* | |
* expect(false).to.be.false; | |
* expect(0).to.not.be.false; | |
* | |
* @name false | |
* @api public | |
*/ | |
Assertion.addProperty('false', function () { | |
this.assert( | |
false === flag(this, 'object') | |
, 'expected #{this} to be false' | |
, 'expected #{this} to be true' | |
, this.negate ? true : false | |
); | |
}); | |
/** | |
* ### .null | |
* | |
* Asserts that the target is `null`. | |
* | |
* expect(null).to.be.null; | |
* expect(undefined).not.to.be.null; | |
* | |
* @name null | |
* @api public | |
*/ | |
Assertion.addProperty('null', function () { | |
this.assert( | |
null === flag(this, 'object') | |
, 'expected #{this} to be null' | |
, 'expected #{this} not to be null' | |
); | |
}); | |
/** | |
* ### .undefined | |
* | |
* Asserts that the target is `undefined`. | |
* | |
* expect(undefined).to.be.undefined; | |
* expect(null).to.not.be.undefined; | |
* | |
* @name undefined | |
* @api public | |
*/ | |
Assertion.addProperty('undefined', function () { | |
this.assert( | |
undefined === flag(this, 'object') | |
, 'expected #{this} to be undefined' | |
, 'expected #{this} not to be undefined' | |
); | |
}); | |
/** | |
* ### .exist | |
* | |
* Asserts that the target is neither `null` nor `undefined`. | |
* | |
* var foo = 'hi' | |
* , bar = null | |
* , baz; | |
* | |
* expect(foo).to.exist; | |
* expect(bar).to.not.exist; | |
* expect(baz).to.not.exist; | |
* | |
* @name exist | |
* @api public | |
*/ | |
Assertion.addProperty('exist', function () { | |
this.assert( | |
null != flag(this, 'object') | |
, 'expected #{this} to exist' | |
, 'expected #{this} to not exist' | |
); | |
}); | |
/** | |
* ### .empty | |
* | |
* Asserts that the target's length is `0`. For arrays, it checks | |
* the `length` property. For objects, it gets the count of | |
* enumerable keys. | |
* | |
* expect([]).to.be.empty; | |
* expect('').to.be.empty; | |
* expect({}).to.be.empty; | |
* | |
* @name empty | |
* @api public | |
*/ | |
Assertion.addProperty('empty', function () { | |
var obj = flag(this, 'object') | |
, expected = obj; | |
if (Array.isArray(obj) || 'string' === typeof object) { | |
expected = obj.length; | |
} else if (typeof obj === 'object') { | |
expected = Object.keys(obj).length; | |
} | |
this.assert( | |
!expected | |
, 'expected #{this} to be empty' | |
, 'expected #{this} not to be empty' | |
); | |
}); | |
/** | |
* ### .arguments | |
* | |
* Asserts that the target is an arguments object. | |
* | |
* function test () { | |
* expect(arguments).to.be.arguments; | |
* } | |
* | |
* @name arguments | |
* @alias Arguments | |
* @api public | |
*/ | |
function checkArguments () { | |
var obj = flag(this, 'object') | |
, type = Object.prototype.toString.call(obj); | |
this.assert( | |
'[object Arguments]' === type | |
, 'expected #{this} to be arguments but got ' + type | |
, 'expected #{this} to not be arguments' | |
); | |
} | |
Assertion.addProperty('arguments', checkArguments); | |
Assertion.addProperty('Arguments', checkArguments); | |
/** | |
* ### .equal(value) | |
* | |
* Asserts that the target is strictly equal (`===`) to `value`. | |
* Alternately, if the `deep` flag is set, asserts that | |
* the target is deeply equal to `value`. | |
* | |
* expect('hello').to.equal('hello'); | |
* expect(42).to.equal(42); | |
* expect(1).to.not.equal(true); | |
* expect({ foo: 'bar' }).to.not.equal({ foo: 'bar' }); | |
* expect({ foo: 'bar' }).to.deep.equal({ foo: 'bar' }); | |
* | |
* @name equal | |
* @alias equals | |
* @alias eq | |
* @alias deep.equal | |
* @param {Mixed} value | |
* @param {String} message _optional_ | |
* @api public | |
*/ | |
function assertEqual (val, msg) { | |
if (msg) flag(this, 'message', msg); | |
var obj = flag(this, 'object'); | |
if (flag(this, 'deep')) { | |
return this.eql(val); | |
} else { | |
this.assert( | |
val === obj | |
, 'expected #{this} to equal #{exp}' | |
, 'expected #{this} to not equal #{exp}' | |
, val | |
, this._obj | |
, true | |
); | |
} | |
} | |
Assertion.addMethod('equal', assertEqual); | |
Assertion.addMethod('equals', assertEqual); | |
Assertion.addMethod('eq', assertEqual); | |
/** | |
* ### .eql(value) | |
* | |
* Asserts that the target is deeply equal to `value`. | |
* | |
* expect({ foo: 'bar' }).to.eql({ foo: 'bar' }); | |
* expect([ 1, 2, 3 ]).to.eql([ 1, 2, 3 ]); | |
* | |
* @name eql | |
* @alias eqls | |
* @param {Mixed} value | |
* @param {String} message _optional_ | |
* @api public | |
*/ | |
function assertEql(obj, msg) { | |
if (msg) flag(this, 'message', msg); | |
this.assert( | |
_.eql(obj, flag(this, 'object')) | |
, 'expected #{this} to deeply equal #{exp}' | |
, 'expected #{this} to not deeply equal #{exp}' | |
, obj | |
, this._obj | |
, true | |
); | |
} | |
Assertion.addMethod('eql', assertEql); | |
Assertion.addMethod('eqls', assertEql); | |
/** | |
* ### .above(value) | |
* | |
* Asserts that the target is greater than `value`. | |
* | |
* expect(10).to.be.above(5); | |
* | |
* Can also be used in conjunction with `length` to | |
* assert a minimum length. The benefit being a | |
* more informative error message than if the length | |
* was supplied directly. | |
* | |
* expect('foo').to.have.length.above(2); | |
* expect([ 1, 2, 3 ]).to.have.length.above(2); | |
* | |
* @name above | |
* @alias gt | |
* @alias greaterThan | |
* @param {Number} value | |
* @param {String} message _optional_ | |
* @api public | |
*/ | |
function assertAbove (n, msg) { | |
if (msg) flag(this, 'message', msg); | |
var obj = flag(this, 'object'); | |
if (flag(this, 'doLength')) { | |
new Assertion(obj, msg).to.have.property('length'); | |
var len = obj.length; | |
this.assert( | |
len > n | |
, 'expected #{this} to have a length above #{exp} but got #{act}' | |
, 'expected #{this} to not have a length above #{exp}' | |
, n | |
, len | |
); | |
} else { | |
this.assert( | |
obj > n | |
, 'expected #{this} to be above ' + n | |
, 'expected #{this} to be at most ' + n | |
); | |
} | |
} | |
Assertion.addMethod('above', assertAbove); | |
Assertion.addMethod('gt', assertAbove); | |
Assertion.addMethod('greaterThan', assertAbove); | |
/** | |
* ### .least(value) | |
* | |
* Asserts that the target is greater than or equal to `value`. | |
* | |
* expect(10).to.be.at.least(10); | |
* | |
* Can also be used in conjunction with `length` to | |
* assert a minimum length. The benefit being a | |
* more informative error message than if the length | |
* was supplied directly. | |
* | |
* expect('foo').to.have.length.of.at.least(2); | |
* expect([ 1, 2, 3 ]).to.have.length.of.at.least(3); | |
* | |
* @name least | |
* @alias gte | |
* @param {Number} value | |
* @param {String} message _optional_ | |
* @api public | |
*/ | |
function assertLeast (n, msg) { | |
if (msg) flag(this, 'message', msg); | |
var obj = flag(this, 'object'); | |
if (flag(this, 'doLength')) { | |
new Assertion(obj, msg).to.have.property('length'); | |
var len = obj.length; | |
this.assert( | |
len >= n | |
, 'expected #{this} to have a length at least #{exp} but got #{act}' | |
, 'expected #{this} to have a length below #{exp}' | |
, n | |
, len | |
); | |
} else { | |
this.assert( | |
obj >= n | |
, 'expected #{this} to be at least ' + n | |
, 'expected #{this} to be below ' + n | |
); | |
} | |
} | |
Assertion.addMethod('least', assertLeast); | |
Assertion.addMethod('gte', assertLeast); | |
/** | |
* ### .below(value) | |
* | |
* Asserts that the target is less than `value`. | |
* | |
* expect(5).to.be.below(10); | |
* | |
* Can also be used in conjunction with `length` to | |
* assert a maximum length. The benefit being a | |
* more informative error message than if the length | |
* was supplied directly. | |
* | |
* expect('foo').to.have.length.below(4); | |
* expect([ 1, 2, 3 ]).to.have.length.below(4); | |
* | |
* @name below | |
* @alias lt | |
* @alias lessThan | |
* @param {Number} value | |
* @param {String} message _optional_ | |
* @api public | |
*/ | |
function assertBelow (n, msg) { | |
if (msg) flag(this, 'message', msg); | |
var obj = flag(this, 'object'); | |
if (flag(this, 'doLength')) { | |
new Assertion(obj, msg).to.have.property('length'); | |
var len = obj.length; | |
this.assert( | |
len < n | |
, 'expected #{this} to have a length below #{exp} but got #{act}' | |
, 'expected #{this} to not have a length below #{exp}' | |
, n | |
, len | |
); | |
} else { | |
this.assert( | |
obj < n | |
, 'expected #{this} to be below ' + n | |
, 'expected #{this} to be at least ' + n | |
); | |
} | |
} | |
Assertion.addMethod('below', assertBelow); | |
Assertion.addMethod('lt', assertBelow); | |
Assertion.addMethod('lessThan', assertBelow); | |
/** | |
* ### .most(value) | |
* | |
* Asserts that the target is less than or equal to `value`. | |
* | |
* expect(5).to.be.at.most(5); | |
* | |
* Can also be used in conjunction with `length` to | |
* assert a maximum length. The benefit being a | |
* more informative error message than if the length | |
* was supplied directly. | |
* | |
* expect('foo').to.have.length.of.at.most(4); | |
* expect([ 1, 2, 3 ]).to.have.length.of.at.most(3); | |
* | |
* @name most | |
* @alias lte | |
* @param {Number} value | |
* @param {String} message _optional_ | |
* @api public | |
*/ | |
function assertMost (n, msg) { | |
if (msg) flag(this, 'message', msg); | |
var obj = flag(this, 'object'); | |
if (flag(this, 'doLength')) { | |
new Assertion(obj, msg).to.have.property('length'); | |
var len = obj.length; | |
this.assert( | |
len <= n | |
, 'expected #{this} to have a length at most #{exp} but got #{act}' | |
, 'expected #{this} to have a length above #{exp}' | |
, n | |
, len | |
); | |
} else { | |
this.assert( | |
obj <= n | |
, 'expected #{this} to be at most ' + n | |
, 'expected #{this} to be above ' + n | |
); | |
} | |
} | |
Assertion.addMethod('most', assertMost); | |
Assertion.addMethod('lte', assertMost); | |
/** | |
* ### .within(start, finish) | |
* | |
* Asserts that the target is within a range. | |
* | |
* expect(7).to.be.within(5,10); | |
* | |
* Can also be used in conjunction with `length` to | |
* assert a length range. The benefit being a | |
* more informative error message than if the length | |
* was supplied directly. | |
* | |
* expect('foo').to.have.length.within(2,4); | |
* expect([ 1, 2, 3 ]).to.have.length.within(2,4); | |
* | |
* @name within | |
* @param {Number} start lowerbound inclusive | |
* @param {Number} finish upperbound inclusive | |
* @param {String} message _optional_ | |
* @api public | |
*/ | |
Assertion.addMethod('within', function (start, finish, msg) { | |
if (msg) flag(this, 'message', msg); | |
var obj = flag(this, 'object') | |
, range = start + '..' + finish; | |
if (flag(this, 'doLength')) { | |
new Assertion(obj, msg).to.have.property('length'); | |
var len = obj.length; | |
this.assert( | |
len >= start && len <= finish | |
, 'expected #{this} to have a length within ' + range | |
, 'expected #{this} to not have a length within ' + range | |
); | |
} else { | |
this.assert( | |
obj >= start && obj <= finish | |
, 'expected #{this} to be within ' + range | |
, 'expected #{this} to not be within ' + range | |
); | |
} | |
}); | |
/** | |
* ### .instanceof(constructor) | |
* | |
* Asserts that the target is an instance of `constructor`. | |
* | |
* var Tea = function (name) { this.name = name; } | |
* , Chai = new Tea('chai'); | |
* | |
* expect(Chai).to.be.an.instanceof(Tea); | |
* expect([ 1, 2, 3 ]).to.be.instanceof(Array); | |
* | |
* @name instanceof | |
* @param {Constructor} constructor | |
* @param {String} message _optional_ | |
* @alias instanceOf | |
* @api public | |
*/ | |
function assertInstanceOf (constructor, msg) { | |
if (msg) flag(this, 'message', msg); | |
var name = _.getName(constructor); | |
this.assert( | |
flag(this, 'object') instanceof constructor | |
, 'expected #{this} to be an instance of ' + name | |
, 'expected #{this} to not be an instance of ' + name | |
); | |
}; | |
Assertion.addMethod('instanceof', assertInstanceOf); | |
Assertion.addMethod('instanceOf', assertInstanceOf); | |
/** | |
* ### .property(name, [value]) | |
* | |
* Asserts that the target has a property `name`, optionally asserting that | |
* the value of that property is strictly equal to `value`. | |
* If the `deep` flag is set, you can use dot- and bracket-notation for deep | |
* references into objects and arrays. | |
* | |
* // simple referencing | |
* var obj = { foo: 'bar' }; | |
* expect(obj).to.have.property('foo'); | |
* expect(obj).to.have.property('foo', 'bar'); | |
* | |
* // deep referencing | |
* var deepObj = { | |
* green: { tea: 'matcha' } | |
* , teas: [ 'chai', 'matcha', { tea: 'konacha' } ] | |
* }; | |
* expect(deepObj).to.have.deep.property('green.tea', 'matcha'); | |
* expect(deepObj).to.have.deep.property('teas[1]', 'matcha'); | |
* expect(deepObj).to.have.deep.property('teas[2].tea', 'konacha'); | |
* | |
* You can also use an array as the starting point of a `deep.property` | |
* assertion, or traverse nested arrays. | |
* | |
* var arr = [ | |
* [ 'chai', 'matcha', 'konacha' ] | |
* , [ { tea: 'chai' } | |
* , { tea: 'matcha' } | |
* , { tea: 'konacha' } ] | |
* ]; | |
* | |
* expect(arr).to.have.deep.property('[0][1]', 'matcha'); | |
* expect(arr).to.have.deep.property('[1][2].tea', 'konacha'); | |
* | |
* Furthermore, `property` changes the subject of the assertion | |
* to be the value of that property from the original object. This | |
* permits for further chainable assertions on that property. | |
* | |
* expect(obj).to.have.property('foo') | |
* .that.is.a('string'); | |
* expect(deepObj).to.have.property('green') | |
* .that.is.an('object') | |
* .that.deep.equals({ tea: 'matcha' }); | |
* expect(deepObj).to.have.property('teas') | |
* .that.is.an('array') | |
* .with.deep.property('[2]') | |
* .that.deep.equals({ tea: 'konacha' }); | |
* | |
* @name property | |
* @alias deep.property | |
* @param {String} name | |
* @param {Mixed} value (optional) | |
* @param {String} message _optional_ | |
* @returns value of property for chaining | |
* @api public | |
*/ | |
Assertion.addMethod('property', function (name, val, msg) { | |
if (msg) flag(this, 'message', msg); | |
var descriptor = flag(this, 'deep') ? 'deep property ' : 'property ' | |
, negate = flag(this, 'negate') | |
, obj = flag(this, 'object') | |
, value = flag(this, 'deep') | |
? _.getPathValue(name, obj) | |
: obj[name]; | |
if (negate && undefined !== val) { | |
if (undefined === value) { | |
msg = (msg != null) ? msg + ': ' : ''; | |
throw new Error(msg + _.inspect(obj) + ' has no ' + descriptor + _.inspect(name)); | |
} | |
} else { | |
this.assert( | |
undefined !== value | |
, 'expected #{this} to have a ' + descriptor + _.inspect(name) | |
, 'expected #{this} to not have ' + descriptor + _.inspect(name)); | |
} | |
if (undefined !== val) { | |
this.assert( | |
val === value | |
, 'expected #{this} to have a ' + descriptor + _.inspect(name) + ' of #{exp}, but got #{act}' | |
, 'expected #{this} to not have a ' + descriptor + _.inspect(name) + ' of #{act}' | |
, val | |
, value | |
); | |
} | |
flag(this, 'object', value); | |
}); | |
/** | |
* ### .ownProperty(name) | |
* | |
* Asserts that the target has an own property `name`. | |
* | |
* expect('test').to.have.ownProperty('length'); | |
* | |
* @name ownProperty | |
* @alias haveOwnProperty | |
* @param {String} name | |
* @param {String} message _optional_ | |
* @api public | |
*/ | |
function assertOwnProperty (name, msg) { | |
if (msg) flag(this, 'message', msg); | |
var obj = flag(this, 'object'); | |
this.assert( | |
obj.hasOwnProperty(name) | |
, 'expected #{this} to have own property ' + _.inspect(name) | |
, 'expected #{this} to not have own property ' + _.inspect(name) | |
); | |
} | |
Assertion.addMethod('ownProperty', assertOwnProperty); | |
Assertion.addMethod('haveOwnProperty', assertOwnProperty); | |
/** | |
* ### .length(value) | |
* | |
* Asserts that the target's `length` property has | |
* the expected value. | |
* | |
* expect([ 1, 2, 3]).to.have.length(3); | |
* expect('foobar').to.have.length(6); | |
* | |
* Can also be used as a chain precursor to a value | |
* comparison for the length property. | |
* | |
* expect('foo').to.have.length.above(2); | |
* expect([ 1, 2, 3 ]).to.have.length.above(2); | |
* expect('foo').to.have.length.below(4); | |
* expect([ 1, 2, 3 ]).to.have.length.below(4); | |
* expect('foo').to.have.length.within(2,4); | |
* expect([ 1, 2, 3 ]).to.have.length.within(2,4); | |
* | |
* @name length | |
* @alias lengthOf | |
* @param {Number} length | |
* @param {String} message _optional_ | |
* @api public | |
*/ | |
function assertLengthChain () { | |
flag(this, 'doLength', true); | |
} | |
function assertLength (n, msg) { | |
if (msg) flag(this, 'message', msg); | |
var obj = flag(this, 'object'); | |
new Assertion(obj, msg).to.have.property('length'); | |
var len = obj.length; | |
this.assert( | |
len == n | |
, 'expected #{this} to have a length of #{exp} but got #{act}' | |
, 'expected #{this} to not have a length of #{act}' | |
, n | |
, len | |
); | |
} | |
Assertion.addChainableMethod('length', assertLength, assertLengthChain); | |
Assertion.addMethod('lengthOf', assertLength, assertLengthChain); | |
/** | |
* ### .match(regexp) | |
* | |
* Asserts that the target matches a regular expression. | |
* | |
* expect('foobar').to.match(/^foo/); | |
* | |
* @name match | |
* @param {RegExp} RegularExpression | |
* @param {String} message _optional_ | |
* @api public | |
*/ | |
Assertion.addMethod('match', function (re, msg) { | |
if (msg) flag(this, 'message', msg); | |
var obj = flag(this, 'object'); | |
this.assert( | |
re.exec(obj) | |
, 'expected #{this} to match ' + re | |
, 'expected #{this} not to match ' + re | |
); | |
}); | |
/** | |
* ### .string(string) | |
* | |
* Asserts that the string target contains another string. | |
* | |
* expect('foobar').to.have.string('bar'); | |
* | |
* @name string | |
* @param {String} string | |
* @param {String} message _optional_ | |
* @api public | |
*/ | |
Assertion.addMethod('string', function (str, msg) { | |
if (msg) flag(this, 'message', msg); | |
var obj = flag(this, 'object'); | |
new Assertion(obj, msg).is.a('string'); | |
this.assert( | |
~obj.indexOf(str) | |
, 'expected #{this} to contain ' + _.inspect(str) | |
, 'expected #{this} to not contain ' + _.inspect(str) | |
); | |
}); | |
/** | |
* ### .keys(key1, [key2], [...]) | |
* | |
* Asserts that the target has exactly the given keys, or | |
* asserts the inclusion of some keys when using the | |
* `include` or `contain` modifiers. | |
* | |
* expect({ foo: 1, bar: 2 }).to.have.keys(['foo', 'bar']); | |
* expect({ foo: 1, bar: 2, baz: 3 }).to.contain.keys('foo', 'bar'); | |
* | |
* @name keys | |
* @alias key | |
* @param {String...|Array} keys | |
* @api public | |
*/ | |
function assertKeys (keys) { | |
var obj = flag(this, 'object') | |
, str | |
, ok = true; | |
keys = keys instanceof Array | |
? keys | |
: Array.prototype.slice.call(arguments); | |
if (!keys.length) throw new Error('keys required'); | |
var actual = Object.keys(obj) | |
, len = keys.length; | |
// Inclusion | |
ok = keys.every(function(key){ | |
return ~actual.indexOf(key); | |
}); | |
// Strict | |
if (!flag(this, 'negate') && !flag(this, 'contains')) { | |
ok = ok && keys.length == actual.length; | |
} | |
// Key string | |
if (len > 1) { | |
keys = keys.map(function(key){ | |
return _.inspect(key); | |
}); | |
var last = keys.pop(); | |
str = keys.join(', ') + ', and ' + last; | |
} else { | |
str = _.inspect(keys[0]); | |
} | |
// Form | |
str = (len > 1 ? 'keys ' : 'key ') + str; | |
// Have / include | |
str = (flag(this, 'contains') ? 'contain ' : 'have ') + str; | |
// Assertion | |
this.assert( | |
ok | |
, 'expected #{this} to ' + str | |
, 'expected #{this} to not ' + str | |
); | |
} | |
Assertion.addMethod('keys', assertKeys); | |
Assertion.addMethod('key', assertKeys); | |
/** | |
* ### .throw(constructor) | |
* | |
* Asserts that the function target will throw a specific error, or specific type of error | |
* (as determined using `instanceof`), optionally with a RegExp or string inclusion test | |
* for the error's message. | |
* | |
* var err = new ReferenceError('This is a bad function.'); | |
* var fn = function () { throw err; } | |
* expect(fn).to.throw(ReferenceError); | |
* expect(fn).to.throw(Error); | |
* expect(fn).to.throw(/bad function/); | |
* expect(fn).to.not.throw('good function'); | |
* expect(fn).to.throw(ReferenceError, /bad function/); | |
* expect(fn).to.throw(err); | |
* expect(fn).to.not.throw(new RangeError('Out of range.')); | |
* | |
* Please note that when a throw expectation is negated, it will check each | |
* parameter independently, starting with error constructor type. The appropriate way | |
* to check for the existence of a type of error but for a message that does not match | |
* is to use `and`. | |
* | |
* expect(fn).to.throw(ReferenceError) | |
* .and.not.throw(/good function/); | |
* | |
* @name throw | |
* @alias throws | |
* @alias Throw | |
* @param {ErrorConstructor} constructor | |
* @param {String|RegExp} expected error message | |
* @param {String} message _optional_ | |
* @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error#Error_types | |
* @api public | |
*/ | |
function assertThrows (constructor, errMsg, msg) { | |
if (msg) flag(this, 'message', msg); | |
var obj = flag(this, 'object'); | |
new Assertion(obj, msg).is.a('function'); | |
var thrown = false | |
, desiredError = null | |
, name = null | |
, thrownError = null; | |
if (arguments.length === 0) { | |
errMsg = null; | |
constructor = null; | |
} else if (constructor && (constructor instanceof RegExp || 'string' === typeof constructor)) { | |
errMsg = constructor; | |
constructor = null; | |
} else if (constructor && constructor instanceof Error) { | |
desiredError = constructor; | |
constructor = null; | |
errMsg = null; | |
} else if (typeof constructor === 'function') { | |
name = (new constructor()).name; | |
} else { | |
constructor = null; | |
} | |
try { | |
obj(); | |
} catch (err) { | |
// first, check desired error | |
if (desiredError) { | |
this.assert( | |
err === desiredError | |
, 'expected #{this} to throw #{exp} but #{act} was thrown' | |
, 'expected #{this} to not throw #{exp}' | |
, desiredError | |
, err | |
); | |
return this; | |
} | |
// next, check constructor | |
if (constructor) { | |
this.assert( | |
err instanceof constructor | |
, 'expected #{this} to throw #{exp} but #{act} was thrown' | |
, 'expected #{this} to not throw #{exp} but #{act} was thrown' | |
, name | |
, err | |
); | |
if (!errMsg) return this; | |
} | |
// next, check message | |
var message = 'object' === _.type(err) && "message" in err | |
? err.message | |
: '' + err; | |
if ((message != null) && errMsg && errMsg instanceof RegExp) { | |
this.assert( | |
errMsg.exec(message) | |
, 'expected #{this} to throw error matching #{exp} but got #{act}' | |
, 'expected #{this} to throw error not matching #{exp}' | |
, errMsg | |
, message | |
); | |
return this; | |
} else if ((message != null) && errMsg && 'string' === typeof errMsg) { | |
this.assert( | |
~message.indexOf(errMsg) | |
, 'expected #{this} to throw error including #{exp} but got #{act}' | |
, 'expected #{this} to throw error not including #{act}' | |
, errMsg | |
, message | |
); | |
return this; | |
} else { | |
thrown = true; | |
thrownError = err; | |
} | |
} | |
var actuallyGot = '' | |
, expectedThrown = name !== null | |
? name | |
: desiredError | |
? '#{exp}' //_.inspect(desiredError) | |
: 'an error'; | |
if (thrown) { | |
actuallyGot = ' but #{act} was thrown' | |
} | |
this.assert( | |
thrown === true | |
, 'expected #{this} to throw ' + expectedThrown + actuallyGot | |
, 'expected #{this} to not throw ' + expectedThrown + actuallyGot | |
, desiredError | |
, thrownError | |
); | |
}; | |
Assertion.addMethod('throw', assertThrows); | |
Assertion.addMethod('throws', assertThrows); | |
Assertion.addMethod('Throw', assertThrows); | |
/** | |
* ### .respondTo(method) | |
* | |
* Asserts that the object or class target will respond to a method. | |
* | |
* Klass.prototype.bar = function(){}; | |
* expect(Klass).to.respondTo('bar'); | |
* expect(obj).to.respondTo('bar'); | |
* | |
* To check if a constructor will respond to a static function, | |
* set the `itself` flag. | |
* | |
* Klass.baz = function(){}; | |
* expect(Klass).itself.to.respondTo('baz'); | |
* | |
* @name respondTo | |
* @param {String} method | |
* @param {String} message _optional_ | |
* @api public | |
*/ | |
Assertion.addMethod('respondTo', function (method, msg) { | |
if (msg) flag(this, 'message', msg); | |
var obj = flag(this, 'object') | |
, itself = flag(this, 'itself') | |
, context = ('function' === _.type(obj) && !itself) | |
? obj.prototype[method] | |
: obj[method]; | |
this.assert( | |
'function' === typeof context | |
, 'expected #{this} to respond to ' + _.inspect(method) | |
, 'expected #{this} to not respond to ' + _.inspect(method) | |
); | |
}); | |
/** | |
* ### .itself | |
* | |
* Sets the `itself` flag, later used by the `respondTo` assertion. | |
* | |
* function Foo() {} | |
* Foo.bar = function() {} | |
* Foo.prototype.baz = function() {} | |
* | |
* expect(Foo).itself.to.respondTo('bar'); | |
* expect(Foo).itself.not.to.respondTo('baz'); | |
* | |
* @name itself | |
* @api public | |
*/ | |
Assertion.addProperty('itself', function () { | |
flag(this, 'itself', true); | |
}); | |
/** | |
* ### .satisfy(method) | |
* | |
* Asserts that the target passes a given truth test. | |
* | |
* expect(1).to.satisfy(function(num) { return num > 0; }); | |
* | |
* @name satisfy | |
* @param {Function} matcher | |
* @param {String} message _optional_ | |
* @api public | |
*/ | |
Assertion.addMethod('satisfy', function (matcher, msg) { | |
if (msg) flag(this, 'message', msg); | |
var obj = flag(this, 'object'); | |
this.assert( | |
matcher(obj) | |
, 'expected #{this} to satisfy ' + _.objDisplay(matcher) | |
, 'expected #{this} to not satisfy' + _.objDisplay(matcher) | |
, this.negate ? false : true | |
, matcher(obj) | |
); | |
}); | |
/** | |
* ### .closeTo(expected, delta) | |
* | |
* Asserts that the target is equal `expected`, to within a +/- `delta` range. | |
* | |
* expect(1.5).to.be.closeTo(1, 0.5); | |
* | |
* @name closeTo | |
* @param {Number} expected | |
* @param {Number} delta | |
* @param {String} message _optional_ | |
* @api public | |
*/ | |
Assertion.addMethod('closeTo', function (expected, delta, msg) { | |
if (msg) flag(this, 'message', msg); | |
var obj = flag(this, 'object'); | |
this.assert( | |
Math.abs(obj - expected) <= delta | |
, 'expected #{this} to be close to ' + expected + ' +/- ' + delta | |
, 'expected #{this} not to be close to ' + expected + ' +/- ' + delta | |
); | |
}); | |
function isSubsetOf(subset, superset) { | |
return subset.every(function(elem) { | |
return superset.indexOf(elem) !== -1; | |
}) | |
} | |
/** | |
* ### .members(set) | |
* | |
* Asserts that the target is a superset of `set`, | |
* or that the target and `set` have the same members. | |
* | |
* expect([1, 2, 3]).to.include.members([3, 2]); | |
* expect([1, 2, 3]).to.not.include.members([3, 2, 8]); | |
* | |
* expect([4, 2]).to.have.members([2, 4]); | |
* expect([5, 2]).to.not.have.members([5, 2, 1]); | |
* | |
* @name members | |
* @param {Array} set | |
* @param {String} message _optional_ | |
* @api public | |
*/ | |
Assertion.addMethod('members', function (subset, msg) { | |
if (msg) flag(this, 'message', msg); | |
var obj = flag(this, 'object'); | |
new Assertion(obj).to.be.an('array'); | |
new Assertion(subset).to.be.an('array'); | |
if (flag(this, 'contains')) { | |
return this.assert( | |
isSubsetOf(subset, obj) | |
, 'expected #{this} to be a superset of #{act}' | |
, 'expected #{this} to not be a superset of #{act}' | |
, obj | |
, subset | |
); | |
} | |
this.assert( | |
isSubsetOf(obj, subset) && isSubsetOf(subset, obj) | |
, 'expected #{this} to have the same members as #{act}' | |
, 'expected #{this} to not have the same members as #{act}' | |
, obj | |
, subset | |
); | |
}); | |
}; | |
},{}],53:[function(require,module,exports){ | |
/*! | |
* chai | |
* Copyright(c) 2011-2013 Jake Luer <jake@alogicalparadox.com> | |
* MIT Licensed | |
*/ | |
module.exports = function (chai, util) { | |
/*! | |
* Chai dependencies. | |
*/ | |
var Assertion = chai.Assertion | |
, flag = util.flag; | |
/*! | |
* Module export. | |
*/ | |
/** | |
* ### assert(expression, message) | |
* | |
* Write your own test expressions. | |
* | |
* assert('foo' !== 'bar', 'foo is not bar'); | |
* assert(Array.isArray([]), 'empty arrays are arrays'); | |
* | |
* @param {Mixed} expression to test for truthiness | |
* @param {String} message to display on error | |
* @name assert | |
* @api public | |
*/ | |
var assert = chai.assert = function (express, errmsg) { | |
var test = new Assertion(null); | |
test.assert( | |
express | |
, errmsg | |
, '[ negation message unavailable ]' | |
); | |
}; | |
/** | |
* ### .fail(actual, expected, [message], [operator]) | |
* | |
* Throw a failure. Node.js `assert` module-compatible. | |
* | |
* @name fail | |
* @param {Mixed} actual | |
* @param {Mixed} expected | |
* @param {String} message | |
* @param {String} operator | |
* @api public | |
*/ | |
assert.fail = function (actual, expected, message, operator) { | |
throw new chai.AssertionError({ | |
actual: actual | |
, expected: expected | |
, message: message | |
, operator: operator | |
, stackStartFunction: assert.fail | |
}); | |
}; | |
/** | |
* ### .ok(object, [message]) | |
* | |
* Asserts that `object` is truthy. | |
* | |
* assert.ok('everything', 'everything is ok'); | |
* assert.ok(false, 'this will fail'); | |
* | |
* @name ok | |
* @param {Mixed} object to test | |
* @param {String} message | |
* @api public | |
*/ | |
assert.ok = function (val, msg) { | |
new Assertion(val, msg).is.ok; | |
}; | |
/** | |
* ### .notOk(object, [message]) | |
* | |
* Asserts that `object` is falsy. | |
* | |
* assert.notOk('everything', 'this will fail'); | |
* assert.notOk(false, 'this will pass'); | |
* | |
* @name notOk | |
* @param {Mixed} object to test | |
* @param {String} message | |
* @api public | |
*/ | |
assert.notOk = function (val, msg) { | |
new Assertion(val, msg).is.not.ok; | |
}; | |
/** | |
* ### .equal(actual, expected, [message]) | |
* | |
* Asserts non-strict equality (`==`) of `actual` and `expected`. | |
* | |
* assert.equal(3, '3', '== coerces values to strings'); | |
* | |
* @name equal | |
* @param {Mixed} actual | |
* @param {Mixed} expected | |
* @param {String} message | |
* @api public | |
*/ | |
assert.equal = function (act, exp, msg) { | |
var test = new Assertion(act, msg); | |
test.assert( | |
exp == flag(test, 'object') | |
, 'expected #{this} to equal #{exp}' | |
, 'expected #{this} to not equal #{act}' | |
, exp | |
, act | |
); | |
}; | |
/** | |
* ### .notEqual(actual, expected, [message]) | |
* | |
* Asserts non-strict inequality (`!=`) of `actual` and `expected`. | |
* | |
* assert.notEqual(3, 4, 'these numbers are not equal'); | |
* | |
* @name notEqual | |
* @param {Mixed} actual | |
* @param {Mixed} expected | |
* @param {String} message | |
* @api public | |
*/ | |
assert.notEqual = function (act, exp, msg) { | |
var test = new Assertion(act, msg); | |
test.assert( | |
exp != flag(test, 'object') | |
, 'expected #{this} to not equal #{exp}' | |
, 'expected #{this} to equal #{act}' | |
, exp | |
, act | |
); | |
}; | |
/** | |
* ### .strictEqual(actual, expected, [message]) | |
* | |
* Asserts strict equality (`===`) of `actual` and `expected`. | |
* | |
* assert.strictEqual(true, true, 'these booleans are strictly equal'); | |
* | |
* @name strictEqual | |
* @param {Mixed} actual | |
* @param {Mixed} expected | |
* @param {String} message | |
* @api public | |
*/ | |
assert.strictEqual = function (act, exp, msg) { | |
new Assertion(act, msg).to.equal(exp); | |
}; | |
/** | |
* ### .notStrictEqual(actual, expected, [message]) | |
* | |
* Asserts strict inequality (`!==`) of `actual` and `expected`. | |
* | |
* assert.notStrictEqual(3, '3', 'no coercion for strict equality'); | |
* | |
* @name notStrictEqual | |
* @param {Mixed} actual | |
* @param {Mixed} expected | |
* @param {String} message | |
* @api public | |
*/ | |
assert.notStrictEqual = function (act, exp, msg) { | |
new Assertion(act, msg).to.not.equal(exp); | |
}; | |
/** | |
* ### .deepEqual(actual, expected, [message]) | |
* | |
* Asserts that `actual` is deeply equal to `expected`. | |
* | |
* assert.deepEqual({ tea: 'green' }, { tea: 'green' }); | |
* | |
* @name deepEqual | |
* @param {Mixed} actual | |
* @param {Mixed} expected | |
* @param {String} message | |
* @api public | |
*/ | |
assert.deepEqual = function (act, exp, msg) { | |
new Assertion(act, msg).to.eql(exp); | |
}; | |
/** | |
* ### .notDeepEqual(actual, expected, [message]) | |
* | |
* Assert that `actual` is not deeply equal to `expected`. | |
* | |
* assert.notDeepEqual({ tea: 'green' }, { tea: 'jasmine' }); | |
* | |
* @name notDeepEqual | |
* @param {Mixed} actual | |
* @param {Mixed} expected | |
* @param {String} message | |
* @api public | |
*/ | |
assert.notDeepEqual = function (act, exp, msg) { | |
new Assertion(act, msg).to.not.eql(exp); | |
}; | |
/** | |
* ### .isTrue(value, [message]) | |
* | |
* Asserts that `value` is true. | |
* | |
* var teaServed = true; | |
* assert.isTrue(teaServed, 'the tea has been served'); | |
* | |
* @name isTrue | |
* @param {Mixed} value | |
* @param {String} message | |
* @api public | |
*/ | |
assert.isTrue = function (val, msg) { | |
new Assertion(val, msg).is['true']; | |
}; | |
/** | |
* ### .isFalse(value, [message]) | |
* | |
* Asserts that `value` is false. | |
* | |
* var teaServed = false; | |
* assert.isFalse(teaServed, 'no tea yet? hmm...'); | |
* | |
* @name isFalse | |
* @param {Mixed} value | |
* @param {String} message | |
* @api public | |
*/ | |
assert.isFalse = function (val, msg) { | |
new Assertion(val, msg).is['false']; | |
}; | |
/** | |
* ### .isNull(value, [message]) | |
* | |
* Asserts that `value` is null. | |
* | |
* assert.isNull(err, 'there was no error'); | |
* | |
* @name isNull | |
* @param {Mixed} value | |
* @param {String} message | |
* @api public | |
*/ | |
assert.isNull = function (val, msg) { | |
new Assertion(val, msg).to.equal(null); | |
}; | |
/** | |
* ### .isNotNull(value, [message]) | |
* | |
* Asserts that `value` is not null. | |
* | |
* var tea = 'tasty chai'; | |
* assert.isNotNull(tea, 'great, time for tea!'); | |
* | |
* @name isNotNull | |
* @param {Mixed} value | |
* @param {String} message | |
* @api public | |
*/ | |
assert.isNotNull = function (val, msg) { | |
new Assertion(val, msg).to.not.equal(null); | |
}; | |
/** | |
* ### .isUndefined(value, [message]) | |
* | |
* Asserts that `value` is `undefined`. | |
* | |
* var tea; | |
* assert.isUndefined(tea, 'no tea defined'); | |
* | |
* @name isUndefined | |
* @param {Mixed} value | |
* @param {String} message | |
* @api public | |
*/ | |
assert.isUndefined = function (val, msg) { | |
new Assertion(val, msg).to.equal(undefined); | |
}; | |
/** | |
* ### .isDefined(value, [message]) | |
* | |
* Asserts that `value` is not `undefined`. | |
* | |
* var tea = 'cup of chai'; | |
* assert.isDefined(tea, 'tea has been defined'); | |
* | |
* @name isDefined | |
* @param {Mixed} value | |
* @param {String} message | |
* @api public | |
*/ | |
assert.isDefined = function (val, msg) { | |
new Assertion(val, msg).to.not.equal(undefined); | |
}; | |
/** | |
* ### .isFunction(value, [message]) | |
* | |
* Asserts that `value` is a function. | |
* | |
* function serveTea() { return 'cup of tea'; }; | |
* assert.isFunction(serveTea, 'great, we can have tea now'); | |
* | |
* @name isFunction | |
* @param {Mixed} value | |
* @param {String} message | |
* @api public | |
*/ | |
assert.isFunction = function (val, msg) { | |
new Assertion(val, msg).to.be.a('function'); | |
}; | |
/** | |
* ### .isNotFunction(value, [message]) | |
* | |
* Asserts that `value` is _not_ a function. | |
* | |
* var serveTea = [ 'heat', 'pour', 'sip' ]; | |
* assert.isNotFunction(serveTea, 'great, we have listed the steps'); | |
* | |
* @name isNotFunction | |
* @param {Mixed} value | |
* @param {String} message | |
* @api public | |
*/ | |
assert.isNotFunction = function (val, msg) { | |
new Assertion(val, msg).to.not.be.a('function'); | |
}; | |
/** | |
* ### .isObject(value, [message]) | |
* | |
* Asserts that `value` is an object (as revealed by | |
* `Object.prototype.toString`). | |
* | |
* var selection = { name: 'Chai', serve: 'with spices' }; | |
* assert.isObject(selection, 'tea selection is an object'); | |
* | |
* @name isObject | |
* @param {Mixed} value | |
* @param {String} message | |
* @api public | |
*/ | |
assert.isObject = function (val, msg) { | |
new Assertion(val, msg).to.be.a('object'); | |
}; | |
/** | |
* ### .isNotObject(value, [message]) | |
* | |
* Asserts that `value` is _not_ an object. | |
* | |
* var selection = 'chai' | |
* assert.isObject(selection, 'tea selection is not an object'); | |
* assert.isObject(null, 'null is not an object'); | |
* | |
* @name isNotObject | |
* @param {Mixed} value | |
* @param {String} message | |
* @api public | |
*/ | |
assert.isNotObject = function (val, msg) { | |
new Assertion(val, msg).to.not.be.a('object'); | |
}; | |
/** | |
* ### .isArray(value, [message]) | |
* | |
* Asserts that `value` is an array. | |
* | |
* var menu = [ 'green', 'chai', 'oolong' ]; | |
* assert.isArray(menu, 'what kind of tea do we want?'); | |
* | |
* @name isArray | |
* @param {Mixed} value | |
* @param {String} message | |
* @api public | |
*/ | |
assert.isArray = function (val, msg) { | |
new Assertion(val, msg).to.be.an('array'); | |
}; | |
/** | |
* ### .isNotArray(value, [message]) | |
* | |
* Asserts that `value` is _not_ an array. | |
* | |
* var menu = 'green|chai|oolong'; | |
* assert.isNotArray(menu, 'what kind of tea do we want?'); | |
* | |
* @name isNotArray | |
* @param {Mixed} value | |
* @param {String} message | |
* @api public | |
*/ | |
assert.isNotArray = function (val, msg) { | |
new Assertion(val, msg).to.not.be.an('array'); | |
}; | |
/** | |
* ### .isString(value, [message]) | |
* | |
* Asserts that `value` is a string. | |
* | |
* var teaOrder = 'chai'; | |
* assert.isString(teaOrder, 'order placed'); | |
* | |
* @name isString | |
* @param {Mixed} value | |
* @param {String} message | |
* @api public | |
*/ | |
assert.isString = function (val, msg) { | |
new Assertion(val, msg).to.be.a('string'); | |
}; | |
/** | |
* ### .isNotString(value, [message]) | |
* | |
* Asserts that `value` is _not_ a string. | |
* | |
* var teaOrder = 4; | |
* assert.isNotString(teaOrder, 'order placed'); | |
* | |
* @name isNotString | |
* @param {Mixed} value | |
* @param {String} message | |
* @api public | |
*/ | |
assert.isNotString = function (val, msg) { | |
new Assertion(val, msg).to.not.be.a('string'); | |
}; | |
/** | |
* ### .isNumber(value, [message]) | |
* | |
* Asserts that `value` is a number. | |
* | |
* var cups = 2; | |
* assert.isNumber(cups, 'how many cups'); | |
* | |
* @name isNumber | |
* @param {Number} value | |
* @param {String} message | |
* @api public | |
*/ | |
assert.isNumber = function (val, msg) { | |
new Assertion(val, msg).to.be.a('number'); | |
}; | |
/** | |
* ### .isNotNumber(value, [message]) | |
* | |
* Asserts that `value` is _not_ a number. | |
* | |
* var cups = '2 cups please'; | |
* assert.isNotNumber(cups, 'how many cups'); | |
* | |
* @name isNotNumber | |
* @param {Mixed} value | |
* @param {String} message | |
* @api public | |
*/ | |
assert.isNotNumber = function (val, msg) { | |
new Assertion(val, msg).to.not.be.a('number'); | |
}; | |
/** | |
* ### .isBoolean(value, [message]) | |
* | |
* Asserts that `value` is a boolean. | |
* | |
* var teaReady = true | |
* , teaServed = false; | |
* | |
* assert.isBoolean(teaReady, 'is the tea ready'); | |
* assert.isBoolean(teaServed, 'has tea been served'); | |
* | |
* @name isBoolean | |
* @param {Mixed} value | |
* @param {String} message | |
* @api public | |
*/ | |
assert.isBoolean = function (val, msg) { | |
new Assertion(val, msg).to.be.a('boolean'); | |
}; | |
/** | |
* ### .isNotBoolean(value, [message]) | |
* | |
* Asserts that `value` is _not_ a boolean. | |
* | |
* var teaReady = 'yep' | |
* , teaServed = 'nope'; | |
* | |
* assert.isNotBoolean(teaReady, 'is the tea ready'); | |
* assert.isNotBoolean(teaServed, 'has tea been served'); | |
* | |
* @name isNotBoolean | |
* @param {Mixed} value | |
* @param {String} message | |
* @api public | |
*/ | |
assert.isNotBoolean = function (val, msg) { | |
new Assertion(val, msg).to.not.be.a('boolean'); | |
}; | |
/** | |
* ### .typeOf(value, name, [message]) | |
* | |
* Asserts that `value`'s type is `name`, as determined by | |
* `Object.prototype.toString`. | |
* | |
* assert.typeOf({ tea: 'chai' }, 'object', 'we have an object'); | |
* assert.typeOf(['chai', 'jasmine'], 'array', 'we have an array'); | |
* assert.typeOf('tea', 'string', 'we have a string'); | |
* assert.typeOf(/tea/, 'regexp', 'we have a regular expression'); | |
* assert.typeOf(null, 'null', 'we have a null'); | |
* assert.typeOf(undefined, 'undefined', 'we have an undefined'); | |
* | |
* @name typeOf | |
* @param {Mixed} value | |
* @param {String} name | |
* @param {String} message | |
* @api public | |
*/ | |
assert.typeOf = function (val, type, msg) { | |
new Assertion(val, msg).to.be.a(type); | |
}; | |
/** | |
* ### .notTypeOf(value, name, [message]) | |
* | |
* Asserts that `value`'s type is _not_ `name`, as determined by | |
* `Object.prototype.toString`. | |
* | |
* assert.notTypeOf('tea', 'number', 'strings are not numbers'); | |
* | |
* @name notTypeOf | |
* @param {Mixed} value | |
* @param {String} typeof name | |
* @param {String} message | |
* @api public | |
*/ | |
assert.notTypeOf = function (val, type, msg) { | |
new Assertion(val, msg).to.not.be.a(type); | |
}; | |
/** | |
* ### .instanceOf(object, constructor, [message]) | |
* | |
* Asserts that `value` is an instance of `constructor`. | |
* | |
* var Tea = function (name) { this.name = name; } | |
* , chai = new Tea('chai'); | |
* | |
* assert.instanceOf(chai, Tea, 'chai is an instance of tea'); | |
* | |
* @name instanceOf | |
* @param {Object} object | |
* @param {Constructor} constructor | |
* @param {String} message | |
* @api public | |
*/ | |
assert.instanceOf = function (val, type, msg) { | |
new Assertion(val, msg).to.be.instanceOf(type); | |
}; | |
/** | |
* ### .notInstanceOf(object, constructor, [message]) | |
* | |
* Asserts `value` is not an instance of `constructor`. | |
* | |
* var Tea = function (name) { this.name = name; } | |
* , chai = new String('chai'); | |
* | |
* assert.notInstanceOf(chai, Tea, 'chai is not an instance of tea'); | |
* | |
* @name notInstanceOf | |
* @param {Object} object | |
* @param {Constructor} constructor | |
* @param {String} message | |
* @api public | |
*/ | |
assert.notInstanceOf = function (val, type, msg) { | |
new Assertion(val, msg).to.not.be.instanceOf(type); | |
}; | |
/** | |
* ### .include(haystack, needle, [message]) | |
* | |
* Asserts that `haystack` includes `needle`. Works | |
* for strings and arrays. | |
* | |
* assert.include('foobar', 'bar', 'foobar contains string "bar"'); | |
* assert.include([ 1, 2, 3 ], 3, 'array contains value'); | |
* | |
* @name include | |
* @param {Array|String} haystack | |
* @param {Mixed} needle | |
* @param {String} message | |
* @api public | |
*/ | |
assert.include = function (exp, inc, msg) { | |
var obj = new Assertion(exp, msg); | |
if (Array.isArray(exp)) { | |
obj.to.include(inc); | |
} else if ('string' === typeof exp) { | |
obj.to.contain.string(inc); | |
} else { | |
throw new chai.AssertionError( | |
'expected an array or string' | |
, null | |
, assert.include | |
); | |
} | |
}; | |
/** | |
* ### .notInclude(haystack, needle, [message]) | |
* | |
* Asserts that `haystack` does not include `needle`. Works | |
* for strings and arrays. | |
*i | |
* assert.notInclude('foobar', 'baz', 'string not include substring'); | |
* assert.notInclude([ 1, 2, 3 ], 4, 'array not include contain value'); | |
* | |
* @name notInclude | |
* @param {Array|String} haystack | |
* @param {Mixed} needle | |
* @param {String} message | |
* @api public | |
*/ | |
assert.notInclude = function (exp, inc, msg) { | |
var obj = new Assertion(exp, msg); | |
if (Array.isArray(exp)) { | |
obj.to.not.include(inc); | |
} else if ('string' === typeof exp) { | |
obj.to.not.contain.string(inc); | |
} else { | |
throw new chai.AssertionError( | |
'expected an array or string' | |
, null | |
, assert.notInclude | |
); | |
} | |
}; | |
/** | |
* ### .match(value, regexp, [message]) | |
* | |
* Asserts that `value` matches the regular expression `regexp`. | |
* | |
* assert.match('foobar', /^foo/, 'regexp matches'); | |
* | |
* @name match | |
* @param {Mixed} value | |
* @param {RegExp} regexp | |
* @param {String} message | |
* @api public | |
*/ | |
assert.match = function (exp, re, msg) { | |
new Assertion(exp, msg).to.match(re); | |
}; | |
/** | |
* ### .notMatch(value, regexp, [message]) | |
* | |
* Asserts that `value` does not match the regular expression `regexp`. | |
* | |
* assert.notMatch('foobar', /^foo/, 'regexp does not match'); | |
* | |
* @name notMatch | |
* @param {Mixed} value | |
* @param {RegExp} regexp | |
* @param {String} message | |
* @api public | |
*/ | |
assert.notMatch = function (exp, re, msg) { | |
new Assertion(exp, msg).to.not.match(re); | |
}; | |
/** | |
* ### .property(object, property, [message]) | |
* | |
* Asserts that `object` has a property named by `property`. | |
* | |
* assert.property({ tea: { green: 'matcha' }}, 'tea'); | |
* | |
* @name property | |
* @param {Object} object | |
* @param {String} property | |
* @param {String} message | |
* @api public | |
*/ | |
assert.property = function (obj, prop, msg) { | |
new Assertion(obj, msg).to.have.property(prop); | |
}; | |
/** | |
* ### .notProperty(object, property, [message]) | |
* | |
* Asserts that `object` does _not_ have a property named by `property`. | |
* | |
* assert.notProperty({ tea: { green: 'matcha' }}, 'coffee'); | |
* | |
* @name notProperty | |
* @param {Object} object | |
* @param {String} property | |
* @param {String} message | |
* @api public | |
*/ | |
assert.notProperty = function (obj, prop, msg) { | |
new Assertion(obj, msg).to.not.have.property(prop); | |
}; | |
/** | |
* ### .deepProperty(object, property, [message]) | |
* | |
* Asserts that `object` has a property named by `property`, which can be a | |
* string using dot- and bracket-notation for deep reference. | |
* | |
* assert.deepProperty({ tea: { green: 'matcha' }}, 'tea.green'); | |
* | |
* @name deepProperty | |
* @param {Object} object | |
* @param {String} property | |
* @param {String} message | |
* @api public | |
*/ | |
assert.deepProperty = function (obj, prop, msg) { | |
new Assertion(obj, msg).to.have.deep.property(prop); | |
}; | |
/** | |
* ### .notDeepProperty(object, property, [message]) | |
* | |
* Asserts that `object` does _not_ have a property named by `property`, which | |
* can be a string using dot- and bracket-notation for deep reference. | |
* | |
* assert.notDeepProperty({ tea: { green: 'matcha' }}, 'tea.oolong'); | |
* | |
* @name notDeepProperty | |
* @param {Object} object | |
* @param {String} property | |
* @param {String} message | |
* @api public | |
*/ | |
assert.notDeepProperty = function (obj, prop, msg) { | |
new Assertion(obj, msg).to.not.have.deep.property(prop); | |
}; | |
/** | |
* ### .propertyVal(object, property, value, [message]) | |
* | |
* Asserts that `object` has a property named by `property` with value given | |
* by `value`. | |
* | |
* assert.propertyVal({ tea: 'is good' }, 'tea', 'is good'); | |
* | |
* @name propertyVal | |
* @param {Object} object | |
* @param {String} property | |
* @param {Mixed} value | |
* @param {String} message | |
* @api public | |
*/ | |
assert.propertyVal = function (obj, prop, val, msg) { | |
new Assertion(obj, msg).to.have.property(prop, val); | |
}; | |
/** | |
* ### .propertyNotVal(object, property, value, [message]) | |
* | |
* Asserts that `object` has a property named by `property`, but with a value | |
* different from that given by `value`. | |
* | |
* assert.propertyNotVal({ tea: 'is good' }, 'tea', 'is bad'); | |
* | |
* @name propertyNotVal | |
* @param {Object} object | |
* @param {String} property | |
* @param {Mixed} value | |
* @param {String} message | |
* @api public | |
*/ | |
assert.propertyNotVal = function (obj, prop, val, msg) { | |
new Assertion(obj, msg).to.not.have.property(prop, val); | |
}; | |
/** | |
* ### .deepPropertyVal(object, property, value, [message]) | |
* | |
* Asserts that `object` has a property named by `property` with value given | |
* by `value`. `property` can use dot- and bracket-notation for deep | |
* reference. | |
* | |
* assert.deepPropertyVal({ tea: { green: 'matcha' }}, 'tea.green', 'matcha'); | |
* | |
* @name deepPropertyVal | |
* @param {Object} object | |
* @param {String} property | |
* @param {Mixed} value | |
* @param {String} message | |
* @api public | |
*/ | |
assert.deepPropertyVal = function (obj, prop, val, msg) { | |
new Assertion(obj, msg).to.have.deep.property(prop, val); | |
}; | |
/** | |
* ### .deepPropertyNotVal(object, property, value, [message]) | |
* | |
* Asserts that `object` has a property named by `property`, but with a value | |
* different from that given by `value`. `property` can use dot- and | |
* bracket-notation for deep reference. | |
* | |
* assert.deepPropertyNotVal({ tea: { green: 'matcha' }}, 'tea.green', 'konacha'); | |
* | |
* @name deepPropertyNotVal | |
* @param {Object} object | |
* @param {String} property | |
* @param {Mixed} value | |
* @param {String} message | |
* @api public | |
*/ | |
assert.deepPropertyNotVal = function (obj, prop, val, msg) { | |
new Assertion(obj, msg).to.not.have.deep.property(prop, val); | |
}; | |
/** | |
* ### .lengthOf(object, length, [message]) | |
* | |
* Asserts that `object` has a `length` property with the expected value. | |
* | |
* assert.lengthOf([1,2,3], 3, 'array has length of 3'); | |
* assert.lengthOf('foobar', 5, 'string has length of 6'); | |
* | |
* @name lengthOf | |
* @param {Mixed} object | |
* @param {Number} length | |
* @param {String} message | |
* @api public | |
*/ | |
assert.lengthOf = function (exp, len, msg) { | |
new Assertion(exp, msg).to.have.length(len); | |
}; | |
/** | |
* ### .throws(function, [constructor/string/regexp], [string/regexp], [message]) | |
* | |
* Asserts that `function` will throw an error that is an instance of | |
* `constructor`, or alternately that it will throw an error with message | |
* matching `regexp`. | |
* | |
* assert.throw(fn, 'function throws a reference error'); | |
* assert.throw(fn, /function throws a reference error/); | |
* assert.throw(fn, ReferenceError); | |
* assert.throw(fn, ReferenceError, 'function throws a reference error'); | |
* assert.throw(fn, ReferenceError, /function throws a reference error/); | |
* | |
* @name throws | |
* @alias throw | |
* @alias Throw | |
* @param {Function} function | |
* @param {ErrorConstructor} constructor | |
* @param {RegExp} regexp | |
* @param {String} message | |
* @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error#Error_types | |
* @api public | |
*/ | |
assert.Throw = function (fn, errt, errs, msg) { | |
if ('string' === typeof errt || errt instanceof RegExp) { | |
errs = errt; | |
errt = null; | |
} | |
new Assertion(fn, msg).to.Throw(errt, errs); | |
}; | |
/** | |
* ### .doesNotThrow(function, [constructor/regexp], [message]) | |
* | |
* Asserts that `function` will _not_ throw an error that is an instance of | |
* `constructor`, or alternately that it will not throw an error with message | |
* matching `regexp`. | |
* | |
* assert.doesNotThrow(fn, Error, 'function does not throw'); | |
* | |
* @name doesNotThrow | |
* @param {Function} function | |
* @param {ErrorConstructor} constructor | |
* @param {RegExp} regexp | |
* @param {String} message | |
* @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error#Error_types | |
* @api public | |
*/ | |
assert.doesNotThrow = function (fn, type, msg) { | |
if ('string' === typeof type) { | |
msg = type; | |
type = null; | |
} | |
new Assertion(fn, msg).to.not.Throw(type); | |
}; | |
/** | |
* ### .operator(val1, operator, val2, [message]) | |
* | |
* Compares two values using `operator`. | |
* | |
* assert.operator(1, '<', 2, 'everything is ok'); | |
* assert.operator(1, '>', 2, 'this will fail'); | |
* | |
* @name operator | |
* @param {Mixed} val1 | |
* @param {String} operator | |
* @param {Mixed} val2 | |
* @param {String} message | |
* @api public | |
*/ | |
assert.operator = function (val, operator, val2, msg) { | |
if (!~['==', '===', '>', '>=', '<', '<=', '!=', '!=='].indexOf(operator)) { | |
throw new Error('Invalid operator "' + operator + '"'); | |
} | |
var test = new Assertion(eval(val + operator + val2), msg); | |
test.assert( | |
true === flag(test, 'object') | |
, 'expected ' + util.inspect(val) + ' to be ' + operator + ' ' + util.inspect(val2) | |
, 'expected ' + util.inspect(val) + ' to not be ' + operator + ' ' + util.inspect(val2) ); | |
}; | |
/** | |
* ### .closeTo(actual, expected, delta, [message]) | |
* | |
* Asserts that the target is equal `expected`, to within a +/- `delta` range. | |
* | |
* assert.closeTo(1.5, 1, 0.5, 'numbers are close'); | |
* | |
* @name closeTo | |
* @param {Number} actual | |
* @param {Number} expected | |
* @param {Number} delta | |
* @param {String} message | |
* @api public | |
*/ | |
assert.closeTo = function (act, exp, delta, msg) { | |
new Assertion(act, msg).to.be.closeTo(exp, delta); | |
}; | |
/** | |
* ### .sameMembers(set1, set2, [message]) | |
* | |
* Asserts that `set1` and `set2` have the same members. | |
* Order is not taken into account. | |
* | |
* assert.sameMembers([ 1, 2, 3 ], [ 2, 1, 3 ], 'same members'); | |
* | |
* @name sameMembers | |
* @param {Array} superset | |
* @param {Array} subset | |
* @param {String} message | |
* @api public | |
*/ | |
assert.sameMembers = function (set1, set2, msg) { | |
new Assertion(set1, msg).to.have.same.members(set2); | |
} | |
/** | |
* ### .includeMembers(superset, subset, [message]) | |
* | |
* Asserts that `subset` is included in `superset`. | |
* Order is not taken into account. | |
* | |
* assert.includeMembers([ 1, 2, 3 ], [ 2, 1 ], 'include members'); | |
* | |
* @name includeMembers | |
* @param {Array} superset | |
* @param {Array} subset | |
* @param {String} message | |
* @api public | |
*/ | |
assert.includeMembers = function (superset, subset, msg) { | |
new Assertion(superset, msg).to.include.members(subset); | |
} | |
/*! | |
* Undocumented / untested | |
*/ | |
assert.ifError = function (val, msg) { | |
new Assertion(val, msg).to.not.be.ok; | |
}; | |
/*! | |
* Aliases. | |
*/ | |
(function alias(name, as){ | |
assert[as] = assert[name]; | |
return alias; | |
}) | |
('Throw', 'throw') | |
('Throw', 'throws'); | |
}; | |
},{}],54:[function(require,module,exports){ | |
/*! | |
* chai | |
* Copyright(c) 2011-2013 Jake Luer <jake@alogicalparadox.com> | |
* MIT Licensed | |
*/ | |
module.exports = function (chai, util) { | |
chai.expect = function (val, message) { | |
return new chai.Assertion(val, message); | |
}; | |
}; | |
},{}],55:[function(require,module,exports){ | |
/*! | |
* chai | |
* Copyright(c) 2011-2013 Jake Luer <jake@alogicalparadox.com> | |
* MIT Licensed | |
*/ | |
module.exports = function (chai, util) { | |
var Assertion = chai.Assertion; | |
function loadShould () { | |
// modify Object.prototype to have `should` | |
Object.defineProperty(Object.prototype, 'should', | |
{ | |
set: function (value) { | |
// See https://github.com/chaijs/chai/issues/86: this makes | |
// `whatever.should = someValue` actually set `someValue`, which is | |
// especially useful for `global.should = require('chai').should()`. | |
// | |
// Note that we have to use [[DefineProperty]] instead of [[Put]] | |
// since otherwise we would trigger this very setter! | |
Object.defineProperty(this, 'should', { | |
value: value, | |
enumerable: true, | |
configurable: true, | |
writable: true | |
}); | |
} | |
, get: function(){ | |
if (this instanceof String || this instanceof Number) { | |
return new Assertion(this.constructor(this)); | |
} else if (this instanceof Boolean) { | |
return new Assertion(this == true); | |
} | |
return new Assertion(this); | |
} | |
, configurable: true | |
}); | |
var should = {}; | |
should.equal = function (val1, val2, msg) { | |
new Assertion(val1, msg).to.equal(val2); | |
}; | |
should.Throw = function (fn, errt, errs, msg) { | |
new Assertion(fn, msg).to.Throw(errt, errs); | |
}; | |
should.exist = function (val, msg) { | |
new Assertion(val, msg).to.exist; | |
} | |
// negation | |
should.not = {} | |
should.not.equal = function (val1, val2, msg) { | |
new Assertion(val1, msg).to.not.equal(val2); | |
}; | |
should.not.Throw = function (fn, errt, errs, msg) { | |
new Assertion(fn, msg).to.not.Throw(errt, errs); | |
}; | |
should.not.exist = function (val, msg) { | |
new Assertion(val, msg).to.not.exist; | |
} | |
should['throw'] = should['Throw']; | |
should.not['throw'] = should.not['Throw']; | |
return should; | |
}; | |
chai.should = loadShould; | |
chai.Should = loadShould; | |
}; | |
},{}],56:[function(require,module,exports){ | |
/*! | |
* Chai - addChainingMethod utility | |
* Copyright(c) 2012-2013 Jake Luer <jake@alogicalparadox.com> | |
* MIT Licensed | |
*/ | |
/*! | |
* Module dependencies | |
*/ | |
var transferFlags = require('./transferFlags'); | |
/*! | |
* Module variables | |
*/ | |
// Check whether `__proto__` is supported | |
var hasProtoSupport = '__proto__' in Object; | |
// Without `__proto__` support, this module will need to add properties to a function. | |
// However, some Function.prototype methods cannot be overwritten, | |
// and there seems no easy cross-platform way to detect them (@see chaijs/chai/issues/69). | |
var excludeNames = /^(?:length|name|arguments|caller)$/; | |
// Cache `Function` properties | |
var call = Function.prototype.call, | |
apply = Function.prototype.apply; | |
/** | |
* ### addChainableMethod (ctx, name, method, chainingBehavior) | |
* | |
* Adds a method to an object, such that the method can also be chained. | |
* | |
* utils.addChainableMethod(chai.Assertion.prototype, 'foo', function (str) { | |
* var obj = utils.flag(this, 'object'); | |
* new chai.Assertion(obj).to.be.equal(str); | |
* }); | |
* | |
* Can also be accessed directly from `chai.Assertion`. | |
* | |
* chai.Assertion.addChainableMethod('foo', fn, chainingBehavior); | |
* | |
* The result can then be used as both a method assertion, executing both `method` and | |
* `chainingBehavior`, or as a language chain, which only executes `chainingBehavior`. | |
* | |
* expect(fooStr).to.be.foo('bar'); | |
* expect(fooStr).to.be.foo.equal('foo'); | |
* | |
* @param {Object} ctx object to which the method is added | |
* @param {String} name of method to add | |
* @param {Function} method function to be used for `name`, when called | |
* @param {Function} chainingBehavior function to be called every time the property is accessed | |
* @name addChainableMethod | |
* @api public | |
*/ | |
module.exports = function (ctx, name, method, chainingBehavior) { | |
if (typeof chainingBehavior !== 'function') | |
chainingBehavior = function () { }; | |
Object.defineProperty(ctx, name, | |
{ get: function () { | |
chainingBehavior.call(this); | |
var assert = function () { | |
var result = method.apply(this, arguments); | |
return result === undefined ? this : result; | |
}; | |
// Use `__proto__` if available | |
if (hasProtoSupport) { | |
// Inherit all properties from the object by replacing the `Function` prototype | |
var prototype = assert.__proto__ = Object.create(this); | |
// Restore the `call` and `apply` methods from `Function` | |
prototype.call = call; | |
prototype.apply = apply; | |
} | |
// Otherwise, redefine all properties (slow!) | |
else { | |
var asserterNames = Object.getOwnPropertyNames(ctx); | |
asserterNames.forEach(function (asserterName) { | |
if (!excludeNames.test(asserterName)) { | |
var pd = Object.getOwnPropertyDescriptor(ctx, asserterName); | |
Object.defineProperty(assert, asserterName, pd); | |
} | |
}); | |
} | |
transferFlags(this, assert); | |
return assert; | |
} | |
, configurable: true | |
}); | |
}; | |
},{"./transferFlags":72}],57:[function(require,module,exports){ | |
/*! | |
* Chai - addMethod utility | |
* Copyright(c) 2012-2013 Jake Luer <jake@alogicalparadox.com> | |
* MIT Licensed | |
*/ | |
/** | |
* ### .addMethod (ctx, name, method) | |
* | |
* Adds a method to the prototype of an object. | |
* | |
* utils.addMethod(chai.Assertion.prototype, 'foo', function (str) { | |
* var obj = utils.flag(this, 'object'); | |
* new chai.Assertion(obj).to.be.equal(str); | |
* }); | |
* | |
* Can also be accessed directly from `chai.Assertion`. | |
* | |
* chai.Assertion.addMethod('foo', fn); | |
* | |
* Then can be used as any other assertion. | |
* | |
* expect(fooStr).to.be.foo('bar'); | |
* | |
* @param {Object} ctx object to which the method is added | |
* @param {String} name of method to add | |
* @param {Function} method function to be used for name | |
* @name addMethod | |
* @api public | |
*/ | |
module.exports = function (ctx, name, method) { | |
ctx[name] = function () { | |
var result = method.apply(this, arguments); | |
return result === undefined ? this : result; | |
}; | |
}; | |
},{}],58:[function(require,module,exports){ | |
/*! | |
* Chai - addProperty utility | |
* Copyright(c) 2012-2013 Jake Luer <jake@alogicalparadox.com> | |
* MIT Licensed | |
*/ | |
/** | |
* ### addProperty (ctx, name, getter) | |
* | |
* Adds a property to the prototype of an object. | |
* | |
* utils.addProperty(chai.Assertion.prototype, 'foo', function () { | |
* var obj = utils.flag(this, 'object'); | |
* new chai.Assertion(obj).to.be.instanceof(Foo); | |
* }); | |
* | |
* Can also be accessed directly from `chai.Assertion`. | |
* | |
* chai.Assertion.addProperty('foo', fn); | |
* | |
* Then can be used as any other assertion. | |
* | |
* expect(myFoo).to.be.foo; | |
* | |
* @param {Object} ctx object to which the property is added | |
* @param {String} name of property to add | |
* @param {Function} getter function to be used for name | |
* @name addProperty | |
* @api public | |
*/ | |
module.exports = function (ctx, name, getter) { | |
Object.defineProperty(ctx, name, | |
{ get: function () { | |
var result = getter.call(this); | |
return result === undefined ? this : result; | |
} | |
, configurable: true | |
}); | |
}; | |
},{}],59:[function(require,module,exports){ | |
/*! | |
* Chai - flag utility | |
* Copyright(c) 2012-2013 Jake Luer <jake@alogicalparadox.com> | |
* MIT Licensed | |
*/ | |
/** | |
* ### flag(object ,key, [value]) | |
* | |
* Get or set a flag value on an object. If a | |
* value is provided it will be set, else it will | |
* return the currently set value or `undefined` if | |
* the value is not set. | |
* | |
* utils.flag(this, 'foo', 'bar'); // setter | |
* utils.flag(this, 'foo'); // getter, returns `bar` | |
* | |
* @param {Object} object (constructed Assertion | |
* @param {String} key | |
* @param {Mixed} value (optional) | |
* @name flag | |
* @api private | |
*/ | |
module.exports = function (obj, key, value) { | |
var flags = obj.__flags || (obj.__flags = Object.create(null)); | |
if (arguments.length === 3) { | |
flags[key] = value; | |
} else { | |
return flags[key]; | |
} | |
}; | |
},{}],60:[function(require,module,exports){ | |
/*! | |
* Chai - getActual utility | |
* Copyright(c) 2012-2013 Jake Luer <jake@alogicalparadox.com> | |
* MIT Licensed | |
*/ | |
/** | |
* # getActual(object, [actual]) | |
* | |
* Returns the `actual` value for an Assertion | |
* | |
* @param {Object} object (constructed Assertion) | |
* @param {Arguments} chai.Assertion.prototype.assert arguments | |
*/ | |
module.exports = function (obj, args) { | |
var actual = args[4]; | |
return 'undefined' !== typeof actual ? actual : obj._obj; | |
}; | |
},{}],61:[function(require,module,exports){ | |
/*! | |
* Chai - getEnumerableProperties utility | |
* Copyright(c) 2012-2013 Jake Luer <jake@alogicalparadox.com> | |
* MIT Licensed | |
*/ | |
/** | |
* ### .getEnumerableProperties(object) | |
* | |
* This allows the retrieval of enumerable property names of an object, | |
* inherited or not. | |
* | |
* @param {Object} object | |
* @returns {Array} | |
* @name getEnumerableProperties | |
* @api public | |
*/ | |
module.exports = function getEnumerableProperties(object) { | |
var result = []; | |
for (var name in object) { | |
result.push(name); | |
} | |
return result; | |
}; | |
},{}],62:[function(require,module,exports){ | |
/*! | |
* Chai - message composition utility | |
* Copyright(c) 2012-2013 Jake Luer <jake@alogicalparadox.com> | |
* MIT Licensed | |
*/ | |
/*! | |
* Module dependancies | |
*/ | |
var flag = require('./flag') | |
, getActual = require('./getActual') | |
, inspect = require('./inspect') | |
, objDisplay = require('./objDisplay'); | |
/** | |
* ### .getMessage(object, message, negateMessage) | |
* | |
* Construct the error message based on flags | |
* and template tags. Template tags will return | |
* a stringified inspection of the object referenced. | |
* | |
* Message template tags: | |
* - `#{this}` current asserted object | |
* - `#{act}` actual value | |
* - `#{exp}` expected value | |
* | |
* @param {Object} object (constructed Assertion) | |
* @param {Arguments} chai.Assertion.prototype.assert arguments | |
* @name getMessage | |
* @api public | |
*/ | |
module.exports = function (obj, args) { | |
var negate = flag(obj, 'negate') | |
, val = flag(obj, 'object') | |
, expected = args[3] | |
, actual = getActual(obj, args) | |
, msg = negate ? args[2] : args[1] | |
, flagMsg = flag(obj, 'message'); | |
msg = msg || ''; | |
msg = msg | |
.replace(/#{this}/g, objDisplay(val)) | |
.replace(/#{act}/g, objDisplay(actual)) | |
.replace(/#{exp}/g, objDisplay(expected)); | |
return flagMsg ? flagMsg + ': ' + msg : msg; | |
}; | |
},{"./flag":59,"./getActual":60,"./inspect":67,"./objDisplay":68}],63:[function(require,module,exports){ | |
/*! | |
* Chai - getName utility | |
* Copyright(c) 2012-2013 Jake Luer <jake@alogicalparadox.com> | |
* MIT Licensed | |
*/ | |
/** | |
* # getName(func) | |
* | |
* Gets the name of a function, in a cross-browser way. | |
* | |
* @param {Function} a function (usually a constructor) | |
*/ | |
module.exports = function (func) { | |
if (func.name) return func.name; | |
var match = /^\s?function ([^(]*)\(/.exec(func); | |
return match && match[1] ? match[1] : ""; | |
}; | |
},{}],64:[function(require,module,exports){ | |
/*! | |
* Chai - getPathValue utility | |
* Copyright(c) 2012-2013 Jake Luer <jake@alogicalparadox.com> | |
* @see https://github.com/logicalparadox/filtr | |
* MIT Licensed | |
*/ | |
/** | |
* ### .getPathValue(path, object) | |
* | |
* This allows the retrieval of values in an | |
* object given a string path. | |
* | |
* var obj = { | |
* prop1: { | |
* arr: ['a', 'b', 'c'] | |
* , str: 'Hello' | |
* } | |
* , prop2: { | |
* arr: [ { nested: 'Universe' } ] | |
* , str: 'Hello again!' | |
* } | |
* } | |
* | |
* The following would be the results. | |
* | |
* getPathValue('prop1.str', obj); // Hello | |
* getPathValue('prop1.att[2]', obj); // b | |
* getPathValue('prop2.arr[0].nested', obj); // Universe | |
* | |
* @param {String} path | |
* @param {Object} object | |
* @returns {Object} value or `undefined` | |
* @name getPathValue | |
* @api public | |
*/ | |
var getPathValue = module.exports = function (path, obj) { | |
var parsed = parsePath(path); | |
return _getPathValue(parsed, obj); | |
}; | |
/*! | |
* ## parsePath(path) | |
* | |
* Helper function used to parse string object | |
* paths. Use in conjunction with `_getPathValue`. | |
* | |
* var parsed = parsePath('myobject.property.subprop'); | |
* | |
* ### Paths: | |
* | |
* * Can be as near infinitely deep and nested | |
* * Arrays are also valid using the formal `myobject.document[3].property`. | |
* | |
* @param {String} path | |
* @returns {Object} parsed | |
* @api private | |
*/ | |
function parsePath (path) { | |
var str = path.replace(/\[/g, '.[') | |
, parts = str.match(/(\\\.|[^.]+?)+/g); | |
return parts.map(function (value) { | |
var re = /\[(\d+)\]$/ | |
, mArr = re.exec(value) | |
if (mArr) return { i: parseFloat(mArr[1]) }; | |
else return { p: value }; | |
}); | |
}; | |
/*! | |
* ## _getPathValue(parsed, obj) | |
* | |
* Helper companion function for `.parsePath` that returns | |
* the value located at the parsed address. | |
* | |
* var value = getPathValue(parsed, obj); | |
* | |
* @param {Object} parsed definition from `parsePath`. | |
* @param {Object} object to search against | |
* @returns {Object|Undefined} value | |
* @api private | |
*/ | |
function _getPathValue (parsed, obj) { | |
var tmp = obj | |
, res; | |
for (var i = 0, l = parsed.length; i < l; i++) { | |
var part = parsed[i]; | |
if (tmp) { | |
if ('undefined' !== typeof part.p) | |
tmp = tmp[part.p]; | |
else if ('undefined' !== typeof part.i) | |
tmp = tmp[part.i]; | |
if (i == (l - 1)) res = tmp; | |
} else { | |
res = undefined; | |
} | |
} | |
return res; | |
}; | |
},{}],65:[function(require,module,exports){ | |
/*! | |
* Chai - getProperties utility | |
* Copyright(c) 2012-2013 Jake Luer <jake@alogicalparadox.com> | |
* MIT Licensed | |
*/ | |
/** | |
* ### .getProperties(object) | |
* | |
* This allows the retrieval of property names of an object, enumerable or not, | |
* inherited or not. | |
* | |
* @param {Object} object | |
* @returns {Array} | |
* @name getProperties | |
* @api public | |
*/ | |
module.exports = function getProperties(object) { | |
var result = Object.getOwnPropertyNames(subject); | |
function addProperty(property) { | |
if (result.indexOf(property) === -1) { | |
result.push(property); | |
} | |
} | |
var proto = Object.getPrototypeOf(subject); | |
while (proto !== null) { | |
Object.getOwnPropertyNames(proto).forEach(addProperty); | |
proto = Object.getPrototypeOf(proto); | |
} | |
return result; | |
}; | |
},{}],66:[function(require,module,exports){ | |
/*! | |
* chai | |
* Copyright(c) 2011 Jake Luer <jake@alogicalparadox.com> | |
* MIT Licensed | |
*/ | |
/*! | |
* Main exports | |
*/ | |
var exports = module.exports = {}; | |
/*! | |
* test utility | |
*/ | |
exports.test = require('./test'); | |
/*! | |
* type utility | |
*/ | |
exports.type = require('./type'); | |
/*! | |
* message utility | |
*/ | |
exports.getMessage = require('./getMessage'); | |
/*! | |
* actual utility | |
*/ | |
exports.getActual = require('./getActual'); | |
/*! | |
* Inspect util | |
*/ | |
exports.inspect = require('./inspect'); | |
/*! | |
* Object Display util | |
*/ | |
exports.objDisplay = require('./objDisplay'); | |
/*! | |
* Flag utility | |
*/ | |
exports.flag = require('./flag'); | |
/*! | |
* Flag transferring utility | |
*/ | |
exports.transferFlags = require('./transferFlags'); | |
/*! | |
* Deep equal utility | |
*/ | |
exports.eql = require('deep-eql'); | |
/*! | |
* Deep path value | |
*/ | |
exports.getPathValue = require('./getPathValue'); | |
/*! | |
* Function name | |
*/ | |
exports.getName = require('./getName'); | |
/*! | |
* add Property | |
*/ | |
exports.addProperty = require('./addProperty'); | |
/*! | |
* add Method | |
*/ | |
exports.addMethod = require('./addMethod'); | |
/*! | |
* overwrite Property | |
*/ | |
exports.overwriteProperty = require('./overwriteProperty'); | |
/*! | |
* overwrite Method | |
*/ | |
exports.overwriteMethod = require('./overwriteMethod'); | |
/*! | |
* Add a chainable method | |
*/ | |
exports.addChainableMethod = require('./addChainableMethod'); | |
},{"./addChainableMethod":56,"./addMethod":57,"./addProperty":58,"./flag":59,"./getActual":60,"./getMessage":62,"./getName":63,"./getPathValue":64,"./inspect":67,"./objDisplay":68,"./overwriteMethod":69,"./overwriteProperty":70,"./test":71,"./transferFlags":72,"./type":73,"deep-eql":75}],67:[function(require,module,exports){ | |
// This is (almost) directly from Node.js utils | |
// https://github.com/joyent/node/blob/f8c335d0caf47f16d31413f89aa28eda3878e3aa/lib/util.js | |
var getName = require('./getName'); | |
var getProperties = require('./getProperties'); | |
var getEnumerableProperties = require('./getEnumerableProperties'); | |
module.exports = inspect; | |
/** | |
* Echos the value of a value. Trys to print the value out | |
* in the best way possible given the different types. | |
* | |
* @param {Object} obj The object to print out. | |
* @param {Boolean} showHidden Flag that shows hidden (not enumerable) | |
* properties of objects. | |
* @param {Number} depth Depth in which to descend in object. Default is 2. | |
* @param {Boolean} colors Flag to turn on ANSI escape codes to color the | |
* output. Default is false (no coloring). | |
*/ | |
function inspect(obj, showHidden, depth, colors) { | |
var ctx = { | |
showHidden: showHidden, | |
seen: [], | |
stylize: function (str) { return str; } | |
}; | |
return formatValue(ctx, obj, (typeof depth === 'undefined' ? 2 : depth)); | |
} | |
// https://gist.github.com/1044128/ | |
var getOuterHTML = function(element) { | |
if ('outerHTML' in element) return element.outerHTML; | |
var ns = "http://www.w3.org/1999/xhtml"; | |
var container = document.createElementNS(ns, '_'); | |
var elemProto = (window.HTMLElement || window.Element).prototype; | |
var xmlSerializer = new XMLSerializer(); | |
var html; | |
if (document.xmlVersion) { | |
return xmlSerializer.serializeToString(element); | |
} else { | |
container.appendChild(element.cloneNode(false)); | |
html = container.innerHTML.replace('><', '>' + element.innerHTML + '<'); | |
container.innerHTML = ''; | |
return html; | |
} | |
}; | |
// Returns true if object is a DOM element. | |
var isDOMElement = function (object) { | |
if (typeof HTMLElement === 'object') { | |
return object instanceof HTMLElement; | |
} else { | |
return object && | |
typeof object === 'object' && | |
object.nodeType === 1 && | |
typeof object.nodeName === 'string'; | |
} | |
}; | |
function formatValue(ctx, value, recurseTimes) { | |
// Provide a hook for user-specified inspect functions. | |
// Check that value is an object with an inspect function on it | |
if (value && typeof value.inspect === 'function' && | |
// Filter out the util module, it's inspect function is special | |
value.inspect !== exports.inspect && | |
// Also filter out any prototype objects using the circular check. | |
!(value.constructor && value.constructor.prototype === value)) { | |
var ret = value.inspect(recurseTimes); | |
if (typeof ret !== 'string') { | |
ret = formatValue(ctx, ret, recurseTimes); | |
} | |
return ret; | |
} | |
// Primitive types cannot have properties | |
var primitive = formatPrimitive(ctx, value); | |
if (primitive) { | |
return primitive; | |
} | |
// If it's DOM elem, get outer HTML. | |
if (isDOMElement(value)) { | |
return getOuterHTML(value); | |
} | |
// Look up the keys of the object. | |
var visibleKeys = getEnumerableProperties(value); | |
var keys = ctx.showHidden ? getProperties(value) : visibleKeys; | |
// Some type of object without properties can be shortcutted. | |
// In IE, errors have a single `stack` property, or if they are vanilla `Error`, | |
// a `stack` plus `description` property; ignore those for consistency. | |
if (keys.length === 0 || (isError(value) && ( | |
(keys.length === 1 && keys[0] === 'stack') || | |
(keys.length === 2 && keys[0] === 'description' && keys[1] === 'stack') | |
))) { | |
if (typeof value === 'function') { | |
var name = getName(value); | |
var nameSuffix = name ? ': ' + name : ''; | |
return ctx.stylize('[Function' + nameSuffix + ']', 'special'); | |
} | |
if (isRegExp(value)) { | |
return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); | |
} | |
if (isDate(value)) { | |
return ctx.stylize(Date.prototype.toUTCString.call(value), 'date'); | |
} | |
if (isError(value)) { | |
return formatError(value); | |
} | |
} | |
var base = '', array = false, braces = ['{', '}']; | |
// Make Array say that they are Array | |
if (isArray(value)) { | |
array = true; | |
braces = ['[', ']']; | |
} | |
// Make functions say that they are functions | |
if (typeof value === 'function') { | |
var name = getName(value); | |
var nameSuffix = name ? ': ' + name : ''; | |
base = ' [Function' + nameSuffix + ']'; | |
} | |
// Make RegExps say that they are RegExps | |
if (isRegExp(value)) { | |
base = ' ' + RegExp.prototype.toString.call(value); | |
} | |
// Make dates with properties first say the date | |
if (isDate(value)) { | |
base = ' ' + Date.prototype.toUTCString.call(value); | |
} | |
// Make error with message first say the error | |
if (isError(value)) { | |
return formatError(value); | |
} | |
if (keys.length === 0 && (!array || value.length == 0)) { | |
return braces[0] + base + braces[1]; | |
} | |
if (recurseTimes < 0) { | |
if (isRegExp(value)) { | |
return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); | |
} else { | |
return ctx.stylize('[Object]', 'special'); | |
} | |
} | |
ctx.seen.push(value); | |
var output; | |
if (array) { | |
output = formatArray(ctx, value, recurseTimes, visibleKeys, keys); | |
} else { | |
output = keys.map(function(key) { | |
return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array); | |
}); | |
} | |
ctx.seen.pop(); | |
return reduceToSingleString(output, base, braces); | |
} | |
function formatPrimitive(ctx, value) { | |
switch (typeof value) { | |
case 'undefined': | |
return ctx.stylize('undefined', 'undefined'); | |
case 'string': | |
var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '') | |
.replace(/'/g, "\\'") | |
.replace(/\\"/g, '"') + '\''; | |
return ctx.stylize(simple, 'string'); | |
case 'number': | |
return ctx.stylize('' + value, 'number'); | |
case 'boolean': | |
return ctx.stylize('' + value, 'boolean'); | |
} | |
// For some reason typeof null is "object", so special case here. | |
if (value === null) { | |
return ctx.stylize('null', 'null'); | |
} | |
} | |
function formatError(value) { | |
return '[' + Error.prototype.toString.call(value) + ']'; | |
} | |
function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { | |
var output = []; | |
for (var i = 0, l = value.length; i < l; ++i) { | |
if (Object.prototype.hasOwnProperty.call(value, String(i))) { | |
output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, | |
String(i), true)); | |
} else { | |
output.push(''); | |
} | |
} | |
keys.forEach(function(key) { | |
if (!key.match(/^\d+$/)) { | |
output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, | |
key, true)); | |
} | |
}); | |
return output; | |
} | |
function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) { | |
var name, str; | |
if (value.__lookupGetter__) { | |
if (value.__lookupGetter__(key)) { | |
if (value.__lookupSetter__(key)) { | |
str = ctx.stylize('[Getter/Setter]', 'special'); | |
} else { | |
str = ctx.stylize('[Getter]', 'special'); | |
} | |
} else { | |
if (value.__lookupSetter__(key)) { | |
str = ctx.stylize('[Setter]', 'special'); | |
} | |
} | |
} | |
if (visibleKeys.indexOf(key) < 0) { | |
name = '[' + key + ']'; | |
} | |
if (!str) { | |
if (ctx.seen.indexOf(value[key]) < 0) { | |
if (recurseTimes === null) { | |
str = formatValue(ctx, value[key], null); | |
} else { | |
str = formatValue(ctx, value[key], recurseTimes - 1); | |
} | |
if (str.indexOf('\n') > -1) { | |
if (array) { | |
str = str.split('\n').map(function(line) { | |
return ' ' + line; | |
}).join('\n').substr(2); | |
} else { | |
str = '\n' + str.split('\n').map(function(line) { | |
return ' ' + line; | |
}).join('\n'); | |
} | |
} | |
} else { | |
str = ctx.stylize('[Circular]', 'special'); | |
} | |
} | |
if (typeof name === 'undefined') { | |
if (array && key.match(/^\d+$/)) { | |
return str; | |
} | |
name = JSON.stringify('' + key); | |
if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { | |
name = name.substr(1, name.length - 2); | |
name = ctx.stylize(name, 'name'); | |
} else { | |
name = name.replace(/'/g, "\\'") | |
.replace(/\\"/g, '"') | |
.replace(/(^"|"$)/g, "'"); | |
name = ctx.stylize(name, 'string'); | |
} | |
} | |
return name + ': ' + str; | |
} | |
function reduceToSingleString(output, base, braces) { | |
var numLinesEst = 0; | |
var length = output.reduce(function(prev, cur) { | |
numLinesEst++; | |
if (cur.indexOf('\n') >= 0) numLinesEst++; | |
return prev + cur.length + 1; | |
}, 0); | |
if (length > 60) { | |
return braces[0] + | |
(base === '' ? '' : base + '\n ') + | |
' ' + | |
output.join(',\n ') + | |
' ' + | |
braces[1]; | |
} | |
return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; | |
} | |
function isArray(ar) { | |
return Array.isArray(ar) || | |
(typeof ar === 'object' && objectToString(ar) === '[object Array]'); | |
} | |
function isRegExp(re) { | |
return typeof re === 'object' && objectToString(re) === '[object RegExp]'; | |
} | |
function isDate(d) { | |
return typeof d === 'object' && objectToString(d) === '[object Date]'; | |
} | |
function isError(e) { | |
return typeof e === 'object' && objectToString(e) === '[object Error]'; | |
} | |
function objectToString(o) { | |
return Object.prototype.toString.call(o); | |
} | |
},{"./getEnumerableProperties":61,"./getName":63,"./getProperties":65}],68:[function(require,module,exports){ | |
/*! | |
* Chai - flag utility | |
* Copyright(c) 2012-2013 Jake Luer <jake@alogicalparadox.com> | |
* MIT Licensed | |
*/ | |
/*! | |
* Module dependancies | |
*/ | |
var inspect = require('./inspect'); | |
/** | |
* ### .objDisplay (object) | |
* | |
* Determines if an object or an array matches | |
* criteria to be inspected in-line for error | |
* messages or should be truncated. | |
* | |
* @param {Mixed} javascript object to inspect | |
* @name objDisplay | |
* @api public | |
*/ | |
module.exports = function (obj) { | |
var str = inspect(obj) | |
, type = Object.prototype.toString.call(obj); | |
if (str.length >= 40) { | |
if (type === '[object Function]') { | |
return !obj.name || obj.name === '' | |
? '[Function]' | |
: '[Function: ' + obj.name + ']'; | |
} else if (type === '[object Array]') { | |
return '[ Array(' + obj.length + ') ]'; | |
} else if (type === '[object Object]') { | |
var keys = Object.keys(obj) | |
, kstr = keys.length > 2 | |
? keys.splice(0, 2).join(', ') + ', ...' | |
: keys.join(', '); | |
return '{ Object (' + kstr + ') }'; | |
} else { | |
return str; | |
} | |
} else { | |
return str; | |
} | |
}; | |
},{"./inspect":67}],69:[function(require,module,exports){ | |
/*! | |
* Chai - overwriteMethod utility | |
* Copyright(c) 2012-2013 Jake Luer <jake@alogicalparadox.com> | |
* MIT Licensed | |
*/ | |
/** | |
* ### overwriteMethod (ctx, name, fn) | |
* | |
* Overwites an already existing method and provides | |
* access to previous function. Must return function | |
* to be used for name. | |
* | |
* utils.overwriteMethod(chai.Assertion.prototype, 'equal', function (_super) { | |
* return function (str) { | |
* var obj = utils.flag(this, 'object'); | |
* if (obj instanceof Foo) { | |
* new chai.Assertion(obj.value).to.equal(str); | |
* } else { | |
* _super.apply(this, arguments); | |
* } | |
* } | |
* }); | |
* | |
* Can also be accessed directly from `chai.Assertion`. | |
* | |
* chai.Assertion.overwriteMethod('foo', fn); | |
* | |
* Then can be used as any other assertion. | |
* | |
* expect(myFoo).to.equal('bar'); | |
* | |
* @param {Object} ctx object whose method is to be overwritten | |
* @param {String} name of method to overwrite | |
* @param {Function} method function that returns a function to be used for name | |
* @name overwriteMethod | |
* @api public | |
*/ | |
module.exports = function (ctx, name, method) { | |
var _method = ctx[name] | |
, _super = function () { return this; }; | |
if (_method && 'function' === typeof _method) | |
_super = _method; | |
ctx[name] = function () { | |
var result = method(_super).apply(this, arguments); | |
return result === undefined ? this : result; | |
} | |
}; | |
},{}],70:[function(require,module,exports){ | |
/*! | |
* Chai - overwriteProperty utility | |
* Copyright(c) 2012-2013 Jake Luer <jake@alogicalparadox.com> | |
* MIT Licensed | |
*/ | |
/** | |
* ### overwriteProperty (ctx, name, fn) | |
* | |
* Overwites an already existing property getter and provides | |
* access to previous value. Must return function to use as getter. | |
* | |
* utils.overwriteProperty(chai.Assertion.prototype, 'ok', function (_super) { | |
* return function () { | |
* var obj = utils.flag(this, 'object'); | |
* if (obj instanceof Foo) { | |
* new chai.Assertion(obj.name).to.equal('bar'); | |
* } else { | |
* _super.call(this); | |
* } | |
* } | |
* }); | |
* | |
* | |
* Can also be accessed directly from `chai.Assertion`. | |
* | |
* chai.Assertion.overwriteProperty('foo', fn); | |
* | |
* Then can be used as any other assertion. | |
* | |
* expect(myFoo).to.be.ok; | |
* | |
* @param {Object} ctx object whose property is to be overwritten | |
* @param {String} name of property to overwrite | |
* @param {Function} getter function that returns a getter function to be used for name | |
* @name overwriteProperty | |
* @api public | |
*/ | |
module.exports = function (ctx, name, getter) { | |
var _get = Object.getOwnPropertyDescriptor(ctx, name) | |
, _super = function () {}; | |
if (_get && 'function' === typeof _get.get) | |
_super = _get.get | |
Object.defineProperty(ctx, name, | |
{ get: function () { | |
var result = getter(_super).call(this); | |
return result === undefined ? this : result; | |
} | |
, configurable: true | |
}); | |
}; | |
},{}],71:[function(require,module,exports){ | |
/*! | |
* Chai - test utility | |
* Copyright(c) 2012-2013 Jake Luer <jake@alogicalparadox.com> | |
* MIT Licensed | |
*/ | |
/*! | |
* Module dependancies | |
*/ | |
var flag = require('./flag'); | |
/** | |
* # test(object, expression) | |
* | |
* Test and object for expression. | |
* | |
* @param {Object} object (constructed Assertion) | |
* @param {Arguments} chai.Assertion.prototype.assert arguments | |
*/ | |
module.exports = function (obj, args) { | |
var negate = flag(obj, 'negate') | |
, expr = args[0]; | |
return negate ? !expr : expr; | |
}; | |
},{"./flag":59}],72:[function(require,module,exports){ | |
/*! | |
* Chai - transferFlags utility | |
* Copyright(c) 2012-2013 Jake Luer <jake@alogicalparadox.com> | |
* MIT Licensed | |
*/ | |
/** | |
* ### transferFlags(assertion, object, includeAll = true) | |
* | |
* Transfer all the flags for `assertion` to `object`. If | |
* `includeAll` is set to `false`, then the base Chai | |
* assertion flags (namely `object`, `ssfi`, and `message`) | |
* will not be transferred. | |
* | |
* | |
* var newAssertion = new Assertion(); | |
* utils.transferFlags(assertion, newAssertion); | |
* | |
* var anotherAsseriton = new Assertion(myObj); | |
* utils.transferFlags(assertion, anotherAssertion, false); | |
* | |
* @param {Assertion} assertion the assertion to transfer the flags from | |
* @param {Object} object the object to transfer the flags too; usually a new assertion | |
* @param {Boolean} includeAll | |
* @name getAllFlags | |
* @api private | |
*/ | |
module.exports = function (assertion, object, includeAll) { | |
var flags = assertion.__flags || (assertion.__flags = Object.create(null)); | |
if (!object.__flags) { | |
object.__flags = Object.create(null); | |
} | |
includeAll = arguments.length === 3 ? includeAll : true; | |
for (var flag in flags) { | |
if (includeAll || | |
(flag !== 'object' && flag !== 'ssfi' && flag != 'message')) { | |
object.__flags[flag] = flags[flag]; | |
} | |
} | |
}; | |
},{}],73:[function(require,module,exports){ | |
/*! | |
* Chai - type utility | |
* Copyright(c) 2012-2013 Jake Luer <jake@alogicalparadox.com> | |
* MIT Licensed | |
*/ | |
/*! | |
* Detectable javascript natives | |
*/ | |
var natives = { | |
'[object Arguments]': 'arguments' | |
, '[object Array]': 'array' | |
, '[object Date]': 'date' | |
, '[object Function]': 'function' | |
, '[object Number]': 'number' | |
, '[object RegExp]': 'regexp' | |
, '[object String]': 'string' | |
}; | |
/** | |
* ### type(object) | |
* | |
* Better implementation of `typeof` detection that can | |
* be used cross-browser. Handles the inconsistencies of | |
* Array, `null`, and `undefined` detection. | |
* | |
* utils.type({}) // 'object' | |
* utils.type(null) // `null' | |
* utils.type(undefined) // `undefined` | |
* utils.type([]) // `array` | |
* | |
* @param {Mixed} object to detect type of | |
* @name type | |
* @api private | |
*/ | |
module.exports = function (obj) { | |
var str = Object.prototype.toString.call(obj); | |
if (natives[str]) return natives[str]; | |
if (obj === null) return 'null'; | |
if (obj === undefined) return 'undefined'; | |
if (obj === Object(obj)) return 'object'; | |
return typeof obj; | |
}; | |
},{}],74:[function(require,module,exports){ | |
/*! | |
* assertion-error | |
* Copyright(c) 2013 Jake Luer <jake@qualiancy.com> | |
* MIT Licensed | |
*/ | |
/*! | |
* Return a function that will copy properties from | |
* one object to another excluding any originally | |
* listed. Returned function will create a new `{}`. | |
* | |
* @param {String} excluded properties ... | |
* @return {Function} | |
*/ | |
function exclude () { | |
var excludes = [].slice.call(arguments); | |
function excludeProps (res, obj) { | |
Object.keys(obj).forEach(function (key) { | |
if (!~excludes.indexOf(key)) res[key] = obj[key]; | |
}); | |
} | |
return function extendExclude () { | |
var args = [].slice.call(arguments) | |
, i = 0 | |
, res = {}; | |
for (; i < args.length; i++) { | |
excludeProps(res, args[i]); | |
} | |
return res; | |
}; | |
}; | |
/*! | |
* Primary Exports | |
*/ | |
module.exports = AssertionError; | |
/** | |
* ### AssertionError | |
* | |
* An extension of the JavaScript `Error` constructor for | |
* assertion and validation scenarios. | |
* | |
* @param {String} message | |
* @param {Object} properties to include (optional) | |
* @param {callee} start stack function (optional) | |
*/ | |
function AssertionError (message, _props, ssf) { | |
var extend = exclude('name', 'message', 'stack', 'constructor', 'toJSON') | |
, props = extend(_props || {}); | |
// default values | |
this.message = message || 'Unspecified AssertionError'; | |
this.showDiff = false; | |
// copy from properties | |
for (var key in props) { | |
this[key] = props[key]; | |
} | |
// capture stack trace | |
ssf = ssf || arguments.callee; | |
if (ssf && Error.captureStackTrace) { | |
Error.captureStackTrace(this, ssf); | |
} | |
} | |
/*! | |
* Inherit from Error.prototype | |
*/ | |
AssertionError.prototype = Object.create(Error.prototype); | |
/*! | |
* Statically set name | |
*/ | |
AssertionError.prototype.name = 'AssertionError'; | |
/*! | |
* Ensure correct constructor | |
*/ | |
AssertionError.prototype.constructor = AssertionError; | |
/** | |
* Allow errors to be converted to JSON for static transfer. | |
* | |
* @param {Boolean} include stack (default: `true`) | |
* @return {Object} object that can be `JSON.stringify` | |
*/ | |
AssertionError.prototype.toJSON = function (stack) { | |
var extend = exclude('constructor', 'toJSON', 'stack') | |
, props = extend({ name: this.name }, this); | |
// include stack if exists and not turned off | |
if (false !== stack && this.stack) { | |
props.stack = this.stack; | |
} | |
return props; | |
}; | |
},{}],75:[function(require,module,exports){ | |
module.exports = require('./lib/eql'); | |
},{"./lib/eql":76}],76:[function(require,module,exports){ | |
/*! | |
* deep-eql | |
* Copyright(c) 2013 Jake Luer <jake@alogicalparadox.com> | |
* MIT Licensed | |
*/ | |
/*! | |
* Module dependencies | |
*/ | |
var type = require('type-detect'); | |
/*! | |
* Buffer.isBuffer browser shim | |
*/ | |
var Buffer; | |
try { Buffer = require('buffer').Buffer; } | |
catch(ex) { | |
Buffer = {}; | |
Buffer.isBuffer = function() { return false; } | |
} | |
/*! | |
* Primary Export | |
*/ | |
module.exports = deepEqual; | |
/** | |
* Assert super-strict (egal) equality between | |
* two objects of any type. | |
* | |
* @param {Mixed} a | |
* @param {Mixed} b | |
* @param {Array} memoised (optional) | |
* @return {Boolean} equal match | |
*/ | |
function deepEqual(a, b, m) { | |
if (sameValue(a, b)) { | |
return true; | |
} else if ('date' === type(a)) { | |
return dateEqual(a, b); | |
} else if ('regexp' === type(a)) { | |
return regexpEqual(a, b); | |
} else if (Buffer.isBuffer(a)) { | |
return bufferEqual(a, b); | |
} else if ('arguments' === type(a)) { | |
return argumentsEqual(a, b, m); | |
} else if (!typeEqual(a, b)) { | |
return false; | |
} else if (('object' !== type(a) && 'object' !== type(b)) | |
&& ('array' !== type(a) && 'array' !== type(b))) { | |
return sameValue(a, b); | |
} else { | |
return objectEqual(a, b, m); | |
} | |
} | |
/*! | |
* Strict (egal) equality test. Ensures that NaN always | |
* equals NaN and `-0` does not equal `+0`. | |
* | |
* @param {Mixed} a | |
* @param {Mixed} b | |
* @return {Boolean} equal match | |
*/ | |
function sameValue(a, b) { | |
if (a === b) return a !== 0 || 1 / a === 1 / b; | |
return a !== a && b !== b; | |
} | |
/*! | |
* Compare the types of two given objects and | |
* return if they are equal. Note that an Array | |
* has a type of `array` (not `object`) and arguments | |
* have a type of `arguments` (not `array`/`object`). | |
* | |
* @param {Mixed} a | |
* @param {Mixed} b | |
* @return {Boolean} result | |
*/ | |
function typeEqual(a, b) { | |
return type(a) === type(b); | |
} | |
/*! | |
* Compare two Date objects by asserting that | |
* the time values are equal using `saveValue`. | |
* | |
* @param {Date} a | |
* @param {Date} b | |
* @return {Boolean} result | |
*/ | |
function dateEqual(a, b) { | |
if ('date' !== type(b)) return false; | |
return sameValue(a.getTime(), b.getTime()); | |
} | |
/*! | |
* Compare two regular expressions by converting them | |
* to string and checking for `sameValue`. | |
* | |
* @param {RegExp} a | |
* @param {RegExp} b | |
* @return {Boolean} result | |
*/ | |
function regexpEqual(a, b) { | |
if ('regexp' !== type(b)) return false; | |
return sameValue(a.toString(), b.toString()); | |
} | |
/*! | |
* Assert deep equality of two `arguments` objects. | |
* Unfortunately, these must be sliced to arrays | |
* prior to test to ensure no bad behavior. | |
* | |
* @param {Arguments} a | |
* @param {Arguments} b | |
* @param {Array} memoize (optional) | |
* @return {Boolean} result | |
*/ | |
function argumentsEqual(a, b, m) { | |
if ('arguments' !== type(b)) return false; | |
a = [].slice.call(a); | |
b = [].slice.call(b); | |
return deepEqual(a, b, m); | |
} | |
/*! | |
* Get enumerable properties of a given object. | |
* | |
* @param {Object} a | |
* @return {Array} property names | |
*/ | |
function enumerable(a) { | |
var res = []; | |
for (var key in a) res.push(key); | |
return res; | |
} | |
/*! | |
* Simple equality for flat iterable objects | |
* such as Arrays or Node.js buffers. | |
* | |
* @param {Iterable} a | |
* @param {Iterable} b | |
* @return {Boolean} result | |
*/ | |
function iterableEqual(a, b) { | |
if (a.length !== b.length) return false; | |
var i = 0; | |
var match = true; | |
for (; i < a.length; i++) { | |
if (a[i] !== b[i]) { | |
match = false; | |
break; | |
} | |
} | |
return match; | |
} | |
/*! | |
* Extension to `iterableEqual` specifically | |
* for Node.js Buffers. | |
* | |
* @param {Buffer} a | |
* @param {Mixed} b | |
* @return {Boolean} result | |
*/ | |
function bufferEqual(a, b) { | |
if (!Buffer.isBuffer(b)) return false; | |
return iterableEqual(a, b); | |
} | |
/*! | |
* Block for `objectEqual` ensuring non-existing | |
* values don't get in. | |
* | |
* @param {Mixed} object | |
* @return {Boolean} result | |
*/ | |
function isValue(a) { | |
return a !== null && a !== undefined; | |
} | |
/*! | |
* Recursively check the equality of two objects. | |
* Once basic sameness has been established it will | |
* defer to `deepEqual` for each enumerable key | |
* in the object. | |
* | |
* @param {Mixed} a | |
* @param {Mixed} b | |
* @return {Boolean} result | |
*/ | |
function objectEqual(a, b, m) { | |
if (!isValue(a) || !isValue(b)) { | |
return false; | |
} | |
if (a.prototype !== b.prototype) { | |
return false; | |
} | |
var i; | |
if (m) { | |
for (i = 0; i < m.length; i++) { | |
if ((m[i][0] === a && m[i][1] === b) | |
|| (m[i][0] === b && m[i][1] === a)) { | |
return true; | |
} | |
} | |
} else { | |
m = []; | |
} | |
try { | |
var ka = enumerable(a); | |
var kb = enumerable(b); | |
} catch (ex) { | |
return false; | |
} | |
ka.sort(); | |
kb.sort(); | |
if (!iterableEqual(ka, kb)) { | |
return false; | |
} | |
m.push([ a, b ]); | |
var key; | |
for (i = ka.length - 1; i >= 0; i--) { | |
key = ka[i]; | |
if (!deepEqual(a[key], b[key], m)) { | |
return false; | |
} | |
} | |
return true; | |
} | |
},{"buffer":42,"type-detect":77}],77:[function(require,module,exports){ | |
module.exports = require('./lib/type'); | |
},{"./lib/type":78}],78:[function(require,module,exports){ | |
/*! | |
* type-detect | |
* Copyright(c) 2013 jake luer <jake@alogicalparadox.com> | |
* MIT Licensed | |
*/ | |
/*! | |
* Primary Exports | |
*/ | |
var exports = module.exports = getType; | |
/*! | |
* Detectable javascript natives | |
*/ | |
var natives = { | |
'[object Array]': 'array' | |
, '[object RegExp]': 'regexp' | |
, '[object Function]': 'function' | |
, '[object Arguments]': 'arguments' | |
, '[object Date]': 'date' | |
}; | |
/** | |
* ### typeOf (obj) | |
* | |
* Use several different techniques to determine | |
* the type of object being tested. | |
* | |
* | |
* @param {Mixed} object | |
* @return {String} object type | |
* @api public | |
*/ | |
function getType (obj) { | |
var str = Object.prototype.toString.call(obj); | |
if (natives[str]) return natives[str]; | |
if (obj === null) return 'null'; | |
if (obj === undefined) return 'undefined'; | |
if (obj === Object(obj)) return 'object'; | |
return typeof obj; | |
} | |
exports.Library = Library; | |
/** | |
* ### Library | |
* | |
* Create a repository for custom type detection. | |
* | |
* ```js | |
* var lib = new type.Library; | |
* ``` | |
* | |
*/ | |
function Library () { | |
this.tests = {}; | |
} | |
/** | |
* #### .of (obj) | |
* | |
* Expose replacement `typeof` detection to the library. | |
* | |
* ```js | |
* if ('string' === lib.of('hello world')) { | |
* // ... | |
* } | |
* ``` | |
* | |
* @param {Mixed} object to test | |
* @return {String} type | |
*/ | |
Library.prototype.of = getType; | |
/** | |
* #### .define (type, test) | |
* | |
* Add a test to for the `.test()` assertion. | |
* | |
* Can be defined as a regular expression: | |
* | |
* ```js | |
* lib.define('int', /^[0-9]+$/); | |
* ``` | |
* | |
* ... or as a function: | |
* | |
* ```js | |
* lib.define('bln', function (obj) { | |
* if ('boolean' === lib.of(obj)) return true; | |
* var blns = [ 'yes', 'no', 'true', 'false', 1, 0 ]; | |
* if ('string' === lib.of(obj)) obj = obj.toLowerCase(); | |
* return !! ~blns.indexOf(obj); | |
* }); | |
* ``` | |
* | |
* @param {String} type | |
* @param {RegExp|Function} test | |
* @api public | |
*/ | |
Library.prototype.define = function (type, test) { | |
if (arguments.length === 1) return this.tests[type]; | |
this.tests[type] = test; | |
return this; | |
}; | |
/** | |
* #### .test (obj, test) | |
* | |
* Assert that an object is of type. Will first | |
* check natives, and if that does not pass it will | |
* use the user defined custom tests. | |
* | |
* ```js | |
* assert(lib.test('1', 'int')); | |
* assert(lib.test('yes', 'bln')); | |
* ``` | |
* | |
* @param {Mixed} object | |
* @param {String} type | |
* @return {Boolean} result | |
* @api public | |
*/ | |
Library.prototype.test = function (obj, type) { | |
if (type === getType(obj)) return true; | |
var test = this.tests[type]; | |
if (test && 'regexp' === getType(test)) { | |
return test.test(obj); | |
} else if (test && 'function' === getType(test)) { | |
return test(obj); | |
} else { | |
throw new ReferenceError('Type test "' + type + '" not defined or invalid.'); | |
} | |
}; | |
},{}],79:[function(require,module,exports){ | |
if (typeof Object.create === 'function') { | |
// implementation from standard node.js 'util' module | |
module.exports = function inherits(ctor, superCtor) { | |
ctor.super_ = superCtor | |
ctor.prototype = Object.create(superCtor.prototype, { | |
constructor: { | |
value: ctor, | |
enumerable: false, | |
writable: true, | |
configurable: true | |
} | |
}); | |
}; | |
} else { | |
// old school shim for old browsers | |
module.exports = function inherits(ctor, superCtor) { | |
ctor.super_ = superCtor | |
var TempCtor = function () {} | |
TempCtor.prototype = superCtor.prototype | |
ctor.prototype = new TempCtor() | |
ctor.prototype.constructor = ctor | |
} | |
} | |
},{}],80:[function(require,module,exports){ | |
'use strict'; | |
module.exports = INTERNAL; | |
function INTERNAL() {} | |
},{}],81:[function(require,module,exports){ | |
'use strict'; | |
var Promise = require('./promise'); | |
var reject = require('./reject'); | |
var resolve = require('./resolve'); | |
var INTERNAL = require('./INTERNAL'); | |
var handlers = require('./handlers'); | |
module.exports = all; | |
function all(iterable) { | |
if (Object.prototype.toString.call(iterable) !== '[object Array]') { | |
return reject(new TypeError('must be an array')); | |
} | |
var len = iterable.length; | |
var called = false; | |
if (!len) { | |
return resolve([]); | |
} | |
var values = new Array(len); | |
var resolved = 0; | |
var i = -1; | |
var promise = new Promise(INTERNAL); | |
while (++i < len) { | |
allResolver(iterable[i], i); | |
} | |
return promise; | |
function allResolver(value, i) { | |
resolve(value).then(resolveFromAll, function (error) { | |
if (!called) { | |
called = true; | |
handlers.reject(promise, error); | |
} | |
}); | |
function resolveFromAll(outValue) { | |
values[i] = outValue; | |
if (++resolved === len & !called) { | |
called = true; | |
handlers.resolve(promise, values); | |
} | |
} | |
} | |
} | |
},{"./INTERNAL":80,"./handlers":82,"./promise":84,"./reject":87,"./resolve":88}],82:[function(require,module,exports){ | |
'use strict'; | |
var tryCatch = require('./tryCatch'); | |
var resolveThenable = require('./resolveThenable'); | |
var states = require('./states'); | |
exports.resolve = function (self, value) { | |
var result = tryCatch(getThen, value); | |
if (result.status === 'error') { | |
return exports.reject(self, result.value); | |
} | |
var thenable = result.value; | |
if (thenable) { | |
resolveThenable.safely(self, thenable); | |
} else { | |
self.state = states.FULFILLED; | |
self.outcome = value; | |
var i = -1; | |
var len = self.queue.length; | |
while (++i < len) { | |
self.queue[i].callFulfilled(value); | |
} | |
} | |
return self; | |
}; | |
exports.reject = function (self, error) { | |
self.state = states.REJECTED; | |
self.outcome = error; | |
var i = -1; | |
var len = self.queue.length; | |
while (++i < len) { | |
self.queue[i].callRejected(error); | |
} | |
return self; | |
}; | |
function getThen(obj) { | |
// Make sure we only access the accessor once as required by the spec | |
var then = obj && obj.then; | |
if (obj && typeof obj === 'object' && typeof then === 'function') { | |
return function appyThen() { | |
then.apply(obj, arguments); | |
}; | |
} | |
} | |
},{"./resolveThenable":89,"./states":90,"./tryCatch":91}],83:[function(require,module,exports){ | |
module.exports = exports = require('./promise'); | |
exports.resolve = require('./resolve'); | |
exports.reject = require('./reject'); | |
exports.all = require('./all'); | |
exports.race = require('./race'); | |
},{"./all":81,"./promise":84,"./race":86,"./reject":87,"./resolve":88}],84:[function(require,module,exports){ | |
'use strict'; | |
var unwrap = require('./unwrap'); | |
var INTERNAL = require('./INTERNAL'); | |
var resolveThenable = require('./resolveThenable'); | |
var states = require('./states'); | |
var QueueItem = require('./queueItem'); | |
module.exports = Promise; | |
function Promise(resolver) { | |
if (!(this instanceof Promise)) { | |
return new Promise(resolver); | |
} | |
if (typeof resolver !== 'function') { | |
throw new TypeError('resolver must be a function'); | |
} | |
this.state = states.PENDING; | |
this.queue = []; | |
this.outcome = void 0; | |
if (resolver !== INTERNAL) { | |
resolveThenable.safely(this, resolver); | |
} | |
} | |
Promise.prototype['catch'] = function (onRejected) { | |
return this.then(null, onRejected); | |
}; | |
Promise.prototype.then = function (onFulfilled, onRejected) { | |
if (typeof onFulfilled !== 'function' && this.state === states.FULFILLED || | |
typeof onRejected !== 'function' && this.state === states.REJECTED) { | |
return this; | |
} | |
var promise = new Promise(INTERNAL); | |
if (this.state !== states.PENDING) { | |
var resolver = this.state === states.FULFILLED ? onFulfilled: onRejected; | |
unwrap(promise, resolver, this.outcome); | |
} else { | |
this.queue.push(new QueueItem(promise, onFulfilled, onRejected)); | |
} | |
return promise; | |
}; | |
},{"./INTERNAL":80,"./queueItem":85,"./resolveThenable":89,"./states":90,"./unwrap":92}],85:[function(require,module,exports){ | |
'use strict'; | |
var handlers = require('./handlers'); | |
var unwrap = require('./unwrap'); | |
module.exports = QueueItem; | |
function QueueItem(promise, onFulfilled, onRejected) { | |
this.promise = promise; | |
if (typeof onFulfilled === 'function') { | |
this.onFulfilled = onFulfilled; | |
this.callFulfilled = this.otherCallFulfilled; | |
} | |
if (typeof onRejected === 'function') { | |
this.onRejected = onRejected; | |
this.callRejected = this.otherCallRejected; | |
} | |
} | |
QueueItem.prototype.callFulfilled = function (value) { | |
handlers.resolve(this.promise, value); | |
}; | |
QueueItem.prototype.otherCallFulfilled = function (value) { | |
unwrap(this.promise, this.onFulfilled, value); | |
}; | |
QueueItem.prototype.callRejected = function (value) { | |
handlers.reject(this.promise, value); | |
}; | |
QueueItem.prototype.otherCallRejected = function (value) { | |
unwrap(this.promise, this.onRejected, value); | |
}; | |
},{"./handlers":82,"./unwrap":92}],86:[function(require,module,exports){ | |
'use strict'; | |
var Promise = require('./promise'); | |
var reject = require('./reject'); | |
var resolve = require('./resolve'); | |
var INTERNAL = require('./INTERNAL'); | |
var handlers = require('./handlers'); | |
module.exports = race; | |
function race(iterable) { | |
if (Object.prototype.toString.call(iterable) !== '[object Array]') { | |
return reject(new TypeError('must be an array')); | |
} | |
var len = iterable.length; | |
var called = false; | |
if (!len) { | |
return resolve([]); | |
} | |
var resolved = 0; | |
var i = -1; | |
var promise = new Promise(INTERNAL); | |
while (++i < len) { | |
resolver(iterable[i]); | |
} | |
return promise; | |
function resolver(value) { | |
resolve(value).then(function (response) { | |
if (!called) { | |
called = true; | |
handlers.resolve(promise, response); | |
} | |
}, function (error) { | |
if (!called) { | |
called = true; | |
handlers.reject(promise, error); | |
} | |
}); | |
} | |
} | |
},{"./INTERNAL":80,"./handlers":82,"./promise":84,"./reject":87,"./resolve":88}],87:[function(require,module,exports){ | |
'use strict'; | |
var Promise = require('./promise'); | |
var INTERNAL = require('./INTERNAL'); | |
var handlers = require('./handlers'); | |
module.exports = reject; | |
function reject(reason) { | |
var promise = new Promise(INTERNAL); | |
return handlers.reject(promise, reason); | |
} | |
},{"./INTERNAL":80,"./handlers":82,"./promise":84}],88:[function(require,module,exports){ | |
'use strict'; | |
var Promise = require('./promise'); | |
var INTERNAL = require('./INTERNAL'); | |
var handlers = require('./handlers'); | |
module.exports = resolve; | |
var FALSE = handlers.resolve(new Promise(INTERNAL), false); | |
var NULL = handlers.resolve(new Promise(INTERNAL), null); | |
var UNDEFINED = handlers.resolve(new Promise(INTERNAL), void 0); | |
var ZERO = handlers.resolve(new Promise(INTERNAL), 0); | |
var EMPTYSTRING = handlers.resolve(new Promise(INTERNAL), ''); | |
function resolve(value) { | |
if (value) { | |
if (value instanceof Promise) { | |
return value; | |
} | |
return handlers.resolve(new Promise(INTERNAL), value); | |
} | |
var valueType = typeof value; | |
switch (valueType) { | |
case 'boolean': | |
return FALSE; | |
case 'undefined': | |
return UNDEFINED; | |
case 'object': | |
return NULL; | |
case 'number': | |
return ZERO; | |
case 'string': | |
return EMPTYSTRING; | |
} | |
} | |
},{"./INTERNAL":80,"./handlers":82,"./promise":84}],89:[function(require,module,exports){ | |
'use strict'; | |
var handlers = require('./handlers'); | |
var tryCatch = require('./tryCatch'); | |
function safelyResolveThenable(self, thenable) { | |
// Either fulfill, reject or reject with error | |
var called = false; | |
function onError(value) { | |
if (called) { | |
return; | |
} | |
called = true; | |
handlers.reject(self, value); | |
} | |
function onSuccess(value) { | |
if (called) { | |
return; | |
} | |
called = true; | |
handlers.resolve(self, value); | |
} | |
function tryToUnwrap() { | |
thenable(onSuccess, onError); | |
} | |
var result = tryCatch(tryToUnwrap); | |
if (result.status === 'error') { | |
onError(result.value); | |
} | |
} | |
exports.safely = safelyResolveThenable; | |
},{"./handlers":82,"./tryCatch":91}],90:[function(require,module,exports){ | |
// Lazy man's symbols for states | |
exports.REJECTED = ['REJECTED']; | |
exports.FULFILLED = ['FULFILLED']; | |
exports.PENDING = ['PENDING']; | |
},{}],91:[function(require,module,exports){ | |
'use strict'; | |
module.exports = tryCatch; | |
function tryCatch(func, value) { | |
var out = {}; | |
try { | |
out.value = func(value); | |
out.status = 'success'; | |
} catch (e) { | |
out.status = 'error'; | |
out.value = e; | |
} | |
return out; | |
} | |
},{}],92:[function(require,module,exports){ | |
'use strict'; | |
var immediate = require('immediate'); | |
var handlers = require('./handlers'); | |
module.exports = unwrap; | |
function unwrap(promise, func, value) { | |
immediate(function () { | |
var returnValue; | |
try { | |
returnValue = func(value); | |
} catch (e) { | |
return handlers.reject(promise, e); | |
} | |
if (returnValue === promise) { | |
handlers.reject(promise, new TypeError('Cannot resolve promise with itself')); | |
} else { | |
handlers.resolve(promise, returnValue); | |
} | |
}); | |
} | |
},{"./handlers":82,"immediate":93}],93:[function(require,module,exports){ | |
'use strict'; | |
var types = [ | |
require('./nextTick'), | |
require('./mutation.js'), | |
require('./messageChannel'), | |
require('./stateChange'), | |
require('./timeout') | |
]; | |
var draining; | |
var currentQueue; | |
var queueIndex = -1; | |
var queue = []; | |
function cleanUpNextTick() { | |
draining = false; | |
if (currentQueue && currentQueue.length) { | |
queue = currentQueue.concat(queue); | |
} else { | |
queueIndex = -1; | |
} | |
if (queue.length) { | |
nextTick(); | |
} | |
} | |
//named nextTick for less confusing stack traces | |
function nextTick() { | |
draining = true; | |
var len = queue.length; | |
var timeout = setTimeout(cleanUpNextTick); | |
while (len) { | |
currentQueue = queue; | |
queue = []; | |
while (++queueIndex < len) { | |
currentQueue[queueIndex](); | |
} | |
queueIndex = -1; | |
len = queue.length; | |
} | |
queueIndex = -1; | |
draining = false; | |
clearTimeout(timeout); | |
} | |
var scheduleDrain; | |
var i = -1; | |
var len = types.length; | |
while (++ i < len) { | |
if (types[i] && types[i].test && types[i].test()) { | |
scheduleDrain = types[i].install(nextTick); | |
break; | |
} | |
} | |
module.exports = immediate; | |
function immediate(task) { | |
if (queue.push(task) === 1 && !draining) { | |
scheduleDrain(); | |
} | |
} | |
},{"./messageChannel":94,"./mutation.js":95,"./nextTick":41,"./stateChange":96,"./timeout":97}],94:[function(require,module,exports){ | |
(function (global){ | |
'use strict'; | |
exports.test = function () { | |
if (global.setImmediate) { | |
// we can only get here in IE10 | |
// which doesn't handel postMessage well | |
return false; | |
} | |
return typeof global.MessageChannel !== 'undefined'; | |
}; | |
exports.install = function (func) { | |
var channel = new global.MessageChannel(); | |
channel.port1.onmessage = func; | |
return function () { | |
channel.port2.postMessage(0); | |
}; | |
}; | |
}).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
},{}],95:[function(require,module,exports){ | |
(function (global){ | |
'use strict'; | |
//based off rsvp https://github.com/tildeio/rsvp.js | |
//license https://github.com/tildeio/rsvp.js/blob/master/LICENSE | |
//https://github.com/tildeio/rsvp.js/blob/master/lib/rsvp/asap.js | |
var Mutation = global.MutationObserver || global.WebKitMutationObserver; | |
exports.test = function () { | |
return Mutation; | |
}; | |
exports.install = function (handle) { | |
var called = 0; | |
var observer = new Mutation(handle); | |
var element = global.document.createTextNode(''); | |
observer.observe(element, { | |
characterData: true | |
}); | |
return function () { | |
element.data = (called = ++called % 2); | |
}; | |
}; | |
}).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
},{}],96:[function(require,module,exports){ | |
(function (global){ | |
'use strict'; | |
exports.test = function () { | |
return 'document' in global && 'onreadystatechange' in global.document.createElement('script'); | |
}; | |
exports.install = function (handle) { | |
return function () { | |
// Create a <script> element; its readystatechange event will be fired asynchronously once it is inserted | |
// into the document. Do so, thus queuing up the task. Remember to clean up once it's been called. | |
var scriptEl = global.document.createElement('script'); | |
scriptEl.onreadystatechange = function () { | |
handle(); | |
scriptEl.onreadystatechange = null; | |
scriptEl.parentNode.removeChild(scriptEl); | |
scriptEl = null; | |
}; | |
global.document.documentElement.appendChild(scriptEl); | |
return handle; | |
}; | |
}; | |
}).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
},{}],97:[function(require,module,exports){ | |
'use strict'; | |
exports.test = function () { | |
return true; | |
}; | |
exports.install = function (t) { | |
return function () { | |
setTimeout(t, 0); | |
}; | |
}; | |
},{}],98:[function(require,module,exports){ | |
(function (process){ | |
(function (mochaAsPromised) { | |
"use strict"; | |
function findNodeJSMocha(moduleToTest, suffix, accumulator) { | |
if (accumulator === undefined) { | |
accumulator = []; | |
} | |
if (moduleToTest.id.indexOf(suffix, moduleToTest.id.length - suffix.length) !== -1 && moduleToTest.exports) { | |
accumulator.push(moduleToTest.exports); | |
} | |
moduleToTest.children.forEach(function (child) { | |
findNodeJSMocha(child, suffix, accumulator); | |
}); | |
return accumulator; | |
} | |
// Module systems magic dance. | |
if (typeof require === "function" && typeof exports === "object" && typeof module === "object") { | |
// Node.js: plug in automatically, if no argument is provided. This is a good idea since one can run Mocha tests | |
// using the Mocha test runner from either a locally-installed package, or from a globally-installed one. | |
// In the latter case, naively plugging in `require("mocha")` would end up duck-punching the wrong instance, | |
// so we provide this shortcut to auto-detect which Mocha package needs to be duck-punched. | |
module.exports = function (mochaModules) { | |
if (mochaModules === undefined) { | |
if (typeof process === "object" && Object.prototype.toString.call(process) === "[object process]") { | |
// We're in *real* Node.js, not in a browserify-like environment. Do automatic detection logic. | |
// Funky syntax prevents Browserify from detecting the require, since it's needed for Node.js-only | |
// stuff. | |
var path = (require)("path"); | |
var suffix = path.join("mocha", "lib", "mocha.js"); | |
mochaModules = findNodeJSMocha(require.main, suffix); | |
if (mochaModules === undefined) { | |
throw new Error("Attempted to automatically plug in to Mocha, but could not detect a " + | |
"running Mocha module."); | |
} | |
} else if (typeof Mocha !== "undefined") { | |
// We're in a browserify-like emulation environment. Try the `Mocha` global. | |
mochaModules = [Mocha]; | |
} else { | |
throw new Error("Attempted to automatically plug in to Mocha, but could not detect the " + | |
"environment. Plug in manually by passing the running Mocha module."); | |
} | |
} | |
mochaModules.forEach(mochaAsPromised); | |
}; | |
} else if (typeof define === "function" && define.amd) { | |
// AMD | |
define(function () { | |
return mochaAsPromised; | |
}); | |
} else { | |
// Other environment (usually <script> tag): plug in global `Mocha` directly and automatically. | |
mochaAsPromised(Mocha); | |
} | |
}((function () { | |
"use strict"; | |
function getThen(x) { | |
if ((typeof x === "object" || typeof x === "function") && x !== null) { | |
var then = x.then; | |
if (typeof then === "function") { | |
return then; | |
} | |
} | |
} | |
return function mochaAsPromised(mocha) { | |
if (mocha._mochaAsPromisedLoadedAlready) { | |
return; | |
} | |
mocha._mochaAsPromisedLoadedAlready = true; | |
// Soooo this is an awesome hack. | |
// Here's the idea: Mocha `Runnable` instances have a `fn` property, representing the test to run. Async tests | |
// in Mocha are done with `fn`s that take a `done` callback, and call it with either nothing (success) or an | |
// error (failure). We want to add another paradigm for async tests: `fn`s that take no arguments, but return a | |
// promise. Promise fulfillment corresponds to success, and rejection to failure. | |
// To do this, we translate promise-returning `fn`s into callback-calling ones. So Mocha never sees the promisey | |
// functions, but instead sees a wrapper around them that we provide. The only trick is, how and when to insert | |
// this wrapper into a Mocha `Runnable`? | |
// We accomplish this by intercepting all [[Put]]s to the `fn` property of *any* `Runnable`. That is, we define | |
// a setter for `fn` on `Runnable.prototype` (!). So when Mocha sets the `fn` property of a runnable inside the | |
// `Runnable` constructor (i.e. `this.fn = fn`), it is immediately translated into a wrapped version, which is | |
// then stored as a `_wrappedFn` instance property. Finally we define a getter for `fn` on `Runnable.prototype` | |
// as well, so that any retrievals of the `fn` property (e.g. in `Runnable.prototype.run`) return the wrapped | |
// version we've stored. | |
// We also need to override the `async` property, since the Mocha constructor sets it by looking at the `fn` | |
// passed in, and not at the `this.fn` property we have control over. We just give it a getter that performs the | |
// same logic as the Mocha constructor, and a no-op setter (so that it silently ignores Mocha's attempts to set | |
// it). | |
// ISN'T THIS COOL!? | |
Object.defineProperties(mocha.Runnable.prototype, { | |
fn: { | |
configurable: true, | |
enumerable: true, | |
get: function () { | |
return this._wrappedFn; | |
}, | |
set: function (fn) { | |
this._wrappedFn = function (done) { | |
// Run the original `fn`, passing along `done` for the case in which it's callback-asynchronous. | |
// Make sure to forward the `this` context, since you can set variables and stuff on it to share | |
// within a suite. | |
var retVal = fn.call(this, done); | |
var then = getThen(retVal); | |
if (then) { | |
// If we get a promise back... | |
then.call( | |
retVal, | |
function () { | |
// On fulfillment, ignore the fulfillment value and call `done()` with no arguments. | |
done(); | |
}, | |
function (reason) { | |
// On rejection, make sure there's a rejection reason, then call `done` with it. | |
if (reason === null || reason === undefined) { | |
reason = new Error("Promise rejected with no rejection reason."); | |
} | |
done(reason); | |
} | |
); | |
} else if (fn.length === 0) { | |
// If `fn` is synchronous (i.e. didn't have a `done` parameter and didn't return a promise), | |
// call `done` now. (If it's callback-asynchronous, `fn` will call `done` eventually since | |
// we passed it in above.) | |
done(); | |
} | |
}; | |
this._wrappedFn.toString = function () { | |
return fn.toString(); | |
}; | |
} | |
}, | |
async: { | |
configurable: true, | |
enumerable: true, | |
get: function () { | |
return typeof this._wrappedFn === "function"; | |
}, | |
set: function () { | |
// Ignore Mocha trying to set this; it doesn't know the whole picture. | |
} | |
} | |
}); | |
}; | |
}()))); | |
}).call(this,require("/Users/nlawson/workspace-node/pouchdb-authentication/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js")) | |
},{"/Users/nlawson/workspace-node/pouchdb-authentication/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":46,"path":47}],99:[function(require,module,exports){ | |
"use strict"; | |
// Extends method | |
// (taken from http://code.jquery.com/jquery-1.9.0.js) | |
// Populate the class2type map | |
var class2type = {}; | |
var types = [ | |
"Boolean", "Number", "String", "Function", "Array", | |
"Date", "RegExp", "Object", "Error" | |
]; | |
for (var i = 0; i < types.length; i++) { | |
var typename = types[i]; | |
class2type["[object " + typename + "]"] = typename.toLowerCase(); | |
} | |
var core_toString = class2type.toString; | |
var core_hasOwn = class2type.hasOwnProperty; | |
function type(obj) { | |
if (obj === null) { | |
return String(obj); | |
} | |
return typeof obj === "object" || typeof obj === "function" ? | |
class2type[core_toString.call(obj)] || "object" : | |
typeof obj; | |
} | |
function isWindow(obj) { | |
return obj !== null && obj === obj.window; | |
} | |
function isPlainObject(obj) { | |
// Must be an Object. | |
// Because of IE, we also have to check the presence of | |
// the constructor property. | |
// Make sure that DOM nodes and window objects don't pass through, as well | |
if (!obj || type(obj) !== "object" || obj.nodeType || isWindow(obj)) { | |
return false; | |
} | |
try { | |
// Not own constructor property must be Object | |
if (obj.constructor && | |
!core_hasOwn.call(obj, "constructor") && | |
!core_hasOwn.call(obj.constructor.prototype, "isPrototypeOf")) { | |
return false; | |
} | |
} catch ( e ) { | |
// IE8,9 Will throw exceptions on certain host objects #9897 | |
return false; | |
} | |
// Own properties are enumerated firstly, so to speed up, | |
// if last one is own, then all properties are own. | |
var key; | |
for (key in obj) {} | |
return key === undefined || core_hasOwn.call(obj, key); | |
} | |
function isFunction(obj) { | |
return type(obj) === "function"; | |
} | |
var isArray = Array.isArray || function (obj) { | |
return type(obj) === "array"; | |
}; | |
function extend() { | |
// originally extend() was recursive, but this ended up giving us | |
// "call stack exceeded", so it's been unrolled to use a literal stack | |
// (see https://github.com/pouchdb/pouchdb/issues/2543) | |
var stack = []; | |
var i = -1; | |
var len = arguments.length; | |
var args = new Array(len); | |
while (++i < len) { | |
args[i] = arguments[i]; | |
} | |
var container = {}; | |
stack.push({args: args, result: {container: container, key: 'key'}}); | |
var next; | |
while ((next = stack.pop())) { | |
extendInner(stack, next.args, next.result); | |
} | |
return container.key; | |
} | |
function extendInner(stack, args, result) { | |
var options, name, src, copy, copyIsArray, clone, | |
target = args[0] || {}, | |
i = 1, | |
length = args.length, | |
deep = false, | |
numericStringRegex = /\d+/, | |
optionsIsArray; | |
// Handle a deep copy situation | |
if (typeof target === "boolean") { | |
deep = target; | |
target = args[1] || {}; | |
// skip the boolean and the target | |
i = 2; | |
} | |
// Handle case when target is a string or something (possible in deep copy) | |
if (typeof target !== "object" && !isFunction(target)) { | |
target = {}; | |
} | |
// extend jQuery itself if only one argument is passed | |
if (length === i) { | |
/* jshint validthis: true */ | |
target = this; | |
--i; | |
} | |
for (; i < length; i++) { | |
// Only deal with non-null/undefined values | |
if ((options = args[i]) != null) { | |
optionsIsArray = isArray(options); | |
// Extend the base object | |
for (name in options) { | |
//if (options.hasOwnProperty(name)) { | |
if (!(name in Object.prototype)) { | |
if (optionsIsArray && !numericStringRegex.test(name)) { | |
continue; | |
} | |
src = target[name]; | |
copy = options[name]; | |
// Prevent never-ending loop | |
if (target === copy) { | |
continue; | |
} | |
// Recurse if we're merging plain objects or arrays | |
if (deep && copy && (isPlainObject(copy) || | |
(copyIsArray = isArray(copy)))) { | |
if (copyIsArray) { | |
copyIsArray = false; | |
clone = src && isArray(src) ? src : []; | |
} else { | |
clone = src && isPlainObject(src) ? src : {}; | |
} | |
// Never move original objects, clone them | |
stack.push({ | |
args: [deep, clone, copy], | |
result: { | |
container: target, | |
key: name | |
} | |
}); | |
// Don't bring in undefined values | |
} else if (copy !== undefined) { | |
if (!(isArray(options) && isFunction(copy))) { | |
target[name] = copy; | |
} | |
} | |
} | |
} | |
} | |
} | |
// "Return" the modified object by setting the key | |
// on the given container | |
result.container[result.key] = target; | |
} | |
module.exports = extend; | |
},{}],100:[function(require,module,exports){ | |
'use strict'; | |
// allow external plugins to require('pouchdb/extras/ajax') | |
module.exports = require('../lib/deps/ajax'); | |
},{"../lib/deps/ajax":117}],101:[function(require,module,exports){ | |
'use strict'; | |
// allow external plugins to require('pouchdb/extras/promise') | |
module.exports = require('../lib/deps/promise'); | |
},{"../lib/deps/promise":126}],102:[function(require,module,exports){ | |
"use strict"; | |
var utils = require('./utils'); | |
var merge = require('./merge'); | |
var errors = require('./deps/errors'); | |
var EventEmitter = require('events').EventEmitter; | |
var upsert = require('./deps/upsert'); | |
var Changes = require('./changes'); | |
var Promise = utils.Promise; | |
/* | |
* A generic pouch adapter | |
*/ | |
// returns first element of arr satisfying callback predicate | |
function arrayFirst(arr, callback) { | |
for (var i = 0; i < arr.length; i++) { | |
if (callback(arr[i], i) === true) { | |
return arr[i]; | |
} | |
} | |
return false; | |
} | |
// Wrapper for functions that call the bulkdocs api with a single doc, | |
// if the first result is an error, return an error | |
function yankError(callback) { | |
return function (err, results) { | |
if (err || (results[0] && results[0].error)) { | |
callback(err || results[0]); | |
} else { | |
callback(null, results.length ? results[0] : results); | |
} | |
}; | |
} | |
// for every node in a revision tree computes its distance from the closest | |
// leaf | |
function computeHeight(revs) { | |
var height = {}; | |
var edges = []; | |
merge.traverseRevTree(revs, function (isLeaf, pos, id, prnt) { | |
var rev = pos + "-" + id; | |
if (isLeaf) { | |
height[rev] = 0; | |
} | |
if (prnt !== undefined) { | |
edges.push({from: prnt, to: rev}); | |
} | |
return rev; | |
}); | |
edges.reverse(); | |
edges.forEach(function (edge) { | |
if (height[edge.from] === undefined) { | |
height[edge.from] = 1 + height[edge.to]; | |
} else { | |
height[edge.from] = Math.min(height[edge.from], 1 + height[edge.to]); | |
} | |
}); | |
return height; | |
} | |
function allDocsKeysQuery(api, opts, callback) { | |
var keys = ('limit' in opts) ? | |
opts.keys.slice(opts.skip, opts.limit + opts.skip) : | |
(opts.skip > 0) ? opts.keys.slice(opts.skip) : opts.keys; | |
if (opts.descending) { | |
keys.reverse(); | |
} | |
if (!keys.length) { | |
return api._allDocs({limit: 0}, callback); | |
} | |
var finalResults = { | |
offset: opts.skip | |
}; | |
return Promise.all(keys.map(function (key) { | |
var subOpts = utils.extend(true, {key: key, deleted: 'ok'}, opts); | |
['limit', 'skip', 'keys'].forEach(function (optKey) { | |
delete subOpts[optKey]; | |
}); | |
return new Promise(function (resolve, reject) { | |
api._allDocs(subOpts, function (err, res) { | |
if (err) { | |
return reject(err); | |
} | |
finalResults.total_rows = res.total_rows; | |
resolve(res.rows[0] || {key: key, error: 'not_found'}); | |
}); | |
}); | |
})).then(function (results) { | |
finalResults.rows = results; | |
return finalResults; | |
}); | |
} | |
utils.inherits(AbstractPouchDB, EventEmitter); | |
module.exports = AbstractPouchDB; | |
function AbstractPouchDB() { | |
EventEmitter.call(this); | |
} | |
AbstractPouchDB.prototype.post = | |
utils.adapterFun('post', function (doc, opts, callback) { | |
if (typeof opts === 'function') { | |
callback = opts; | |
opts = {}; | |
} | |
if (typeof doc !== 'object' || Array.isArray(doc)) { | |
return callback(errors.error(errors.NOT_AN_OBJECT)); | |
} | |
this.bulkDocs({docs: [doc]}, opts, yankError(callback)); | |
}); | |
AbstractPouchDB.prototype.put = | |
utils.adapterFun('put', utils.getArguments(function (args) { | |
var temp, temptype, opts, callback; | |
var doc = args.shift(); | |
var id = '_id' in doc; | |
if (typeof doc !== 'object' || Array.isArray(doc)) { | |
callback = args.pop(); | |
return callback(errors.error(errors.NOT_AN_OBJECT)); | |
} | |
doc = utils.clone(doc); | |
while (true) { | |
temp = args.shift(); | |
temptype = typeof temp; | |
if (temptype === "string" && !id) { | |
doc._id = temp; | |
id = true; | |
} else if (temptype === "string" && id && !('_rev' in doc)) { | |
doc._rev = temp; | |
} else if (temptype === "object") { | |
opts = temp; | |
} else if (temptype === "function") { | |
callback = temp; | |
} | |
if (!args.length) { | |
break; | |
} | |
} | |
opts = opts || {}; | |
var error = utils.invalidIdError(doc._id); | |
if (error) { | |
return callback(error); | |
} | |
if (utils.isLocalId(doc._id) && typeof this._putLocal === 'function') { | |
if (doc._deleted) { | |
return this._removeLocal(doc, callback); | |
} else { | |
return this._putLocal(doc, callback); | |
} | |
} | |
this.bulkDocs({docs: [doc]}, opts, yankError(callback)); | |
})); | |
AbstractPouchDB.prototype.putAttachment = | |
utils.adapterFun('putAttachment', function (docId, attachmentId, rev, | |
blob, type, callback) { | |
var api = this; | |
if (typeof type === 'function') { | |
callback = type; | |
type = blob; | |
blob = rev; | |
rev = null; | |
} | |
if (typeof type === 'undefined') { | |
type = blob; | |
blob = rev; | |
rev = null; | |
} | |
function createAttachment(doc) { | |
doc._attachments = doc._attachments || {}; | |
doc._attachments[attachmentId] = { | |
content_type: type, | |
data: blob | |
}; | |
return api.put(doc); | |
} | |
return api.get(docId).then(function (doc) { | |
if (doc._rev !== rev) { | |
throw errors.error(errors.REV_CONFLICT); | |
} | |
return createAttachment(doc); | |
}, function (err) { | |
// create new doc | |
if (err.reason === errors.MISSING_DOC.message) { | |
return createAttachment({_id: docId}); | |
} else { | |
throw err; | |
} | |
}); | |
}); | |
AbstractPouchDB.prototype.removeAttachment = | |
utils.adapterFun('removeAttachment', function (docId, attachmentId, rev, | |
callback) { | |
var self = this; | |
self.get(docId, function (err, obj) { | |
if (err) { | |
callback(err); | |
return; | |
} | |
if (obj._rev !== rev) { | |
callback(errors.error(errors.REV_CONFLICT)); | |
return; | |
} | |
if (!obj._attachments) { | |
return callback(); | |
} | |
delete obj._attachments[attachmentId]; | |
if (Object.keys(obj._attachments).length === 0) { | |
delete obj._attachments; | |
} | |
self.put(obj, callback); | |
}); | |
}); | |
AbstractPouchDB.prototype.remove = | |
utils.adapterFun('remove', function (docOrId, optsOrRev, opts, callback) { | |
var doc; | |
if (typeof optsOrRev === 'string') { | |
// id, rev, opts, callback style | |
doc = { | |
_id: docOrId, | |
_rev: optsOrRev | |
}; | |
if (typeof opts === 'function') { | |
callback = opts; | |
opts = {}; | |
} | |
} else { | |
// doc, opts, callback style | |
doc = docOrId; | |
if (typeof optsOrRev === 'function') { | |
callback = optsOrRev; | |
opts = {}; | |
} else { | |
callback = opts; | |
opts = optsOrRev; | |
} | |
} | |
opts = utils.clone(opts || {}); | |
opts.was_delete = true; | |
var newDoc = {_id: doc._id, _rev: (doc._rev || opts.rev)}; | |
newDoc._deleted = true; | |
if (utils.isLocalId(newDoc._id) && typeof this._removeLocal === 'function') { | |
return this._removeLocal(doc, callback); | |
} | |
this.bulkDocs({docs: [newDoc]}, opts, yankError(callback)); | |
}); | |
AbstractPouchDB.prototype.revsDiff = | |
utils.adapterFun('revsDiff', function (req, opts, callback) { | |
if (typeof opts === 'function') { | |
callback = opts; | |
opts = {}; | |
} | |
opts = utils.clone(opts); | |
var ids = Object.keys(req); | |
if (!ids.length) { | |
return callback(null, {}); | |
} | |
var count = 0; | |
var missing = new utils.Map(); | |
function addToMissing(id, revId) { | |
if (!missing.has(id)) { | |
missing.set(id, {missing: []}); | |
} | |
missing.get(id).missing.push(revId); | |
} | |
function processDoc(id, rev_tree) { | |
// Is this fast enough? Maybe we should switch to a set simulated by a map | |
var missingForId = req[id].slice(0); | |
merge.traverseRevTree(rev_tree, function (isLeaf, pos, revHash, ctx, | |
opts) { | |
var rev = pos + '-' + revHash; | |
var idx = missingForId.indexOf(rev); | |
if (idx === -1) { | |
return; | |
} | |
missingForId.splice(idx, 1); | |
if (opts.status !== 'available') { | |
addToMissing(id, rev); | |
} | |
}); | |
// Traversing the tree is synchronous, so now `missingForId` contains | |
// revisions that were not found in the tree | |
missingForId.forEach(function (rev) { | |
addToMissing(id, rev); | |
}); | |
} | |
ids.map(function (id) { | |
this._getRevisionTree(id, function (err, rev_tree) { | |
if (err && err.status === 404 && err.message === 'missing') { | |
missing.set(id, {missing: req[id]}); | |
} else if (err) { | |
return callback(err); | |
} else { | |
processDoc(id, rev_tree); | |
} | |
if (++count === ids.length) { | |
// convert LazyMap to object | |
var missingObj = {}; | |
missing.forEach(function (value, key) { | |
missingObj[key] = value; | |
}); | |
return callback(null, missingObj); | |
} | |
}); | |
}, this); | |
}); | |
// compact one document and fire callback | |
// by compacting we mean removing all revisions which | |
// are further from the leaf in revision tree than max_height | |
AbstractPouchDB.prototype.compactDocument = | |
utils.adapterFun('compactDocument', function (docId, maxHeight, callback) { | |
var self = this; | |
this._getRevisionTree(docId, function (err, revTree) { | |
if (err) { | |
return callback(err); | |
} | |
var height = computeHeight(revTree); | |
var candidates = []; | |
var revs = []; | |
Object.keys(height).forEach(function (rev) { | |
if (height[rev] > maxHeight) { | |
candidates.push(rev); | |
} | |
}); | |
merge.traverseRevTree(revTree, function (isLeaf, pos, revHash, ctx, opts) { | |
var rev = pos + '-' + revHash; | |
if (opts.status === 'available' && candidates.indexOf(rev) !== -1) { | |
revs.push(rev); | |
} | |
}); | |
self._doCompaction(docId, revs, callback); | |
}); | |
}); | |
// compact the whole database using single document | |
// compaction | |
AbstractPouchDB.prototype.compact = | |
utils.adapterFun('compact', function (opts, callback) { | |
if (typeof opts === 'function') { | |
callback = opts; | |
opts = {}; | |
} | |
var self = this; | |
opts = utils.clone(opts || {}); | |
self.get('_local/compaction').catch(function () { | |
return false; | |
}).then(function (doc) { | |
if (typeof self._compact === 'function') { | |
if (doc && doc.last_seq) { | |
opts.last_seq = doc.last_seq; | |
} | |
return self._compact(opts, callback); | |
} | |
}); | |
}); | |
AbstractPouchDB.prototype._compact = function (opts, callback) { | |
var self = this; | |
var changesOpts = { | |
returnDocs: false, | |
last_seq: opts.last_seq || 0 | |
}; | |
var promises = []; | |
function onChange(row) { | |
promises.push(self.compactDocument(row.id, 0)); | |
} | |
function onComplete(resp) { | |
var lastSeq = resp.last_seq; | |
Promise.all(promises).then(function () { | |
return upsert(self, '_local/compaction', function deltaFunc(doc) { | |
if (!doc.last_seq || doc.last_seq < lastSeq) { | |
doc.last_seq = lastSeq; | |
return doc; | |
} | |
return false; // somebody else got here first, don't update | |
}); | |
}).then(function () { | |
callback(null, {ok: true}); | |
}).catch(callback); | |
} | |
self.changes(changesOpts) | |
.on('change', onChange) | |
.on('complete', onComplete) | |
.on('error', callback); | |
}; | |
/* Begin api wrappers. Specific functionality to storage belongs in the | |
_[method] */ | |
AbstractPouchDB.prototype.get = | |
utils.adapterFun('get', function (id, opts, callback) { | |
if (typeof opts === 'function') { | |
callback = opts; | |
opts = {}; | |
} | |
if (typeof id !== 'string') { | |
return callback(errors.error(errors.INVALID_ID)); | |
} | |
if (utils.isLocalId(id) && typeof this._getLocal === 'function') { | |
return this._getLocal(id, callback); | |
} | |
var leaves = [], self = this; | |
function finishOpenRevs() { | |
var result = []; | |
var count = leaves.length; | |
if (!count) { | |
return callback(null, result); | |
} | |
// order with open_revs is unspecified | |
leaves.forEach(function (leaf) { | |
self.get(id, { | |
rev: leaf, | |
revs: opts.revs, | |
attachments: opts.attachments | |
}, function (err, doc) { | |
if (!err) { | |
result.push({ok: doc}); | |
} else { | |
result.push({missing: leaf}); | |
} | |
count--; | |
if (!count) { | |
callback(null, result); | |
} | |
}); | |
}); | |
} | |
if (opts.open_revs) { | |
if (opts.open_revs === "all") { | |
this._getRevisionTree(id, function (err, rev_tree) { | |
if (err) { | |
return callback(err); | |
} | |
leaves = merge.collectLeaves(rev_tree).map(function (leaf) { | |
return leaf.rev; | |
}); | |
finishOpenRevs(); | |
}); | |
} else { | |
if (Array.isArray(opts.open_revs)) { | |
leaves = opts.open_revs; | |
for (var i = 0; i < leaves.length; i++) { | |
var l = leaves[i]; | |
// looks like it's the only thing couchdb checks | |
if (!(typeof(l) === "string" && /^\d+-/.test(l))) { | |
return callback(errors.error(errors.INVALID_REV)); | |
} | |
} | |
finishOpenRevs(); | |
} else { | |
return callback(errors.error(errors.UNKNOWN_ERROR, | |
'function_clause')); | |
} | |
} | |
return; // open_revs does not like other options | |
} | |
return this._get(id, opts, function (err, result) { | |
opts = utils.clone(opts); | |
if (err) { | |
return callback(err); | |
} | |
var doc = result.doc; | |
var metadata = result.metadata; | |
var ctx = result.ctx; | |
if (opts.conflicts) { | |
var conflicts = merge.collectConflicts(metadata); | |
if (conflicts.length) { | |
doc._conflicts = conflicts; | |
} | |
} | |
if (utils.isDeleted(metadata, doc._rev)) { | |
doc._deleted = true; | |
} | |
if (opts.revs || opts.revs_info) { | |
var paths = merge.rootToLeaf(metadata.rev_tree); | |
var path = arrayFirst(paths, function (arr) { | |
return arr.ids.map(function (x) { return x.id; }) | |
.indexOf(doc._rev.split('-')[1]) !== -1; | |
}); | |
var indexOfRev = path.ids.map(function (x) {return x.id; }) | |
.indexOf(doc._rev.split('-')[1]) + 1; | |
var howMany = path.ids.length - indexOfRev; | |
path.ids.splice(indexOfRev, howMany); | |
path.ids.reverse(); | |
if (opts.revs) { | |
doc._revisions = { | |
start: (path.pos + path.ids.length) - 1, | |
ids: path.ids.map(function (rev) { | |
return rev.id; | |
}) | |
}; | |
} | |
if (opts.revs_info) { | |
var pos = path.pos + path.ids.length; | |
doc._revs_info = path.ids.map(function (rev) { | |
pos--; | |
return { | |
rev: pos + '-' + rev.id, | |
status: rev.opts.status | |
}; | |
}); | |
} | |
} | |
if (opts.local_seq) { | |
utils.info('The "local_seq" option is deprecated and will be removed'); | |
doc._local_seq = result.metadata.seq; | |
} | |
if (opts.attachments && doc._attachments) { | |
var attachments = doc._attachments; | |
var count = Object.keys(attachments).length; | |
if (count === 0) { | |
return callback(null, doc); | |
} | |
Object.keys(attachments).forEach(function (key) { | |
this._getAttachment(attachments[key], | |
{encode: true, ctx: ctx}, function (err, data) { | |
var att = doc._attachments[key]; | |
att.data = data; | |
delete att.stub; | |
delete att.length; | |
if (!--count) { | |
callback(null, doc); | |
} | |
}); | |
}, self); | |
} else { | |
if (doc._attachments) { | |
for (var key in doc._attachments) { | |
if (doc._attachments.hasOwnProperty(key)) { | |
doc._attachments[key].stub = true; | |
} | |
} | |
} | |
callback(null, doc); | |
} | |
}); | |
}); | |
AbstractPouchDB.prototype.getAttachment = | |
utils.adapterFun('getAttachment', function (docId, attachmentId, opts, | |
callback) { | |
var self = this; | |
if (opts instanceof Function) { | |
callback = opts; | |
opts = {}; | |
} | |
opts = utils.clone(opts); | |
this._get(docId, opts, function (err, res) { | |
if (err) { | |
return callback(err); | |
} | |
if (res.doc._attachments && res.doc._attachments[attachmentId]) { | |
opts.ctx = res.ctx; | |
self._getAttachment(res.doc._attachments[attachmentId], opts, callback); | |
} else { | |
return callback(errors.error(errors.MISSING_DOC)); | |
} | |
}); | |
}); | |
AbstractPouchDB.prototype.allDocs = | |
utils.adapterFun('allDocs', function (opts, callback) { | |
if (typeof opts === 'function') { | |
callback = opts; | |
opts = {}; | |
} | |
opts = utils.clone(opts); | |
opts.skip = typeof opts.skip !== 'undefined' ? opts.skip : 0; | |
if ('keys' in opts) { | |
if (!Array.isArray(opts.keys)) { | |
return callback(new TypeError('options.keys must be an array')); | |
} | |
var incompatibleOpt = | |
['startkey', 'endkey', 'key'].filter(function (incompatibleOpt) { | |
return incompatibleOpt in opts; | |
})[0]; | |
if (incompatibleOpt) { | |
callback(errors.error(errors.QUERY_PARSE_ERROR, | |
'Query parameter `' + incompatibleOpt + | |
'` is not compatible with multi-get' | |
)); | |
return; | |
} | |
if (this.type() !== 'http') { | |
return allDocsKeysQuery(this, opts, callback); | |
} | |
} | |
return this._allDocs(opts, callback); | |
}); | |
AbstractPouchDB.prototype.changes = function (opts, callback) { | |
if (typeof opts === 'function') { | |
callback = opts; | |
opts = {}; | |
} | |
return new Changes(this, opts, callback); | |
}; | |
AbstractPouchDB.prototype.close = | |
utils.adapterFun('close', function (callback) { | |
this._closed = true; | |
return this._close(callback); | |
}); | |
AbstractPouchDB.prototype.info = utils.adapterFun('info', function (callback) { | |
var self = this; | |
this._info(function (err, info) { | |
if (err) { | |
return callback(err); | |
} | |
// assume we know better than the adapter, unless it informs us | |
info.db_name = info.db_name || self._db_name; | |
info.auto_compaction = !!(self.auto_compaction && self.type() !== 'http'); | |
callback(null, info); | |
}); | |
}); | |
AbstractPouchDB.prototype.id = utils.adapterFun('id', function (callback) { | |
return this._id(callback); | |
}); | |
AbstractPouchDB.prototype.type = function () { | |
return (typeof this._type === 'function') ? this._type() : this.adapter; | |
}; | |
AbstractPouchDB.prototype.bulkDocs = | |
utils.adapterFun('bulkDocs', function (req, opts, callback) { | |
if (typeof opts === 'function') { | |
callback = opts; | |
opts = {}; | |
} | |
opts = utils.clone(opts); | |
if (Array.isArray(req)) { | |
req = { | |
docs: req | |
}; | |
} | |
if (!req || !req.docs || !Array.isArray(req.docs)) { | |
return callback(errors.error(errors.MISSING_BULK_DOCS)); | |
} | |
for (var i = 0; i < req.docs.length; ++i) { | |
if (typeof req.docs[i] !== 'object' || Array.isArray(req.docs[i])) { | |
return callback(errors.error(errors.NOT_AN_OBJECT)); | |
} | |
} | |
req = utils.clone(req); | |
if (!('new_edits' in opts)) { | |
if ('new_edits' in req) { | |
opts.new_edits = req.new_edits; | |
} else { | |
opts.new_edits = true; | |
} | |
} | |
if (!opts.new_edits && this.type() !== 'http') { | |
// ensure revisions of the same doc are sorted, so that | |
// the local adapter processes them correctly (#2935) | |
req.docs.sort(function (a, b) { | |
var idCompare = utils.compare(a._id, b._id); | |
if (idCompare !== 0) { | |
return idCompare; | |
} | |
var aStart = a._revisions ? a._revisions.start : 0; | |
var bStart = b._revisions ? b._revisions.start : 0; | |
return utils.compare(aStart, bStart); | |
}); | |
} | |
req.docs.forEach(function (doc) { | |
if (doc._deleted) { | |
delete doc._attachments; // ignore atts for deleted docs | |
} | |
}); | |
return this._bulkDocs(req, opts, function (err, res) { | |
if (err) { | |
return callback(err); | |
} | |
if (!opts.new_edits) { | |
// this is what couch does when new_edits is false | |
res = res.filter(function (x) { | |
return x.error; | |
}); | |
} | |
callback(null, res); | |
}); | |
}); | |
AbstractPouchDB.prototype.registerDependentDatabase = | |
utils.adapterFun('registerDependentDatabase', function (dependentDb, | |
callback) { | |
var depDB = new this.constructor(dependentDb, this.__opts); | |
function diffFun(doc) { | |
doc.dependentDbs = doc.dependentDbs || {}; | |
if (doc.dependentDbs[dependentDb]) { | |
return false; // no update required | |
} | |
doc.dependentDbs[dependentDb] = true; | |
return doc; | |
} | |
upsert(this, '_local/_pouch_dependentDbs', diffFun, function (err) { | |
if (err) { | |
return callback(err); | |
} | |
return callback(null, {db: depDB}); | |
}); | |
}); | |
AbstractPouchDB.prototype.destroy = | |
utils.adapterFun('destroy', function (callback) { | |
var self = this; | |
var usePrefix = 'use_prefix' in self ? self.use_prefix : true; | |
function destroyDb() { | |
// call destroy method of the particular adaptor | |
self._destroy(function (err, resp) { | |
if (err) { | |
return callback(err); | |
} | |
self.emit('destroyed'); | |
callback(null, resp || { 'ok': true }); | |
}); | |
} | |
self.get('_local/_pouch_dependentDbs', function (err, localDoc) { | |
if (err) { | |
if (err.status !== 404) { | |
return callback(err); | |
} else { // no dependencies | |
return destroyDb(); | |
} | |
} | |
var dependentDbs = localDoc.dependentDbs; | |
var PouchDB = self.constructor; | |
var deletedMap = Object.keys(dependentDbs).map(function (name) { | |
var trueName = usePrefix ? | |
name.replace(new RegExp('^' + PouchDB.prefix), '') : name; | |
return new PouchDB(trueName, self.__opts).destroy(); | |
}); | |
Promise.all(deletedMap).then(destroyDb, function (error) { | |
callback(error); | |
}); | |
}); | |
}); | |
},{"./changes":114,"./deps/errors":120,"./deps/upsert":128,"./merge":133,"./utils":138,"events":45}],103:[function(require,module,exports){ | |
(function (process){ | |
"use strict"; | |
var CHANGES_BATCH_SIZE = 25; | |
// according to http://stackoverflow.com/a/417184/680742, | |
// the de factor URL length limit is 2000 characters. | |
// but since most of our measurements don't take the full | |
// URL into account, we fudge it a bit. | |
// TODO: we could measure the full URL to enforce exactly 2000 chars | |
var MAX_URL_LENGTH = 1800; | |
var utils = require('../../utils'); | |
var errors = require('../../deps/errors'); | |
var log = require('debug')('pouchdb:http'); | |
var isBrowser = typeof process === 'undefined' || process.browser; | |
var buffer = require('../../deps/buffer'); | |
function encodeDocId(id) { | |
if (/^_design/.test(id)) { | |
return '_design/' + encodeURIComponent(id.slice(8)); | |
} | |
if (/^_local/.test(id)) { | |
return '_local/' + encodeURIComponent(id.slice(7)); | |
} | |
return encodeURIComponent(id); | |
} | |
function preprocessAttachments(doc) { | |
if (!doc._attachments || !Object.keys(doc._attachments)) { | |
return utils.Promise.resolve(); | |
} | |
return utils.Promise.all(Object.keys(doc._attachments).map(function (key) { | |
var attachment = doc._attachments[key]; | |
if (attachment.data && typeof attachment.data !== 'string') { | |
if (isBrowser) { | |
return new utils.Promise(function (resolve) { | |
utils.readAsBinaryString(attachment.data, function (binary) { | |
attachment.data = utils.btoa(binary); | |
resolve(); | |
}); | |
}); | |
} else { | |
attachment.data = attachment.data.toString('base64'); | |
} | |
} | |
})); | |
} | |
// Get all the information you possibly can about the URI given by name and | |
// return it as a suitable object. | |
function getHost(name, opts) { | |
// If the given name contains "http:" | |
if (/http(s?):/.test(name)) { | |
// Prase the URI into all its little bits | |
var uri = utils.parseUri(name); | |
// Store the fact that it is a remote URI | |
uri.remote = true; | |
// Store the user and password as a separate auth object | |
if (uri.user || uri.password) { | |
uri.auth = {username: uri.user, password: uri.password}; | |
} | |
// Split the path part of the URI into parts using '/' as the delimiter | |
// after removing any leading '/' and any trailing '/' | |
var parts = uri.path.replace(/(^\/|\/$)/g, '').split('/'); | |
// Store the first part as the database name and remove it from the parts | |
// array | |
uri.db = parts.pop(); | |
// Restore the path by joining all the remaining parts (all the parts | |
// except for the database name) with '/'s | |
uri.path = parts.join('/'); | |
opts = opts || {}; | |
opts = utils.clone(opts); | |
uri.headers = opts.headers || (opts.ajax && opts.ajax.headers) || {}; | |
if (opts.auth || uri.auth) { | |
var nAuth = opts.auth || uri.auth; | |
var token = utils.btoa(nAuth.username + ':' + nAuth.password); | |
uri.headers.Authorization = 'Basic ' + token; | |
} | |
if (opts.headers) { | |
uri.headers = opts.headers; | |
} | |
return uri; | |
} | |
// If the given name does not contain 'http:' then return a very basic object | |
// with no host, the current path, the given name as the database name and no | |
// username/password | |
return {host: '', path: '/', db: name, auth: false}; | |
} | |
// Generate a URL with the host data given by opts and the given path | |
function genDBUrl(opts, path) { | |
return genUrl(opts, opts.db + '/' + path); | |
} | |
// Generate a URL with the host data given by opts and the given path | |
function genUrl(opts, path) { | |
if (opts.remote) { | |
// If the host already has a path, then we need to have a path delimiter | |
// Otherwise, the path delimiter is the empty string | |
var pathDel = !opts.path ? '' : '/'; | |
// If the host already has a path, then we need to have a path delimiter | |
// Otherwise, the path delimiter is the empty string | |
return opts.protocol + '://' + opts.host + ':' + opts.port + '/' + | |
opts.path + pathDel + path; | |
} | |
return '/' + path; | |
} | |
// Implements the PouchDB API for dealing with CouchDB instances over HTTP | |
function HttpPouch(opts, callback) { | |
// The functions that will be publicly available for HttpPouch | |
var api = this; | |
api.getHost = opts.getHost ? opts.getHost : getHost; | |
// Parse the URI given by opts.name into an easy-to-use object | |
var host = api.getHost(opts.name, opts); | |
// Generate the database URL based on the host | |
var dbUrl = genDBUrl(host, ''); | |
api.getUrl = function () {return dbUrl; }; | |
api.getHeaders = function () {return utils.clone(host.headers); }; | |
var ajaxOpts = opts.ajax || {}; | |
opts = utils.clone(opts); | |
function ajax(options, callback) { | |
var reqOpts = utils.extend(true, utils.clone(ajaxOpts), options); | |
log(reqOpts.method + ' ' + reqOpts.url); | |
return utils.ajax(reqOpts, callback); | |
} | |
// Create a new CouchDB database based on the given opts | |
var createDB = function () { | |
ajax({headers: host.headers, method: 'PUT', url: dbUrl}, function (err) { | |
// If we get an "Unauthorized" error | |
if (err && err.status === 401) { | |
// Test if the database already exists | |
ajax({headers: host.headers, method: 'HEAD', url: dbUrl}, | |
function (err) { | |
// If there is still an error | |
if (err) { | |
// Give the error to the callback to deal with | |
callback(err); | |
} else { | |
// Continue as if there had been no errors | |
callback(null, api); | |
} | |
}); | |
// If there were no errros or if the only error is "Precondition Failed" | |
// (note: "Precondition Failed" occurs when we try to create a database | |
// that already exists) | |
} else if (!err || err.status === 412) { | |
// Continue as if there had been no errors | |
callback(null, api); | |
} else { | |
callback(err); | |
} | |
}); | |
}; | |
if (!opts.skipSetup) { | |
ajax({headers: host.headers, method: 'GET', url: dbUrl}, function (err) { | |
//check if the db exists | |
if (err) { | |
if (err.status === 404) { | |
utils.explain404( | |
'PouchDB is just detecting if the remote DB exists.'); | |
//if it doesn't, create it | |
createDB(); | |
} else { | |
callback(err); | |
} | |
} else { | |
//go do stuff with the db | |
callback(null, api); | |
} | |
}); | |
} | |
api.type = function () { | |
return 'http'; | |
}; | |
api.id = utils.adapterFun('id', function (callback) { | |
ajax({ | |
headers: host.headers, | |
method: 'GET', | |
url: genUrl(host, '') | |
}, function (err, result) { | |
var uuid = (result && result.uuid) ? | |
result.uuid + host.db : genDBUrl(host, ''); | |
callback(null, uuid); | |
}); | |
}); | |
api.request = utils.adapterFun('request', function (options, callback) { | |
options.headers = host.headers; | |
options.url = genDBUrl(host, options.url); | |
ajax(options, callback); | |
}); | |
// Sends a POST request to the host calling the couchdb _compact function | |
// version: The version of CouchDB it is running | |
api.compact = utils.adapterFun('compact', function (opts, callback) { | |
if (typeof opts === 'function') { | |
callback = opts; | |
opts = {}; | |
} | |
opts = utils.clone(opts); | |
ajax({ | |
headers: host.headers, | |
url: genDBUrl(host, '_compact'), | |
method: 'POST' | |
}, function () { | |
function ping() { | |
api.info(function (err, res) { | |
if (!res.compact_running) { | |
callback(null, {ok: true}); | |
} else { | |
setTimeout(ping, opts.interval || 200); | |
} | |
}); | |
} | |
// Ping the http if it's finished compaction | |
if (typeof callback === "function") { | |
ping(); | |
} | |
}); | |
}); | |
// Calls GET on the host, which gets back a JSON string containing | |
// couchdb: A welcome string | |
// version: The version of CouchDB it is running | |
api._info = function (callback) { | |
ajax({ | |
headers: host.headers, | |
method: 'GET', | |
url: genDBUrl(host, '') | |
}, function (err, res) { | |
if (err) { | |
callback(err); | |
} else { | |
res.host = genDBUrl(host, ''); | |
callback(null, res); | |
} | |
}); | |
}; | |
// Get the document with the given id from the database given by host. | |
// The id could be solely the _id in the database, or it may be a | |
// _design/ID or _local/ID path | |
api.get = utils.adapterFun('get', function (id, opts, callback) { | |
// If no options were given, set the callback to the second parameter | |
if (typeof opts === 'function') { | |
callback = opts; | |
opts = {}; | |
} | |
opts = utils.clone(opts); | |
if (opts.auto_encode === undefined) { | |
opts.auto_encode = true; | |
} | |
// List of parameters to add to the GET request | |
var params = []; | |
// If it exists, add the opts.revs value to the list of parameters. | |
// If revs=true then the resulting JSON will include a field | |
// _revisions containing an array of the revision IDs. | |
if (opts.revs) { | |
params.push('revs=true'); | |
} | |
// If it exists, add the opts.revs_info value to the list of parameters. | |
// If revs_info=true then the resulting JSON will include the field | |
// _revs_info containing an array of objects in which each object | |
// representing an available revision. | |
if (opts.revs_info) { | |
params.push('revs_info=true'); | |
} | |
if (opts.local_seq) { | |
params.push('local_seq=true'); | |
} | |
// If it exists, add the opts.open_revs value to the list of parameters. | |
// If open_revs=all then the resulting JSON will include all the leaf | |
// revisions. If open_revs=["rev1", "rev2",...] then the resulting JSON | |
// will contain an array of objects containing data of all revisions | |
if (opts.open_revs) { | |
if (opts.open_revs !== "all") { | |
opts.open_revs = JSON.stringify(opts.open_revs); | |
} | |
params.push('open_revs=' + opts.open_revs); | |
} | |
// If it exists, add the opts.attachments value to the list of parameters. | |
// If attachments=true the resulting JSON will include the base64-encoded | |
// contents in the "data" property of each attachment. | |
if (opts.attachments) { | |
params.push('attachments=true'); | |
} | |
// If it exists, add the opts.rev value to the list of parameters. | |
// If rev is given a revision number then get the specified revision. | |
if (opts.rev) { | |
params.push('rev=' + opts.rev); | |
} | |
// If it exists, add the opts.conflicts value to the list of parameters. | |
// If conflicts=true then the resulting JSON will include the field | |
// _conflicts containing all the conflicting revisions. | |
if (opts.conflicts) { | |
params.push('conflicts=' + opts.conflicts); | |
} | |
// Format the list of parameters into a valid URI query string | |
params = params.join('&'); | |
params = params === '' ? '' : '?' + params; | |
if (opts.auto_encode) { | |
id = encodeDocId(id); | |
} | |
// Set the options for the ajax call | |
var options = { | |
headers: host.headers, | |
method: 'GET', | |
url: genDBUrl(host, id + params) | |
}; | |
var getRequestAjaxOpts = opts.ajax || {}; | |
utils.extend(true, options, getRequestAjaxOpts); | |
// If the given id contains at least one '/' and the part before the '/' | |
// is NOT "_design" and is NOT "_local" | |
// OR | |
// If the given id contains at least two '/' and the part before the first | |
// '/' is "_design". | |
// TODO This second condition seems strange since if parts[0] === '_design' | |
// then we already know that parts[0] !== '_local'. | |
var parts = id.split('/'); | |
if ((parts.length > 1 && parts[0] !== '_design' && parts[0] !== '_local') || | |
(parts.length > 2 && parts[0] === '_design' && parts[0] !== '_local')) { | |
// Binary is expected back from the server | |
options.binary = true; | |
} | |
// Get the document | |
ajax(options, function (err, doc, xhr) { | |
// If the document does not exist, send an error to the callback | |
if (err) { | |
return callback(err); | |
} | |
// Send the document to the callback | |
callback(null, doc, xhr); | |
}); | |
}); | |
// Delete the document given by doc from the database given by host. | |
api.remove = utils.adapterFun('remove', | |
function (docOrId, optsOrRev, opts, callback) { | |
var doc; | |
if (typeof optsOrRev === 'string') { | |
// id, rev, opts, callback style | |
doc = { | |
_id: docOrId, | |
_rev: optsOrRev | |
}; | |
if (typeof opts === 'function') { | |
callback = opts; | |
opts = {}; | |
} | |
} else { | |
// doc, opts, callback style | |
doc = docOrId; | |
if (typeof optsOrRev === 'function') { | |
callback = optsOrRev; | |
opts = {}; | |
} else { | |
callback = opts; | |
opts = optsOrRev; | |
} | |
} | |
var rev = (doc._rev || opts.rev); | |
// Delete the document | |
ajax({ | |
headers: host.headers, | |
method: 'DELETE', | |
url: genDBUrl(host, encodeDocId(doc._id)) + '?rev=' + rev | |
}, callback); | |
}); | |
function encodeAttachmentId(attachmentId) { | |
return attachmentId.split("/").map(encodeURIComponent).join("/"); | |
} | |
// Get the attachment | |
api.getAttachment = | |
utils.adapterFun('getAttachment', function (docId, attachmentId, opts, | |
callback) { | |
if (typeof opts === 'function') { | |
callback = opts; | |
opts = {}; | |
} | |
opts = utils.clone(opts); | |
if (opts.auto_encode === undefined) { | |
opts.auto_encode = true; | |
} | |
if (opts.auto_encode) { | |
docId = encodeDocId(docId); | |
} | |
opts.auto_encode = false; | |
api.get(docId + '/' + encodeAttachmentId(attachmentId), opts, callback); | |
}); | |
// Remove the attachment given by the id and rev | |
api.removeAttachment = | |
utils.adapterFun('removeAttachment', function (docId, attachmentId, rev, | |
callback) { | |
var url = genDBUrl(host, encodeDocId(docId) + '/' + | |
encodeAttachmentId(attachmentId)) + '?rev=' + rev; | |
ajax({ | |
headers: host.headers, | |
method: 'DELETE', | |
url: url | |
}, callback); | |
}); | |
// Add the attachment given by blob and its contentType property | |
// to the document with the given id, the revision given by rev, and | |
// add it to the database given by host. | |
api.putAttachment = | |
utils.adapterFun('putAttachment', function (docId, attachmentId, rev, blob, | |
type, callback) { | |
if (typeof type === 'function') { | |
callback = type; | |
type = blob; | |
blob = rev; | |
rev = null; | |
} | |
if (typeof type === 'undefined') { | |
type = blob; | |
blob = rev; | |
rev = null; | |
} | |
var id = encodeDocId(docId) + '/' + encodeAttachmentId(attachmentId); | |
var url = genDBUrl(host, id); | |
if (rev) { | |
url += '?rev=' + rev; | |
} | |
if (typeof blob === 'string') { | |
var binary; | |
try { | |
binary = utils.atob(blob); | |
} catch (err) { | |
// it's not base64-encoded, so throw error | |
return callback(errors.error(errors.BAD_ARG, | |
'Attachments need to be base64 encoded')); | |
} | |
if (isBrowser) { | |
blob = utils.createBlob([utils.fixBinary(binary)], {type: type}); | |
} else { | |
blob = binary ? new buffer(binary, 'binary') : ''; | |
} | |
} | |
var opts = { | |
headers: utils.clone(host.headers), | |
method: 'PUT', | |
url: url, | |
processData: false, | |
body: blob, | |
timeout: 60000 | |
}; | |
opts.headers['Content-Type'] = type; | |
// Add the attachment | |
ajax(opts, callback); | |
}); | |
// Add the document given by doc (in JSON string format) to the database | |
// given by host. This fails if the doc has no _id field. | |
api.put = utils.adapterFun('put', utils.getArguments(function (args) { | |
var temp, temptype, opts; | |
var doc = args.shift(); | |
var id = '_id' in doc; | |
var callback = args.pop(); | |
if (typeof doc !== 'object' || Array.isArray(doc)) { | |
return callback(errors.error(errors.NOT_AN_OBJECT)); | |
} | |
doc = utils.clone(doc); | |
preprocessAttachments(doc).then(function () { | |
while (true) { | |
temp = args.shift(); | |
temptype = typeof temp; | |
if (temptype === "string" && !id) { | |
doc._id = temp; | |
id = true; | |
} else if (temptype === "string" && id && !('_rev' in doc)) { | |
doc._rev = temp; | |
} else if (temptype === "object") { | |
opts = utils.clone(temp); | |
} | |
if (!args.length) { | |
break; | |
} | |
} | |
opts = opts || {}; | |
var error = utils.invalidIdError(doc._id); | |
if (error) { | |
throw error; | |
} | |
// List of parameter to add to the PUT request | |
var params = []; | |
// If it exists, add the opts.new_edits value to the list of parameters. | |
// If new_edits = false then the database will NOT assign this document a | |
// new revision number | |
if (opts && typeof opts.new_edits !== 'undefined') { | |
params.push('new_edits=' + opts.new_edits); | |
} | |
// Format the list of parameters into a valid URI query string | |
params = params.join('&'); | |
if (params !== '') { | |
params = '?' + params; | |
} | |
// Add the document | |
ajax({ | |
headers: host.headers, | |
method: 'PUT', | |
url: genDBUrl(host, encodeDocId(doc._id)) + params, | |
body: doc | |
}, function (err, res) { | |
if (err) { | |
return callback(err); | |
} | |
res.ok = true; | |
callback(null, res); | |
}); | |
}).catch(callback); | |
})); | |
// Add the document given by doc (in JSON string format) to the database | |
// given by host. This does not assume that doc is a new document | |
// (i.e. does not have a _id or a _rev field.) | |
api.post = utils.adapterFun('post', function (doc, opts, callback) { | |
// If no options were given, set the callback to be the second parameter | |
if (typeof opts === 'function') { | |
callback = opts; | |
opts = {}; | |
} | |
opts = utils.clone(opts); | |
if (typeof doc !== 'object') { | |
return callback(errors.error(errors.NOT_AN_OBJECT)); | |
} | |
if (! ("_id" in doc)) { | |
doc._id = utils.uuid(); | |
} | |
api.put(doc, opts, function (err, res) { | |
if (err) { | |
return callback(err); | |
} | |
res.ok = true; | |
callback(null, res); | |
}); | |
}); | |
// Update/create multiple documents given by req in the database | |
// given by host. | |
api._bulkDocs = function (req, opts, callback) { | |
// If opts.new_edits exists add it to the document data to be | |
// send to the database. | |
// If new_edits=false then it prevents the database from creating | |
// new revision numbers for the documents. Instead it just uses | |
// the old ones. This is used in database replication. | |
if (typeof opts.new_edits !== 'undefined') { | |
req.new_edits = opts.new_edits; | |
} | |
utils.Promise.all(req.docs.map(preprocessAttachments)).then(function () { | |
// Update/create the documents | |
ajax({ | |
headers: host.headers, | |
method: 'POST', | |
url: genDBUrl(host, '_bulk_docs'), | |
body: req | |
}, function (err, results) { | |
if (err) { | |
return callback(err); | |
} | |
results.forEach(function (result) { | |
result.ok = true; // smooths out cloudant not adding this | |
}); | |
callback(null, results); | |
}); | |
}).catch(callback); | |
}; | |
// Get a listing of the documents in the database given | |
// by host and ordered by increasing id. | |
api.allDocs = utils.adapterFun('allDocs', function (opts, callback) { | |
if (typeof opts === 'function') { | |
callback = opts; | |
opts = {}; | |
} | |
opts = utils.clone(opts); | |
// List of parameters to add to the GET request | |
var params = []; | |
var body; | |
var method = 'GET'; | |
if (opts.conflicts) { | |
params.push('conflicts=true'); | |
} | |
// If opts.descending is truthy add it to params | |
if (opts.descending) { | |
params.push('descending=true'); | |
} | |
// If opts.include_docs exists, add the include_docs value to the | |
// list of parameters. | |
// If include_docs=true then include the associated document with each | |
// result. | |
if (opts.include_docs) { | |
params.push('include_docs=true'); | |
} | |
if (opts.attachments) { | |
// added in CouchDB 1.6.0 | |
params.push('attachments=true'); | |
} | |
if (opts.key) { | |
params.push('key=' + encodeURIComponent(JSON.stringify(opts.key))); | |
} | |
// If opts.startkey exists, add the startkey value to the list of | |
// parameters. | |
// If startkey is given then the returned list of documents will | |
// start with the document whose id is startkey. | |
if (opts.startkey) { | |
params.push('startkey=' + | |
encodeURIComponent(JSON.stringify(opts.startkey))); | |
} | |
// If opts.endkey exists, add the endkey value to the list of parameters. | |
// If endkey is given then the returned list of docuemnts will | |
// end with the document whose id is endkey. | |
if (opts.endkey) { | |
params.push('endkey=' + encodeURIComponent(JSON.stringify(opts.endkey))); | |
} | |
if (typeof opts.inclusive_end !== 'undefined') { | |
params.push('inclusive_end=' + !!opts.inclusive_end); | |
} | |
// If opts.limit exists, add the limit value to the parameter list. | |
if (typeof opts.limit !== 'undefined') { | |
params.push('limit=' + opts.limit); | |
} | |
if (typeof opts.skip !== 'undefined') { | |
params.push('skip=' + opts.skip); | |
} | |
// Format the list of parameters into a valid URI query string | |
params = params.join('&'); | |
if (params !== '') { | |
params = '?' + params; | |
} | |
if (typeof opts.keys !== 'undefined') { | |
var keysAsString = | |
'keys=' + encodeURIComponent(JSON.stringify(opts.keys)); | |
if (keysAsString.length + params.length + 1 <= MAX_URL_LENGTH) { | |
// If the keys are short enough, do a GET. we do this to work around | |
// Safari not understanding 304s on POSTs (see issue #1239) | |
params += (params.indexOf('?') !== -1 ? '&' : '?') + keysAsString; | |
} else { | |
// If keys are too long, issue a POST request to circumvent GET | |
// query string limits | |
// see http://wiki.apache.org/couchdb/HTTP_view_API#Querying_Options | |
method = 'POST'; | |
body = JSON.stringify({keys: opts.keys}); | |
} | |
} | |
// Get the document listing | |
ajax({ | |
headers: host.headers, | |
method: method, | |
url: genDBUrl(host, '_all_docs' + params), | |
body: body | |
}, callback); | |
}); | |
// Get a list of changes made to documents in the database given by host. | |
// TODO According to the README, there should be two other methods here, | |
// api.changes.addListener and api.changes.removeListener. | |
api._changes = function (opts) { | |
// We internally page the results of a changes request, this means | |
// if there is a large set of changes to be returned we can start | |
// processing them quicker instead of waiting on the entire | |
// set of changes to return and attempting to process them at once | |
var batchSize = 'batch_size' in opts ? opts.batch_size : CHANGES_BATCH_SIZE; | |
opts = utils.clone(opts); | |
opts.timeout = opts.timeout || 30 * 1000; | |
// We give a 5 second buffer for CouchDB changes to respond with | |
// an ok timeout | |
var params = { timeout: opts.timeout - (5 * 1000) }; | |
var limit = (typeof opts.limit !== 'undefined') ? opts.limit : false; | |
if (limit === 0) { | |
limit = 1; | |
} | |
var returnDocs; | |
if ('returnDocs' in opts) { | |
returnDocs = opts.returnDocs; | |
} else { | |
returnDocs = true; | |
} | |
// | |
var leftToFetch = limit; | |
if (opts.style) { | |
params.style = opts.style; | |
} | |
if (opts.include_docs || opts.filter && typeof opts.filter === 'function') { | |
params.include_docs = true; | |
} | |
if (opts.attachments) { | |
params.attachments = true; | |
} | |
if (opts.continuous) { | |
params.feed = 'longpoll'; | |
} | |
if (opts.conflicts) { | |
params.conflicts = true; | |
} | |
if (opts.descending) { | |
params.descending = true; | |
} | |
if (opts.filter && typeof opts.filter === 'string') { | |
params.filter = opts.filter; | |
if (opts.filter === '_view' && | |
opts.view && | |
typeof opts.view === 'string') { | |
params.view = opts.view; | |
} | |
} | |
// If opts.query_params exists, pass it through to the changes request. | |
// These parameters may be used by the filter on the source database. | |
if (opts.query_params && typeof opts.query_params === 'object') { | |
for (var param_name in opts.query_params) { | |
if (opts.query_params.hasOwnProperty(param_name)) { | |
params[param_name] = opts.query_params[param_name]; | |
} | |
} | |
} | |
var method = 'GET'; | |
var body; | |
if (opts.doc_ids) { | |
// set this automagically for the user; it's annoying that couchdb | |
// requires both a "filter" and a "doc_ids" param. | |
params.filter = '_doc_ids'; | |
var docIdsJson = JSON.stringify(opts.doc_ids); | |
if (docIdsJson.length < MAX_URL_LENGTH) { | |
params.doc_ids = docIdsJson; | |
} else { | |
// anything greater than ~2000 is unsafe for gets, so | |
// use POST instead | |
method = 'POST'; | |
body = {doc_ids: opts.doc_ids }; | |
} | |
} | |
if (opts.continuous && api._useSSE) { | |
return api.sse(opts, params, returnDocs); | |
} | |
var xhr; | |
var lastFetchedSeq; | |
// Get all the changes starting wtih the one immediately after the | |
// sequence number given by since. | |
var fetch = function (since, callback) { | |
if (opts.aborted) { | |
return; | |
} | |
params.since = since; | |
if (typeof params.since === "object") { | |
params.since = JSON.stringify(params.since); | |
} | |
if (opts.descending) { | |
if (limit) { | |
params.limit = leftToFetch; | |
} | |
} else { | |
params.limit = (!limit || leftToFetch > batchSize) ? | |
batchSize : leftToFetch; | |
} | |
var paramStr = '?' + Object.keys(params).map(function (k) { | |
return k + '=' + params[k]; | |
}).join('&'); | |
// Set the options for the ajax call | |
var xhrOpts = { | |
headers: host.headers, | |
method: method, | |
url: genDBUrl(host, '_changes' + paramStr), | |
// _changes can take a long time to generate, especially when filtered | |
timeout: opts.timeout, | |
body: body | |
}; | |
lastFetchedSeq = since; | |
if (opts.aborted) { | |
return; | |
} | |
// Get the changes | |
xhr = ajax(xhrOpts, callback); | |
}; | |
// If opts.since exists, get all the changes from the sequence | |
// number given by opts.since. Otherwise, get all the changes | |
// from the sequence number 0. | |
var fetchTimeout = 10; | |
var fetchRetryCount = 0; | |
var results = {results: []}; | |
var fetched = function (err, res) { | |
if (opts.aborted) { | |
return; | |
} | |
var raw_results_length = 0; | |
// If the result of the ajax call (res) contains changes (res.results) | |
if (res && res.results) { | |
raw_results_length = res.results.length; | |
results.last_seq = res.last_seq; | |
// For each change | |
var req = {}; | |
req.query = opts.query_params; | |
res.results = res.results.filter(function (c) { | |
leftToFetch--; | |
var ret = utils.filterChange(opts)(c); | |
if (ret) { | |
if (returnDocs) { | |
results.results.push(c); | |
} | |
utils.call(opts.onChange, c); | |
} | |
return ret; | |
}); | |
} else if (err) { | |
// In case of an error, stop listening for changes and call | |
// opts.complete | |
opts.aborted = true; | |
utils.call(opts.complete, err); | |
return; | |
} | |
// The changes feed may have timed out with no results | |
// if so reuse last update sequence | |
if (res && res.last_seq) { | |
lastFetchedSeq = res.last_seq; | |
} | |
var finished = (limit && leftToFetch <= 0) || | |
(res && raw_results_length < batchSize) || | |
(opts.descending); | |
if ((opts.continuous && !(limit && leftToFetch <= 0)) || !finished) { | |
// Increase retry delay exponentially as long as errors persist | |
if (err) { | |
fetchRetryCount += 1; | |
} else { | |
fetchRetryCount = 0; | |
} | |
var timeoutMultiplier = 1 << fetchRetryCount; | |
var retryWait = fetchTimeout * timeoutMultiplier; | |
var maximumWait = opts.maximumWait || 30000; | |
if (retryWait > maximumWait) { | |
utils.call(opts.complete, err || errors.error(errors.UNKNOWN_ERROR)); | |
return; | |
} | |
// Queue a call to fetch again with the newest sequence number | |
setTimeout(function () { fetch(lastFetchedSeq, fetched); }, retryWait); | |
} else { | |
// We're done, call the callback | |
utils.call(opts.complete, null, results); | |
} | |
}; | |
fetch(opts.since || 0, fetched); | |
// Return a method to cancel this method from processing any more | |
return { | |
cancel: function () { | |
opts.aborted = true; | |
if (xhr) { | |
xhr.abort(); | |
} | |
} | |
}; | |
}; | |
api.sse = function (opts, params, returnDocs) { | |
params.feed = 'eventsource'; | |
params.since = opts.since || 0; | |
params.limit = opts.limit; | |
delete params.timeout; | |
var paramStr = '?' + Object.keys(params).map(function (k) { | |
return k + '=' + params[k]; | |
}).join('&'); | |
var url = genDBUrl(host, '_changes' + paramStr); | |
var source = new EventSource(url); | |
var results = { | |
results: [], | |
last_seq: false | |
}; | |
var dispatched = false; | |
var open = false; | |
source.addEventListener('message', msgHandler, false); | |
source.onopen = function () { | |
open = true; | |
}; | |
source.onerror = errHandler; | |
return { | |
cancel: function () { | |
if (dispatched) { | |
return dispatched.cancel(); | |
} | |
source.removeEventListener('message', msgHandler, false); | |
source.close(); | |
} | |
}; | |
function msgHandler(e) { | |
var data = JSON.parse(e.data); | |
if (returnDocs) { | |
results.results.push(data); | |
} | |
results.last_seq = data.seq; | |
utils.call(opts.onChange, data); | |
} | |
function errHandler(err) { | |
source.removeEventListener('message', msgHandler, false); | |
if (open === false) { | |
// errored before it opened | |
// likely doesn't support EventSource | |
api._useSSE = false; | |
dispatched = api._changes(opts); | |
return; | |
} | |
source.close(); | |
utils.call(opts.complete, err); | |
} | |
}; | |
api._useSSE = false; | |
// Currently disabled due to failing chrome tests in saucelabs | |
// api._useSSE = typeof global.EventSource === 'function'; | |
// Given a set of document/revision IDs (given by req), tets the subset of | |
// those that do NOT correspond to revisions stored in the database. | |
// See http://wiki.apache.org/couchdb/HttpPostRevsDiff | |
api.revsDiff = utils.adapterFun('revsDiff', function (req, opts, callback) { | |
// If no options were given, set the callback to be the second parameter | |
if (typeof opts === 'function') { | |
callback = opts; | |
opts = {}; | |
} | |
// Get the missing document/revision IDs | |
ajax({ | |
headers: host.headers, | |
method: 'POST', | |
url: genDBUrl(host, '_revs_diff'), | |
body: JSON.stringify(req) | |
}, callback); | |
}); | |
api._close = function (callback) { | |
callback(); | |
}; | |
api._destroy = function (callback) { | |
ajax({ | |
url: genDBUrl(host, ''), | |
method: 'DELETE', | |
headers: host.headers | |
}, function (err, resp) { | |
if (err) { | |
api.emit('error', err); | |
callback(err); | |
} else { | |
api.emit('destroyed'); | |
api.constructor.emit('destroyed', opts.name); | |
callback(null, resp); | |
} | |
}); | |
}; | |
} | |
// HttpPouch is a valid adapter. | |
HttpPouch.valid = function () { | |
return true; | |
}; | |
module.exports = HttpPouch; | |
}).call(this,require("/Users/nlawson/workspace-node/pouchdb-authentication/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js")) | |
},{"../../deps/buffer":119,"../../deps/errors":120,"../../utils":138,"/Users/nlawson/workspace-node/pouchdb-authentication/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":46,"debug":141}],104:[function(require,module,exports){ | |
'use strict'; | |
var merge = require('../../merge'); | |
var errors = require('../../deps/errors'); | |
var idbUtils = require('./idb-utils'); | |
var idbConstants = require('./idb-constants'); | |
var ATTACH_STORE = idbConstants.ATTACH_STORE; | |
var BY_SEQ_STORE = idbConstants.BY_SEQ_STORE; | |
var DOC_STORE = idbConstants.DOC_STORE; | |
var decodeDoc = idbUtils.decodeDoc; | |
var decodeMetadata = idbUtils.decodeMetadata; | |
var fetchAttachmentsIfNecessary = idbUtils.fetchAttachmentsIfNecessary; | |
var postProcessAttachments = idbUtils.postProcessAttachments; | |
var openTransactionSafely = idbUtils.openTransactionSafely; | |
function createKeyRange(start, end, inclusiveEnd, key, descending) { | |
try { | |
if (start && end) { | |
if (descending) { | |
return IDBKeyRange.bound(end, start, !inclusiveEnd, false); | |
} else { | |
return IDBKeyRange.bound(start, end, false, !inclusiveEnd); | |
} | |
} else if (start) { | |
if (descending) { | |
return IDBKeyRange.upperBound(start); | |
} else { | |
return IDBKeyRange.lowerBound(start); | |
} | |
} else if (end) { | |
if (descending) { | |
return IDBKeyRange.lowerBound(end, !inclusiveEnd); | |
} else { | |
return IDBKeyRange.upperBound(end, !inclusiveEnd); | |
} | |
} else if (key) { | |
return IDBKeyRange.only(key); | |
} | |
} catch (e) { | |
return {error: e}; | |
} | |
return null; | |
} | |
function handleKeyRangeError(api, opts, err, callback) { | |
if (err.name === "DataError" && err.code === 0) { | |
// data error, start is less than end | |
return callback(null, { | |
total_rows: api._meta.docCount, | |
offset: opts.skip, | |
rows: [] | |
}); | |
} | |
callback(errors.error(errors.IDB_ERROR, err.name, err.message)); | |
} | |
function idbAllDocs(opts, api, idb, callback) { | |
function allDocsQuery(opts, callback) { | |
var start = 'startkey' in opts ? opts.startkey : false; | |
var end = 'endkey' in opts ? opts.endkey : false; | |
var key = 'key' in opts ? opts.key : false; | |
var skip = opts.skip || 0; | |
var limit = typeof opts.limit === 'number' ? opts.limit : -1; | |
var inclusiveEnd = opts.inclusive_end !== false; | |
var descending = 'descending' in opts && opts.descending ? 'prev' : null; | |
var keyRange = createKeyRange(start, end, inclusiveEnd, key, descending); | |
if (keyRange && keyRange.error) { | |
return handleKeyRangeError(api, opts, keyRange.error, callback); | |
} | |
var stores = [DOC_STORE, BY_SEQ_STORE]; | |
if (opts.attachments) { | |
stores.push(ATTACH_STORE); | |
} | |
var txnResult = openTransactionSafely(idb, stores, 'readonly'); | |
if (txnResult.error) { | |
return callback(txnResult.error); | |
} | |
var txn = txnResult.txn; | |
var docStore = txn.objectStore(DOC_STORE); | |
var seqStore = txn.objectStore(BY_SEQ_STORE); | |
var cursor = descending ? | |
docStore.openCursor(keyRange, descending) : | |
docStore.openCursor(keyRange); | |
var docIdRevIndex = seqStore.index('_doc_id_rev'); | |
var results = []; | |
var docCount = 0; | |
// if the user specifies include_docs=true, then we don't | |
// want to block the main cursor while we're fetching the doc | |
function fetchDocAsynchronously(metadata, row, winningRev) { | |
var key = metadata.id + "::" + winningRev; | |
docIdRevIndex.get(key).onsuccess = function onGetDoc(e) { | |
row.doc = decodeDoc(e.target.result); | |
if (opts.conflicts) { | |
row.doc._conflicts = merge.collectConflicts(metadata); | |
} | |
fetchAttachmentsIfNecessary(row.doc, opts, txn); | |
}; | |
} | |
function allDocsInner(cursor, winningRev, metadata) { | |
var row = { | |
id: metadata.id, | |
key: metadata.id, | |
value: { | |
rev: winningRev | |
} | |
}; | |
var deleted = metadata.deleted; | |
if (opts.deleted === 'ok') { | |
results.push(row); | |
// deleted docs are okay with "keys" requests | |
if (deleted) { | |
row.value.deleted = true; | |
row.doc = null; | |
} else if (opts.include_docs) { | |
fetchDocAsynchronously(metadata, row, winningRev); | |
} | |
} else if (!deleted && skip-- <= 0) { | |
results.push(row); | |
if (opts.include_docs) { | |
fetchDocAsynchronously(metadata, row, winningRev); | |
} | |
if (--limit === 0) { | |
return; | |
} | |
} | |
cursor.continue(); | |
} | |
function onGetCursor(e) { | |
docCount = api._meta.docCount; // do this within the txn for consistency | |
var cursor = e.target.result; | |
if (!cursor) { | |
return; | |
} | |
var metadata = decodeMetadata(cursor.value); | |
var winningRev = metadata.winningRev; | |
allDocsInner(cursor, winningRev, metadata); | |
} | |
function onResultsReady() { | |
callback(null, { | |
total_rows: docCount, | |
offset: opts.skip, | |
rows: results | |
}); | |
} | |
function onTxnComplete() { | |
if (opts.attachments) { | |
postProcessAttachments(results).then(onResultsReady); | |
} else { | |
onResultsReady(); | |
} | |
} | |
txn.oncomplete = onTxnComplete; | |
cursor.onsuccess = onGetCursor; | |
} | |
function allDocs(opts, callback) { | |
if (opts.limit === 0) { | |
return callback(null, { | |
total_rows: api._meta.docCount, | |
offset: opts.skip, | |
rows: [] | |
}); | |
} | |
allDocsQuery(opts, callback); | |
} | |
allDocs(opts, callback); | |
} | |
module.exports = idbAllDocs; | |
},{"../../deps/errors":120,"../../merge":133,"./idb-constants":107,"./idb-utils":108}],105:[function(require,module,exports){ | |
'use strict'; | |
var utils = require('../../utils'); | |
var idbConstants = require('./idb-constants'); | |
var DETECT_BLOB_SUPPORT_STORE = idbConstants.DETECT_BLOB_SUPPORT_STORE; | |
// | |
// Detect blob support. Chrome didn't support it until version 38. | |
// In version 37 they had a broken version where PNGs (and possibly | |
// other binary types) aren't stored correctly, because when you fetch | |
// them, the content type is always null. | |
// | |
// Furthermore, they have some outstanding bugs where blobs occasionally | |
// are read by FileReader as null, or by ajax as 404s. | |
// | |
// Sadly we use the 404 bug to detect the FileReader bug, so if they | |
// get fixed independently and released in different versions of Chrome, | |
// then the bug could come back. So it's worthwhile to watch these issues: | |
// 404 bug: https://code.google.com/p/chromium/issues/detail?id=447916 | |
// FileReader bug: https://code.google.com/p/chromium/issues/detail?id=447836 | |
// | |
function checkBlobSupport(txn, idb) { | |
return new utils.Promise(function (resolve, reject) { | |
var blob = utils.createBlob([''], {type: 'image/png'}); | |
txn.objectStore(DETECT_BLOB_SUPPORT_STORE).put(blob, 'key'); | |
txn.oncomplete = function () { | |
// have to do it in a separate transaction, else the correct | |
// content type is always returned | |
var blobTxn = idb.transaction([DETECT_BLOB_SUPPORT_STORE], | |
'readwrite'); | |
var getBlobReq = blobTxn.objectStore( | |
DETECT_BLOB_SUPPORT_STORE).get('key'); | |
getBlobReq.onerror = reject; | |
getBlobReq.onsuccess = function (e) { | |
var storedBlob = e.target.result; | |
var url = URL.createObjectURL(storedBlob); | |
utils.ajax({ | |
url: url, | |
cache: true, | |
binary: true | |
}, function (err, res) { | |
if (err && err.status === 405) { | |
// firefox won't let us do that. but firefox doesn't | |
// have the blob type bug that Chrome does, so that's ok | |
resolve(true); | |
} else { | |
resolve(!!(res && res.type === 'image/png')); | |
if (err && err.status === 404) { | |
utils.explain404('PouchDB is just detecting blob URL support.'); | |
} | |
} | |
URL.revokeObjectURL(url); | |
}); | |
}; | |
}; | |
}).catch(function () { | |
return false; // error, so assume unsupported | |
}); | |
} | |
module.exports = checkBlobSupport; | |
},{"../../utils":138,"./idb-constants":107}],106:[function(require,module,exports){ | |
'use strict'; | |
var utils = require('../../utils'); | |
var errors = require('../../deps/errors'); | |
var idbUtils = require('./idb-utils'); | |
var idbConstants = require('./idb-constants'); | |
var ATTACH_AND_SEQ_STORE = idbConstants.ATTACH_AND_SEQ_STORE; | |
var ATTACH_STORE = idbConstants.ATTACH_STORE; | |
var BY_SEQ_STORE = idbConstants.BY_SEQ_STORE; | |
var DOC_STORE = idbConstants.DOC_STORE; | |
var LOCAL_STORE = idbConstants.LOCAL_STORE; | |
var META_STORE = idbConstants.META_STORE; | |
var compactRevs = idbUtils.compactRevs; | |
var decodeMetadata = idbUtils.decodeMetadata; | |
var encodeMetadata = idbUtils.encodeMetadata; | |
var idbError = idbUtils.idbError; | |
var openTransactionSafely = idbUtils.openTransactionSafely; | |
function idbBulkDocs(req, opts, api, idb, Changes, callback) { | |
var docInfos = req.docs; | |
var txn; | |
var docStore; | |
var bySeqStore; | |
var attachStore; | |
var attachAndSeqStore; | |
var docInfoError; | |
var docCountDelta = 0; | |
for (var i = 0, len = docInfos.length; i < len; i++) { | |
var doc = docInfos[i]; | |
if (doc._id && utils.isLocalId(doc._id)) { | |
continue; | |
} | |
doc = docInfos[i] = utils.parseDoc(doc, opts.new_edits); | |
if (doc.error && !docInfoError) { | |
docInfoError = doc; | |
} | |
} | |
if (docInfoError) { | |
return callback(docInfoError); | |
} | |
var results = new Array(docInfos.length); | |
var fetchedDocs = new utils.Map(); | |
var preconditionErrored = false; | |
var blobType = api._meta.blobSupport ? 'blob' : 'base64'; | |
utils.preprocessAttachments(docInfos, blobType, function (err) { | |
if (err) { | |
return callback(err); | |
} | |
startTransaction(); | |
}); | |
function startTransaction() { | |
var stores = [ | |
DOC_STORE, BY_SEQ_STORE, | |
ATTACH_STORE, META_STORE, | |
LOCAL_STORE, ATTACH_AND_SEQ_STORE | |
]; | |
var txnResult = openTransactionSafely(idb, stores, 'readwrite'); | |
if (txnResult.error) { | |
return callback(txnResult.error); | |
} | |
txn = txnResult.txn; | |
txn.onerror = idbError(callback); | |
txn.ontimeout = idbError(callback); | |
txn.oncomplete = complete; | |
docStore = txn.objectStore(DOC_STORE); | |
bySeqStore = txn.objectStore(BY_SEQ_STORE); | |
attachStore = txn.objectStore(ATTACH_STORE); | |
attachAndSeqStore = txn.objectStore(ATTACH_AND_SEQ_STORE); | |
verifyAttachments(function (err) { | |
if (err) { | |
preconditionErrored = true; | |
return callback(err); | |
} | |
fetchExistingDocs(); | |
}); | |
} | |
function processDocs() { | |
utils.processDocs(docInfos, api, fetchedDocs, txn, results, | |
writeDoc, opts); | |
} | |
function fetchExistingDocs() { | |
if (!docInfos.length) { | |
return; | |
} | |
var numFetched = 0; | |
function checkDone() { | |
if (++numFetched === docInfos.length) { | |
processDocs(); | |
} | |
} | |
function readMetadata(event) { | |
var metadata = decodeMetadata(event.target.result); | |
if (metadata) { | |
fetchedDocs.set(metadata.id, metadata); | |
} | |
checkDone(); | |
} | |
for (var i = 0, len = docInfos.length; i < len; i++) { | |
var docInfo = docInfos[i]; | |
if (docInfo._id && utils.isLocalId(docInfo._id)) { | |
checkDone(); // skip local docs | |
continue; | |
} | |
var req = docStore.get(docInfo.metadata.id); | |
req.onsuccess = readMetadata; | |
} | |
} | |
function complete() { | |
if (preconditionErrored) { | |
return; | |
} | |
Changes.notify(api._meta.name); | |
api._meta.docCount += docCountDelta; | |
callback(null, results); | |
} | |
function verifyAttachment(digest, callback) { | |
var req = attachStore.get(digest); | |
req.onsuccess = function (e) { | |
if (!e.target.result) { | |
var err = errors.error(errors.MISSING_STUB, | |
'unknown stub attachment with digest ' + | |
digest); | |
err.status = 412; | |
callback(err); | |
} else { | |
callback(); | |
} | |
}; | |
} | |
function verifyAttachments(finish) { | |
var digests = []; | |
docInfos.forEach(function (docInfo) { | |
if (docInfo.data && docInfo.data._attachments) { | |
Object.keys(docInfo.data._attachments).forEach(function (filename) { | |
var att = docInfo.data._attachments[filename]; | |
if (att.stub) { | |
digests.push(att.digest); | |
} | |
}); | |
} | |
}); | |
if (!digests.length) { | |
return finish(); | |
} | |
var numDone = 0; | |
var err; | |
function checkDone() { | |
if (++numDone === digests.length) { | |
finish(err); | |
} | |
} | |
digests.forEach(function (digest) { | |
verifyAttachment(digest, function (attErr) { | |
if (attErr && !err) { | |
err = attErr; | |
} | |
checkDone(); | |
}); | |
}); | |
} | |
function writeDoc(docInfo, winningRev, winningRevIsDeleted, newRevIsDeleted, | |
isUpdate, delta, resultsIdx, callback) { | |
docCountDelta += delta; | |
var doc = docInfo.data; | |
doc._id = docInfo.metadata.id; | |
doc._rev = docInfo.metadata.rev; | |
if (newRevIsDeleted) { | |
doc._deleted = true; | |
} | |
var hasAttachments = doc._attachments && | |
Object.keys(doc._attachments).length; | |
if (hasAttachments) { | |
return writeAttachments(docInfo, winningRev, winningRevIsDeleted, | |
isUpdate, resultsIdx, callback); | |
} | |
finishDoc(docInfo, winningRev, winningRevIsDeleted, | |
isUpdate, resultsIdx, callback); | |
} | |
function autoCompact(docInfo) { | |
var revsToDelete = utils.compactTree(docInfo.metadata); | |
compactRevs(revsToDelete, docInfo.metadata.id, txn); | |
} | |
function finishDoc(docInfo, winningRev, winningRevIsDeleted, | |
isUpdate, resultsIdx, callback) { | |
var doc = docInfo.data; | |
var metadata = docInfo.metadata; | |
doc._doc_id_rev = metadata.id + '::' + metadata.rev; | |
delete doc._id; | |
delete doc._rev; | |
function afterPutDoc(e) { | |
if (isUpdate && api.auto_compaction) { | |
autoCompact(docInfo); | |
} | |
metadata.seq = e.target.result; | |
// Current _rev is calculated from _rev_tree on read | |
delete metadata.rev; | |
var metadataToStore = encodeMetadata(metadata, winningRev, | |
winningRevIsDeleted); | |
var metaDataReq = docStore.put(metadataToStore); | |
metaDataReq.onsuccess = afterPutMetadata; | |
} | |
function afterPutDocError(e) { | |
// ConstraintError, need to update, not put (see #1638 for details) | |
e.preventDefault(); // avoid transaction abort | |
e.stopPropagation(); // avoid transaction onerror | |
var index = bySeqStore.index('_doc_id_rev'); | |
var getKeyReq = index.getKey(doc._doc_id_rev); | |
getKeyReq.onsuccess = function (e) { | |
var putReq = bySeqStore.put(doc, e.target.result); | |
putReq.onsuccess = afterPutDoc; | |
}; | |
} | |
function afterPutMetadata() { | |
results[resultsIdx] = { | |
ok: true, | |
id: metadata.id, | |
rev: winningRev | |
}; | |
fetchedDocs.set(docInfo.metadata.id, docInfo.metadata); | |
insertAttachmentMappings(docInfo, metadata.seq, callback); | |
} | |
var putReq = bySeqStore.put(doc); | |
putReq.onsuccess = afterPutDoc; | |
putReq.onerror = afterPutDocError; | |
} | |
function writeAttachments(docInfo, winningRev, winningRevIsDeleted, | |
isUpdate, resultsIdx, callback) { | |
var doc = docInfo.data; | |
var numDone = 0; | |
var attachments = Object.keys(doc._attachments); | |
function collectResults() { | |
if (numDone === attachments.length) { | |
finishDoc(docInfo, winningRev, winningRevIsDeleted, | |
isUpdate, resultsIdx, callback); | |
} | |
} | |
function attachmentSaved() { | |
numDone++; | |
collectResults(); | |
} | |
attachments.forEach(function (key) { | |
var att = docInfo.data._attachments[key]; | |
if (!att.stub) { | |
var data = att.data; | |
delete att.data; | |
var digest = att.digest; | |
saveAttachment(digest, data, attachmentSaved); | |
} else { | |
numDone++; | |
collectResults(); | |
} | |
}); | |
} | |
// map seqs to attachment digests, which | |
// we will need later during compaction | |
function insertAttachmentMappings(docInfo, seq, callback) { | |
var attsAdded = 0; | |
var attsToAdd = Object.keys(docInfo.data._attachments || {}); | |
if (!attsToAdd.length) { | |
return callback(); | |
} | |
function checkDone() { | |
if (++attsAdded === attsToAdd.length) { | |
callback(); | |
} | |
} | |
function add(att) { | |
var digest = docInfo.data._attachments[att].digest; | |
var req = attachAndSeqStore.put({ | |
seq: seq, | |
digestSeq: digest + '::' + seq | |
}); | |
req.onsuccess = checkDone; | |
req.onerror = function (e) { | |
// this callback is for a constaint error, which we ignore | |
// because this docid/rev has already been associated with | |
// the digest (e.g. when new_edits == false) | |
e.preventDefault(); // avoid transaction abort | |
e.stopPropagation(); // avoid transaction onerror | |
checkDone(); | |
}; | |
} | |
for (var i = 0; i < attsToAdd.length; i++) { | |
add(attsToAdd[i]); // do in parallel | |
} | |
} | |
function saveAttachment(digest, data, callback) { | |
var getKeyReq = attachStore.count(digest); | |
getKeyReq.onsuccess = function(e) { | |
var count = e.target.result; | |
if (count) { | |
return callback(); // already exists | |
} | |
var newAtt = { | |
digest: digest, | |
body: data | |
}; | |
var putReq = attachStore.put(newAtt); | |
putReq.onsuccess = callback; | |
}; | |
} | |
} | |
module.exports = idbBulkDocs; | |
},{"../../deps/errors":120,"../../utils":138,"./idb-constants":107,"./idb-utils":108}],107:[function(require,module,exports){ | |
'use strict'; | |
// IndexedDB requires a versioned database structure, so we use the | |
// version here to manage migrations. | |
exports.ADAPTER_VERSION = 5; | |
// The object stores created for each database | |
// DOC_STORE stores the document meta data, its revision history and state | |
// Keyed by document id | |
exports. DOC_STORE = 'document-store'; | |
// BY_SEQ_STORE stores a particular version of a document, keyed by its | |
// sequence id | |
exports.BY_SEQ_STORE = 'by-sequence'; | |
// Where we store attachments | |
exports.ATTACH_STORE = 'attach-store'; | |
// Where we store many-to-many relations | |
// between attachment digests and seqs | |
exports.ATTACH_AND_SEQ_STORE = 'attach-seq-store'; | |
// Where we store database-wide meta data in a single record | |
// keyed by id: META_STORE | |
exports.META_STORE = 'meta-store'; | |
// Where we store local documents | |
exports.LOCAL_STORE = 'local-store'; | |
// Where we detect blob support | |
exports.DETECT_BLOB_SUPPORT_STORE = 'detect-blob-support'; | |
},{}],108:[function(require,module,exports){ | |
(function (process){ | |
'use strict'; | |
var errors = require('../../deps/errors'); | |
var utils = require('../../utils'); | |
var constants = require('./idb-constants'); | |
function tryCode(fun, that, args) { | |
try { | |
fun.apply(that, args); | |
} catch (err) { // shouldn't happen | |
if (typeof PouchDB !== 'undefined') { | |
PouchDB.emit('error', err); | |
} | |
} | |
} | |
exports.taskQueue = { | |
running: false, | |
queue: [] | |
}; | |
exports.applyNext = function () { | |
if (exports.taskQueue.running || !exports.taskQueue.queue.length) { | |
return; | |
} | |
exports.taskQueue.running = true; | |
var item = exports.taskQueue.queue.shift(); | |
item.action(function (err, res) { | |
tryCode(item.callback, this, [err, res]); | |
exports.taskQueue.running = false; | |
process.nextTick(exports.applyNext); | |
}); | |
}; | |
exports.idbError = function (callback) { | |
return function (event) { | |
var message = (event.target && event.target.error && | |
event.target.error.name) || event.target; | |
callback(errors.error(errors.IDB_ERROR, message, event.type)); | |
}; | |
}; | |
// Unfortunately, the metadata has to be stringified | |
// when it is put into the database, because otherwise | |
// IndexedDB can throw errors for deeply-nested objects. | |
// Originally we just used JSON.parse/JSON.stringify; now | |
// we use this custom vuvuzela library that avoids recursion. | |
// If we could do it all over again, we'd probably use a | |
// format for the revision trees other than JSON. | |
exports.encodeMetadata = function (metadata, winningRev, deleted) { | |
return { | |
data: utils.safeJsonStringify(metadata), | |
winningRev: winningRev, | |
deletedOrLocal: deleted ? '1' : '0', | |
seq: metadata.seq, // highest seq for this doc | |
id: metadata.id | |
}; | |
}; | |
exports.decodeMetadata = function (storedObject) { | |
if (!storedObject) { | |
return null; | |
} | |
var metadata = utils.safeJsonParse(storedObject.data); | |
metadata.winningRev = storedObject.winningRev; | |
metadata.deleted = storedObject.deletedOrLocal === '1'; | |
metadata.seq = storedObject.seq; | |
return metadata; | |
}; | |
// read the doc back out from the database. we don't store the | |
// _id or _rev because we already have _doc_id_rev. | |
exports.decodeDoc = function (doc) { | |
if (!doc) { | |
return doc; | |
} | |
var idx = utils.lastIndexOf(doc._doc_id_rev, ':'); | |
doc._id = doc._doc_id_rev.substring(0, idx - 1); | |
doc._rev = doc._doc_id_rev.substring(idx + 1); | |
delete doc._doc_id_rev; | |
return doc; | |
}; | |
// Read a blob from the database, encoding as necessary | |
// and translating from base64 if the IDB doesn't support | |
// native Blobs | |
exports.readBlobData = function (body, type, encode, callback) { | |
if (encode) { | |
if (!body) { | |
callback(''); | |
} else if (typeof body !== 'string') { // we have blob support | |
utils.readAsBinaryString(body, function (binary) { | |
callback(utils.btoa(binary)); | |
}); | |
} else { // no blob support | |
callback(body); | |
} | |
} else { | |
if (!body) { | |
callback(utils.createBlob([''], {type: type})); | |
} else if (typeof body !== 'string') { // we have blob support | |
callback(body); | |
} else { // no blob support | |
body = utils.fixBinary(atob(body)); | |
callback(utils.createBlob([body], {type: type})); | |
} | |
} | |
}; | |
exports.fetchAttachmentsIfNecessary = function (doc, opts, txn, cb) { | |
var attachments = Object.keys(doc._attachments || {}); | |
if (!attachments.length) { | |
return cb && cb(); | |
} | |
var numDone = 0; | |
function checkDone() { | |
if (++numDone === attachments.length && cb) { | |
cb(); | |
} | |
} | |
function fetchAttachment(doc, att) { | |
var attObj = doc._attachments[att]; | |
var digest = attObj.digest; | |
var req = txn.objectStore(constants.ATTACH_STORE).get(digest); | |
req.onsuccess = function (e) { | |
attObj.body = e.target.result.body; | |
checkDone(); | |
}; | |
} | |
attachments.forEach(function (att) { | |
if (opts.attachments && opts.include_docs) { | |
fetchAttachment(doc, att); | |
} else { | |
doc._attachments[att].stub = true; | |
checkDone(); | |
} | |
}); | |
}; | |
// IDB-specific postprocessing necessary because | |
// we don't know whether we stored a true Blob or | |
// a base64-encoded string, and if it's a Blob it | |
// needs to be read outside of the transaction context | |
exports.postProcessAttachments = function (results) { | |
return utils.Promise.all(results.map(function (row) { | |
if (row.doc && row.doc._attachments) { | |
var attNames = Object.keys(row.doc._attachments); | |
return utils.Promise.all(attNames.map(function (att) { | |
var attObj = row.doc._attachments[att]; | |
if (!('body' in attObj)) { // already processed | |
return; | |
} | |
var body = attObj.body; | |
var type = attObj.content_type; | |
return new utils.Promise(function (resolve) { | |
exports.readBlobData(body, type, true, function (base64) { | |
row.doc._attachments[att] = utils.extend( | |
utils.pick(attObj, ['digest', 'content_type']), | |
{data: base64} | |
); | |
resolve(); | |
}); | |
}); | |
})); | |
} | |
})); | |
}; | |
exports.compactRevs = function (revs, docId, txn) { | |
var possiblyOrphanedDigests = []; | |
var seqStore = txn.objectStore(constants.BY_SEQ_STORE); | |
var attStore = txn.objectStore(constants.ATTACH_STORE); | |
var attAndSeqStore = txn.objectStore(constants.ATTACH_AND_SEQ_STORE); | |
var count = revs.length; | |
function checkDone() { | |
count--; | |
if (!count) { // done processing all revs | |
deleteOrphanedAttachments(); | |
} | |
} | |
function deleteOrphanedAttachments() { | |
if (!possiblyOrphanedDigests.length) { | |
return; | |
} | |
possiblyOrphanedDigests.forEach(function (digest) { | |
var countReq = attAndSeqStore.index('digestSeq').count( | |
IDBKeyRange.bound( | |
digest + '::', digest + '::\uffff', false, false)); | |
countReq.onsuccess = function (e) { | |
var count = e.target.result; | |
if (!count) { | |
// orphaned | |
attStore.delete(digest); | |
} | |
}; | |
}); | |
} | |
revs.forEach(function (rev) { | |
var index = seqStore.index('_doc_id_rev'); | |
var key = docId + "::" + rev; | |
index.getKey(key).onsuccess = function (e) { | |
var seq = e.target.result; | |
if (typeof seq !== 'number') { | |
return checkDone(); | |
} | |
seqStore.delete(seq); | |
var cursor = attAndSeqStore.index('seq') | |
.openCursor(IDBKeyRange.only(seq)); | |
cursor.onsuccess = function (event) { | |
var cursor = event.target.result; | |
if (cursor) { | |
var digest = cursor.value.digestSeq.split('::')[0]; | |
possiblyOrphanedDigests.push(digest); | |
attAndSeqStore.delete(cursor.primaryKey); | |
cursor.continue(); | |
} else { // done | |
checkDone(); | |
} | |
}; | |
}; | |
}); | |
}; | |
exports.openTransactionSafely = function (idb, stores, mode) { | |
try { | |
return { | |
txn: idb.transaction(stores, mode) | |
}; | |
} catch (err) { | |
return { | |
error: err | |
}; | |
} | |
}; | |
}).call(this,require("/Users/nlawson/workspace-node/pouchdb-authentication/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js")) | |
},{"../../deps/errors":120,"../../utils":138,"./idb-constants":107,"/Users/nlawson/workspace-node/pouchdb-authentication/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":46}],109:[function(require,module,exports){ | |
(function (process){ | |
'use strict'; | |
var utils = require('../../utils'); | |
var merge = require('../../merge'); | |
var errors = require('../../deps/errors'); | |
var idbUtils = require('./idb-utils'); | |
var idbConstants = require('./idb-constants'); | |
var idbBulkDocs = require('./idb-bulk-docs'); | |
var idbAllDocs = require('./idb-all-docs'); | |
var checkBlobSupport = require('./idb-blob-support'); | |
var ADAPTER_VERSION = idbConstants.ADAPTER_VERSION; | |
var ATTACH_AND_SEQ_STORE = idbConstants.ATTACH_AND_SEQ_STORE; | |
var ATTACH_STORE = idbConstants.ATTACH_STORE; | |
var BY_SEQ_STORE = idbConstants.BY_SEQ_STORE; | |
var DETECT_BLOB_SUPPORT_STORE = idbConstants.DETECT_BLOB_SUPPORT_STORE; | |
var DOC_STORE = idbConstants.DOC_STORE; | |
var LOCAL_STORE = idbConstants.LOCAL_STORE; | |
var META_STORE = idbConstants.META_STORE; | |
var applyNext = idbUtils.applyNext; | |
var compactRevs = idbUtils.compactRevs; | |
var decodeDoc = idbUtils.decodeDoc; | |
var decodeMetadata = idbUtils.decodeMetadata; | |
var encodeMetadata = idbUtils.encodeMetadata; | |
var fetchAttachmentsIfNecessary = idbUtils.fetchAttachmentsIfNecessary; | |
var idbError = idbUtils.idbError; | |
var postProcessAttachments = idbUtils.postProcessAttachments; | |
var readBlobData = idbUtils.readBlobData; | |
var taskQueue = idbUtils.taskQueue; | |
var openTransactionSafely = idbUtils.openTransactionSafely; | |
var cachedDBs = {}; | |
var blobSupportPromise; | |
function IdbPouch(opts, callback) { | |
var api = this; | |
taskQueue.queue.push({ | |
action: function (thisCallback) { | |
init(api, opts, thisCallback); | |
}, | |
callback: callback | |
}); | |
applyNext(); | |
} | |
function init(api, opts, callback) { | |
var dbName = opts.name; | |
var idb = null; | |
api._meta = null; | |
// called when creating a fresh new database | |
function createSchema(db) { | |
var docStore = db.createObjectStore(DOC_STORE, {keyPath : 'id'}); | |
db.createObjectStore(BY_SEQ_STORE, {autoIncrement: true}) | |
.createIndex('_doc_id_rev', '_doc_id_rev', {unique: true}); | |
db.createObjectStore(ATTACH_STORE, {keyPath: 'digest'}); | |
db.createObjectStore(META_STORE, {keyPath: 'id', autoIncrement: false}); | |
db.createObjectStore(DETECT_BLOB_SUPPORT_STORE); | |
// added in v2 | |
docStore.createIndex('deletedOrLocal', 'deletedOrLocal', {unique : false}); | |
// added in v3 | |
db.createObjectStore(LOCAL_STORE, {keyPath: '_id'}); | |
// added in v4 | |
var attAndSeqStore = db.createObjectStore(ATTACH_AND_SEQ_STORE, | |
{autoIncrement: true}); | |
attAndSeqStore.createIndex('seq', 'seq'); | |
attAndSeqStore.createIndex('digestSeq', 'digestSeq', {unique: true}); | |
} | |
// migration to version 2 | |
// unfortunately "deletedOrLocal" is a misnomer now that we no longer | |
// store local docs in the main doc-store, but whaddyagonnado | |
function addDeletedOrLocalIndex(txn, callback) { | |
var docStore = txn.objectStore(DOC_STORE); | |
docStore.createIndex('deletedOrLocal', 'deletedOrLocal', {unique : false}); | |
docStore.openCursor().onsuccess = function (event) { | |
var cursor = event.target.result; | |
if (cursor) { | |
var metadata = cursor.value; | |
var deleted = utils.isDeleted(metadata); | |
metadata.deletedOrLocal = deleted ? "1" : "0"; | |
docStore.put(metadata); | |
cursor.continue(); | |
} else { | |
callback(); | |
} | |
}; | |
} | |
// migration to version 3 (part 1) | |
function createLocalStoreSchema(db) { | |
db.createObjectStore(LOCAL_STORE, {keyPath: '_id'}) | |
.createIndex('_doc_id_rev', '_doc_id_rev', {unique: true}); | |
} | |
// migration to version 3 (part 2) | |
function migrateLocalStore(txn, cb) { | |
var localStore = txn.objectStore(LOCAL_STORE); | |
var docStore = txn.objectStore(DOC_STORE); | |
var seqStore = txn.objectStore(BY_SEQ_STORE); | |
var cursor = docStore.openCursor(); | |
cursor.onsuccess = function (event) { | |
var cursor = event.target.result; | |
if (cursor) { | |
var metadata = cursor.value; | |
var docId = metadata.id; | |
var local = utils.isLocalId(docId); | |
var rev = merge.winningRev(metadata); | |
if (local) { | |
var docIdRev = docId + "::" + rev; | |
// remove all seq entries | |
// associated with this docId | |
var start = docId + "::"; | |
var end = docId + "::~"; | |
var index = seqStore.index('_doc_id_rev'); | |
var range = IDBKeyRange.bound(start, end, false, false); | |
var seqCursor = index.openCursor(range); | |
seqCursor.onsuccess = function (e) { | |
seqCursor = e.target.result; | |
if (!seqCursor) { | |
// done | |
docStore.delete(cursor.primaryKey); | |
cursor.continue(); | |
} else { | |
var data = seqCursor.value; | |
if (data._doc_id_rev === docIdRev) { | |
localStore.put(data); | |
} | |
seqStore.delete(seqCursor.primaryKey); | |
seqCursor.continue(); | |
} | |
}; | |
} else { | |
cursor.continue(); | |
} | |
} else if (cb) { | |
cb(); | |
} | |
}; | |
} | |
// migration to version 4 (part 1) | |
function addAttachAndSeqStore(db) { | |
var attAndSeqStore = db.createObjectStore(ATTACH_AND_SEQ_STORE, | |
{autoIncrement: true}); | |
attAndSeqStore.createIndex('seq', 'seq'); | |
attAndSeqStore.createIndex('digestSeq', 'digestSeq', {unique: true}); | |
} | |
// migration to version 4 (part 2) | |
function migrateAttsAndSeqs(txn, callback) { | |
var seqStore = txn.objectStore(BY_SEQ_STORE); | |
var attStore = txn.objectStore(ATTACH_STORE); | |
var attAndSeqStore = txn.objectStore(ATTACH_AND_SEQ_STORE); | |
// need to actually populate the table. this is the expensive part, | |
// so as an optimization, check first that this database even | |
// contains attachments | |
var req = attStore.count(); | |
req.onsuccess = function (e) { | |
var count = e.target.result; | |
if (!count) { | |
return callback(); // done | |
} | |
seqStore.openCursor().onsuccess = function (e) { | |
var cursor = e.target.result; | |
if (!cursor) { | |
return callback(); // done | |
} | |
var doc = cursor.value; | |
var seq = cursor.primaryKey; | |
var atts = Object.keys(doc._attachments || {}); | |
var digestMap = {}; | |
for (var j = 0; j < atts.length; j++) { | |
var att = doc._attachments[atts[j]]; | |
digestMap[att.digest] = true; // uniq digests, just in case | |
} | |
var digests = Object.keys(digestMap); | |
for (j = 0; j < digests.length; j++) { | |
var digest = digests[j]; | |
attAndSeqStore.put({ | |
seq: seq, | |
digestSeq: digest + '::' + seq | |
}); | |
} | |
cursor.continue(); | |
}; | |
}; | |
} | |
// migration to version 5 | |
// Instead of relying on on-the-fly migration of metadata, | |
// this brings the doc-store to its modern form: | |
// - metadata.winningrev | |
// - metadata.seq | |
// - stringify the metadata when storing it | |
function migrateMetadata(txn) { | |
function decodeMetadataCompat(storedObject) { | |
if (!storedObject.data) { | |
// old format, when we didn't store it stringified | |
storedObject.deleted = storedObject.deletedOrLocal === '1'; | |
return storedObject; | |
} | |
return decodeMetadata(storedObject); | |
} | |
// ensure that every metadata has a winningRev and seq, | |
// which was previously created on-the-fly but better to migrate | |
var bySeqStore = txn.objectStore(BY_SEQ_STORE); | |
var docStore = txn.objectStore(DOC_STORE); | |
var cursor = docStore.openCursor(); | |
cursor.onsuccess = function (e) { | |
var cursor = e.target.result; | |
if (!cursor) { | |
return; // done | |
} | |
var metadata = decodeMetadataCompat(cursor.value); | |
metadata.winningRev = metadata.winningRev || merge.winningRev(metadata); | |
function fetchMetadataSeq() { | |
// metadata.seq was added post-3.2.0, so if it's missing, | |
// we need to fetch it manually | |
var start = metadata.id + '::'; | |
var end = metadata.id + '::\uffff'; | |
var req = bySeqStore.index('_doc_id_rev').openCursor( | |
IDBKeyRange.bound(start, end)); | |
var metadataSeq = 0; | |
req.onsuccess = function (e) { | |
var cursor = e.target.result; | |
if (!cursor) { | |
metadata.seq = metadataSeq; | |
return onGetMetadataSeq(); | |
} | |
var seq = cursor.primaryKey; | |
if (seq > metadataSeq) { | |
metadataSeq = seq; | |
} | |
cursor.continue(); | |
}; | |
} | |
function onGetMetadataSeq() { | |
var metadataToStore = encodeMetadata(metadata, | |
metadata.winningRev, metadata.deleted); | |
var req = docStore.put(metadataToStore); | |
req.onsuccess = function () { | |
cursor.continue(); | |
}; | |
} | |
if (metadata.seq) { | |
return onGetMetadataSeq(); | |
} | |
fetchMetadataSeq(); | |
}; | |
} | |
api.type = function () { | |
return 'idb'; | |
}; | |
api._id = utils.toPromise(function (callback) { | |
callback(null, api._meta.instanceId); | |
}); | |
api._bulkDocs = function idb_bulkDocs(req, opts, callback) { | |
idbBulkDocs(req, opts, api, idb, IdbPouch.Changes, callback); | |
}; | |
// First we look up the metadata in the ids database, then we fetch the | |
// current revision(s) from the by sequence store | |
api._get = function idb_get(id, opts, callback) { | |
var doc; | |
var metadata; | |
var err; | |
var txn; | |
opts = utils.clone(opts); | |
if (opts.ctx) { | |
txn = opts.ctx; | |
} else { | |
var txnResult = openTransactionSafely(idb, | |
[DOC_STORE, BY_SEQ_STORE, ATTACH_STORE], 'readonly'); | |
if (txnResult.error) { | |
return callback(txnResult.error); | |
} | |
txn = txnResult.txn; | |
} | |
function finish() { | |
callback(err, {doc: doc, metadata: metadata, ctx: txn}); | |
} | |
txn.objectStore(DOC_STORE).get(id).onsuccess = function (e) { | |
metadata = decodeMetadata(e.target.result); | |
// we can determine the result here if: | |
// 1. there is no such document | |
// 2. the document is deleted and we don't ask about specific rev | |
// When we ask with opts.rev we expect the answer to be either | |
// doc (possibly with _deleted=true) or missing error | |
if (!metadata) { | |
err = errors.error(errors.MISSING_DOC, 'missing'); | |
return finish(); | |
} | |
if (utils.isDeleted(metadata) && !opts.rev) { | |
err = errors.error(errors.MISSING_DOC, "deleted"); | |
return finish(); | |
} | |
var objectStore = txn.objectStore(BY_SEQ_STORE); | |
var rev = opts.rev || metadata.winningRev; | |
var key = metadata.id + '::' + rev; | |
objectStore.index('_doc_id_rev').get(key).onsuccess = function (e) { | |
doc = e.target.result; | |
if (doc) { | |
doc = decodeDoc(doc); | |
} | |
if (!doc) { | |
err = errors.error(errors.MISSING_DOC, 'missing'); | |
return finish(); | |
} | |
finish(); | |
}; | |
}; | |
}; | |
api._getAttachment = function (attachment, opts, callback) { | |
var txn; | |
opts = utils.clone(opts); | |
if (opts.ctx) { | |
txn = opts.ctx; | |
} else { | |
var txnResult = openTransactionSafely(idb, | |
[DOC_STORE, BY_SEQ_STORE, ATTACH_STORE], 'readonly'); | |
if (txnResult.error) { | |
return callback(txnResult.error); | |
} | |
txn = txnResult.txn; | |
} | |
var digest = attachment.digest; | |
var type = attachment.content_type; | |
txn.objectStore(ATTACH_STORE).get(digest).onsuccess = function (e) { | |
var body = e.target.result.body; | |
readBlobData(body, type, opts.encode, function (blobData) { | |
callback(null, blobData); | |
}); | |
}; | |
}; | |
api._info = function idb_info(callback) { | |
if (idb === null || !cachedDBs[dbName]) { | |
var error = new Error('db isn\'t open'); | |
error.id = 'idbNull'; | |
return callback(error); | |
} | |
var updateSeq; | |
var docCount; | |
var txnResult = openTransactionSafely(idb, [BY_SEQ_STORE], 'readonly'); | |
if (txnResult.error) { | |
return callback(txnResult.error); | |
} | |
var txn = txnResult.txn; | |
var cursor = txn.objectStore(BY_SEQ_STORE).openCursor(null, 'prev'); | |
cursor.onsuccess = function (event) { | |
var cursor = event.target.result; | |
updateSeq = cursor ? cursor.key : 0; | |
// count within the same txn for consistency | |
docCount = api._meta.docCount; | |
}; | |
txn.oncomplete = function () { | |
callback(null, { | |
doc_count: docCount, | |
update_seq: updateSeq, | |
// for debugging | |
idb_attachment_format: (api._meta.blobSupport ? 'binary' : 'base64') | |
}); | |
}; | |
}; | |
api._allDocs = function idb_allDocs(opts, callback) { | |
idbAllDocs(opts, api, idb, callback); | |
}; | |
api._changes = function (opts) { | |
opts = utils.clone(opts); | |
if (opts.continuous) { | |
var id = dbName + ':' + utils.uuid(); | |
IdbPouch.Changes.addListener(dbName, id, api, opts); | |
IdbPouch.Changes.notify(dbName); | |
return { | |
cancel: function () { | |
IdbPouch.Changes.removeListener(dbName, id); | |
} | |
}; | |
} | |
var docIds = opts.doc_ids && new utils.Set(opts.doc_ids); | |
var descending = opts.descending ? 'prev' : null; | |
opts.since = opts.since || 0; | |
var lastSeq = opts.since; | |
var limit = 'limit' in opts ? opts.limit : -1; | |
if (limit === 0) { | |
limit = 1; // per CouchDB _changes spec | |
} | |
var returnDocs; | |
if ('returnDocs' in opts) { | |
returnDocs = opts.returnDocs; | |
} else { | |
returnDocs = true; | |
} | |
var results = []; | |
var numResults = 0; | |
var filter = utils.filterChange(opts); | |
var docIdsToMetadata = new utils.Map(); | |
var txn; | |
var bySeqStore; | |
var docStore; | |
function onGetCursor(cursor) { | |
var doc = decodeDoc(cursor.value); | |
var seq = cursor.key; | |
if (docIds && !docIds.has(doc._id)) { | |
return cursor.continue(); | |
} | |
var metadata; | |
function onGetMetadata() { | |
if (metadata.seq !== seq) { | |
// some other seq is later | |
return cursor.continue(); | |
} | |
lastSeq = seq; | |
if (metadata.winningRev === doc._rev) { | |
return onGetWinningDoc(doc); | |
} | |
fetchWinningDoc(); | |
} | |
function fetchWinningDoc() { | |
var docIdRev = doc._id + '::' + metadata.winningRev; | |
var req = bySeqStore.index('_doc_id_rev').openCursor( | |
IDBKeyRange.bound(docIdRev, docIdRev + '\uffff')); | |
req.onsuccess = function (e) { | |
onGetWinningDoc(decodeDoc(e.target.result.value)); | |
}; | |
} | |
function onGetWinningDoc(winningDoc) { | |
var change = opts.processChange(winningDoc, metadata, opts); | |
change.seq = metadata.seq; | |
if (filter(change)) { | |
numResults++; | |
if (returnDocs) { | |
results.push(change); | |
} | |
// process the attachment immediately | |
// for the benefit of live listeners | |
if (opts.attachments && opts.include_docs) { | |
fetchAttachmentsIfNecessary(winningDoc, opts, txn, function () { | |
postProcessAttachments([change]).then(function () { | |
opts.onChange(change); | |
}); | |
}); | |
} else { | |
opts.onChange(change); | |
} | |
} | |
if (numResults !== limit) { | |
cursor.continue(); | |
} | |
} | |
metadata = docIdsToMetadata.get(doc._id); | |
if (metadata) { // cached | |
return onGetMetadata(); | |
} | |
// metadata not cached, have to go fetch it | |
docStore.get(doc._id).onsuccess = function (event) { | |
metadata = decodeMetadata(event.target.result); | |
docIdsToMetadata.set(doc._id, metadata); | |
onGetMetadata(); | |
}; | |
} | |
function onsuccess(event) { | |
var cursor = event.target.result; | |
if (!cursor) { | |
return; | |
} | |
onGetCursor(cursor); | |
} | |
function fetchChanges() { | |
var objectStores = [DOC_STORE, BY_SEQ_STORE]; | |
if (opts.attachments) { | |
objectStores.push(ATTACH_STORE); | |
} | |
var txnResult = openTransactionSafely(idb, objectStores, 'readonly'); | |
if (txnResult.error) { | |
return opts.complete(txnResult.error); | |
} | |
txn = txnResult.txn; | |
txn.onerror = idbError(opts.complete); | |
txn.oncomplete = onTxnComplete; | |
bySeqStore = txn.objectStore(BY_SEQ_STORE); | |
docStore = txn.objectStore(DOC_STORE); | |
var req; | |
if (descending) { | |
req = bySeqStore.openCursor( | |
null, descending); | |
} else { | |
req = bySeqStore.openCursor( | |
IDBKeyRange.lowerBound(opts.since, true)); | |
} | |
req.onsuccess = onsuccess; | |
} | |
fetchChanges(); | |
function onTxnComplete() { | |
function finish() { | |
opts.complete(null, { | |
results: results, | |
last_seq: lastSeq | |
}); | |
} | |
if (!opts.continuous && opts.attachments) { | |
// cannot guarantee that postProcessing was already done, | |
// so do it again | |
postProcessAttachments(results).then(finish); | |
} else { | |
finish(); | |
} | |
} | |
}; | |
api._close = function (callback) { | |
if (idb === null) { | |
return callback(errors.error(errors.NOT_OPEN)); | |
} | |
// https://developer.mozilla.org/en-US/docs/IndexedDB/IDBDatabase#close | |
// "Returns immediately and closes the connection in a separate thread..." | |
idb.close(); | |
delete cachedDBs[dbName]; | |
idb = null; | |
callback(); | |
}; | |
api._getRevisionTree = function (docId, callback) { | |
var txnResult = openTransactionSafely(idb, [DOC_STORE], 'readonly'); | |
if (txnResult.error) { | |
return callback(txnResult.error); | |
} | |
var txn = txnResult.txn; | |
var req = txn.objectStore(DOC_STORE).get(docId); | |
req.onsuccess = function (event) { | |
var doc = decodeMetadata(event.target.result); | |
if (!doc) { | |
callback(errors.error(errors.MISSING_DOC)); | |
} else { | |
callback(null, doc.rev_tree); | |
} | |
}; | |
}; | |
// This function removes revisions of document docId | |
// which are listed in revs and sets this document | |
// revision to to rev_tree | |
api._doCompaction = function (docId, revs, callback) { | |
var stores = [ | |
DOC_STORE, | |
BY_SEQ_STORE, | |
ATTACH_STORE, | |
ATTACH_AND_SEQ_STORE | |
]; | |
var txnResult = openTransactionSafely(idb, stores, 'readwrite'); | |
if (txnResult.error) { | |
return callback(txnResult.error); | |
} | |
var txn = txnResult.txn; | |
var docStore = txn.objectStore(DOC_STORE); | |
docStore.get(docId).onsuccess = function (event) { | |
var metadata = decodeMetadata(event.target.result); | |
merge.traverseRevTree(metadata.rev_tree, function (isLeaf, pos, | |
revHash, ctx, opts) { | |
var rev = pos + '-' + revHash; | |
if (revs.indexOf(rev) !== -1) { | |
opts.status = 'missing'; | |
} | |
}); | |
compactRevs(revs, docId, txn); | |
var winningRev = metadata.winningRev; | |
var deleted = metadata.deleted; | |
txn.objectStore(DOC_STORE).put( | |
encodeMetadata(metadata, winningRev, deleted)); | |
}; | |
txn.onerror = idbError(callback); | |
txn.oncomplete = function () { | |
utils.call(callback); | |
}; | |
}; | |
api._getLocal = function (id, callback) { | |
var txnResult = openTransactionSafely(idb, [LOCAL_STORE], 'readonly'); | |
if (txnResult.error) { | |
return callback(txnResult.error); | |
} | |
var tx = txnResult.txn; | |
var req = tx.objectStore(LOCAL_STORE).get(id); | |
req.onerror = idbError(callback); | |
req.onsuccess = function (e) { | |
var doc = e.target.result; | |
if (!doc) { | |
callback(errors.error(errors.MISSING_DOC)); | |
} else { | |
delete doc['_doc_id_rev']; // for backwards compat | |
callback(null, doc); | |
} | |
}; | |
}; | |
api._putLocal = function (doc, opts, callback) { | |
if (typeof opts === 'function') { | |
callback = opts; | |
opts = {}; | |
} | |
delete doc._revisions; // ignore this, trust the rev | |
var oldRev = doc._rev; | |
var id = doc._id; | |
if (!oldRev) { | |
doc._rev = '0-1'; | |
} else { | |
doc._rev = '0-' + (parseInt(oldRev.split('-')[1], 10) + 1); | |
} | |
var tx = opts.ctx; | |
var ret; | |
if (!tx) { | |
var txnResult = openTransactionSafely(idb, [LOCAL_STORE], 'readwrite'); | |
if (txnResult.error) { | |
return callback(txnResult.error); | |
} | |
tx = txnResult.txn; | |
tx.onerror = idbError(callback); | |
tx.oncomplete = function () { | |
if (ret) { | |
callback(null, ret); | |
} | |
}; | |
} | |
var oStore = tx.objectStore(LOCAL_STORE); | |
var req; | |
if (oldRev) { | |
req = oStore.get(id); | |
req.onsuccess = function (e) { | |
var oldDoc = e.target.result; | |
if (!oldDoc || oldDoc._rev !== oldRev) { | |
callback(errors.error(errors.REV_CONFLICT)); | |
} else { // update | |
var req = oStore.put(doc); | |
req.onsuccess = function () { | |
ret = {ok: true, id: doc._id, rev: doc._rev}; | |
if (opts.ctx) { // return immediately | |
callback(null, ret); | |
} | |
}; | |
} | |
}; | |
} else { // new doc | |
req = oStore.add(doc); | |
req.onerror = function (e) { | |
// constraint error, already exists | |
callback(errors.error(errors.REV_CONFLICT)); | |
e.preventDefault(); // avoid transaction abort | |
e.stopPropagation(); // avoid transaction onerror | |
}; | |
req.onsuccess = function () { | |
ret = {ok: true, id: doc._id, rev: doc._rev}; | |
if (opts.ctx) { // return immediately | |
callback(null, ret); | |
} | |
}; | |
} | |
}; | |
api._removeLocal = function (doc, callback) { | |
var txnResult = openTransactionSafely(idb, [LOCAL_STORE], 'readwrite'); | |
if (txnResult.error) { | |
return callback(txnResult.error); | |
} | |
var tx = txnResult.txn; | |
var ret; | |
tx.oncomplete = function () { | |
if (ret) { | |
callback(null, ret); | |
} | |
}; | |
var id = doc._id; | |
var oStore = tx.objectStore(LOCAL_STORE); | |
var req = oStore.get(id); | |
req.onerror = idbError(callback); | |
req.onsuccess = function (e) { | |
var oldDoc = e.target.result; | |
if (!oldDoc || oldDoc._rev !== doc._rev) { | |
callback(errors.error(errors.MISSING_DOC)); | |
} else { | |
oStore.delete(id); | |
ret = {ok: true, id: id, rev: '0-0'}; | |
} | |
}; | |
}; | |
api._destroy = function (callback) { | |
IdbPouch.Changes.removeAllListeners(dbName); | |
//Close open request for "dbName" database to fix ie delay. | |
if (IdbPouch.openReqList[dbName] && IdbPouch.openReqList[dbName].result) { | |
IdbPouch.openReqList[dbName].result.close(); | |
delete cachedDBs[dbName]; | |
} | |
var req = indexedDB.deleteDatabase(dbName); | |
req.onsuccess = function () { | |
//Remove open request from the list. | |
if (IdbPouch.openReqList[dbName]) { | |
IdbPouch.openReqList[dbName] = null; | |
} | |
if (utils.hasLocalStorage() && (dbName in localStorage)) { | |
delete localStorage[dbName]; | |
} | |
callback(null, { 'ok': true }); | |
}; | |
req.onerror = idbError(callback); | |
}; | |
var cached = cachedDBs[dbName]; | |
if (cached) { | |
idb = cached.idb; | |
api._meta = cached.global; | |
process.nextTick(function () { | |
callback(null, api); | |
}); | |
return; | |
} | |
var req = indexedDB.open(dbName, ADAPTER_VERSION); | |
if (!('openReqList' in IdbPouch)) { | |
IdbPouch.openReqList = {}; | |
} | |
IdbPouch.openReqList[dbName] = req; | |
req.onupgradeneeded = function (e) { | |
var db = e.target.result; | |
if (e.oldVersion < 1) { | |
return createSchema(db); // new db, initial schema | |
} | |
// do migrations | |
var txn = e.currentTarget.transaction; | |
// these migrations have to be done in this function, before | |
// control is returned to the event loop, because IndexedDB | |
if (e.oldVersion < 3) { | |
createLocalStoreSchema(db); // v2 -> v3 | |
} | |
if (e.oldVersion < 4) { | |
addAttachAndSeqStore(db); // v3 -> v4 | |
} | |
var migrations = [ | |
addDeletedOrLocalIndex, // v1 -> v2 | |
migrateLocalStore, // v2 -> v3 | |
migrateAttsAndSeqs, // v3 -> v4 | |
migrateMetadata // v4 -> v5 | |
]; | |
var i = e.oldVersion; | |
function next() { | |
var migration = migrations[i - 1]; | |
i++; | |
if (migration) { | |
migration(txn, next); | |
} | |
} | |
next(); | |
}; | |
req.onsuccess = function (e) { | |
idb = e.target.result; | |
idb.onversionchange = function () { | |
idb.close(); | |
delete cachedDBs[dbName]; | |
}; | |
idb.onabort = function () { | |
idb.close(); | |
delete cachedDBs[dbName]; | |
}; | |
var txn = idb.transaction([ | |
META_STORE, | |
DETECT_BLOB_SUPPORT_STORE, | |
DOC_STORE | |
], 'readwrite'); | |
var req = txn.objectStore(META_STORE).get(META_STORE); | |
var blobSupport = null; | |
var docCount = null; | |
var instanceId = null; | |
req.onsuccess = function (e) { | |
var checkSetupComplete = function () { | |
if (blobSupport === null || docCount === null || | |
instanceId === null) { | |
return; | |
} else { | |
api._meta = { | |
name: dbName, | |
instanceId: instanceId, | |
blobSupport: blobSupport, | |
docCount: docCount | |
}; | |
cachedDBs[dbName] = { | |
idb: idb, | |
global: api._meta | |
}; | |
callback(null, api); | |
} | |
}; | |
// | |
// fetch/store the id | |
// | |
var meta = e.target.result || {id: META_STORE}; | |
if (dbName + '_id' in meta) { | |
instanceId = meta[dbName + '_id']; | |
checkSetupComplete(); | |
} else { | |
instanceId = utils.uuid(); | |
meta[dbName + '_id'] = instanceId; | |
txn.objectStore(META_STORE).put(meta).onsuccess = function () { | |
checkSetupComplete(); | |
}; | |
} | |
// | |
// check blob support | |
// | |
if (!blobSupportPromise) { | |
// make sure blob support is only checked once | |
blobSupportPromise = checkBlobSupport(txn, idb); | |
} | |
blobSupportPromise.then(function (val) { | |
blobSupport = val; | |
checkSetupComplete(); | |
}); | |
// | |
// count docs | |
// | |
var index = txn.objectStore(DOC_STORE).index('deletedOrLocal'); | |
index.count(IDBKeyRange.only('0')).onsuccess = function (e) { | |
docCount = e.target.result; | |
checkSetupComplete(); | |
}; | |
}; | |
}; | |
req.onerror = idbError(callback); | |
} | |
IdbPouch.valid = function () { | |
// Issue #2533, we finally gave up on doing bug | |
// detection instead of browser sniffing. Safari brought us | |
// to our knees. | |
var isSafari = typeof openDatabase !== 'undefined' && | |
/(Safari|iPhone|iPad|iPod)/.test(navigator.userAgent) && | |
!/Chrome/.test(navigator.userAgent) && | |
!/BlackBerry/.test(navigator.platform); | |
// some outdated implementations of IDB that appear on Samsung | |
// and HTC Android devices <4.4 are missing IDBKeyRange | |
return !isSafari && typeof indexedDB !== 'undefined' && | |
typeof IDBKeyRange !== 'undefined'; | |
}; | |
IdbPouch.Changes = new utils.Changes(); | |
module.exports = IdbPouch; | |
}).call(this,require("/Users/nlawson/workspace-node/pouchdb-authentication/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js")) | |
},{"../../deps/errors":120,"../../merge":133,"../../utils":138,"./idb-all-docs":104,"./idb-blob-support":105,"./idb-bulk-docs":106,"./idb-constants":107,"./idb-utils":108,"/Users/nlawson/workspace-node/pouchdb-authentication/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":46}],110:[function(require,module,exports){ | |
'use strict'; | |
var utils = require('../../utils'); | |
var errors = require('../../deps/errors'); | |
var websqlUtils = require('./websql-utils'); | |
var websqlConstants = require('./websql-constants'); | |
var DOC_STORE = websqlConstants.DOC_STORE; | |
var BY_SEQ_STORE = websqlConstants.BY_SEQ_STORE; | |
var ATTACH_STORE = websqlConstants.ATTACH_STORE; | |
var ATTACH_AND_SEQ_STORE = websqlConstants.ATTACH_AND_SEQ_STORE; | |
var select = websqlUtils.select; | |
var stringifyDoc = websqlUtils.stringifyDoc; | |
var compactRevs = websqlUtils.compactRevs; | |
var unknownError = websqlUtils.unknownError; | |
function websqlBulkDocs(req, opts, api, db, Changes, callback) { | |
var newEdits = opts.new_edits; | |
var userDocs = req.docs; | |
// Parse the docs, give them a sequence number for the result | |
var docInfos = userDocs.map(function (doc) { | |
if (doc._id && utils.isLocalId(doc._id)) { | |
return doc; | |
} | |
var newDoc = utils.parseDoc(doc, newEdits); | |
return newDoc; | |
}); | |
var docInfoErrors = docInfos.filter(function (docInfo) { | |
return docInfo.error; | |
}); | |
if (docInfoErrors.length) { | |
return callback(docInfoErrors[0]); | |
} | |
var tx; | |
var results = new Array(docInfos.length); | |
var fetchedDocs = new utils.Map(); | |
var preconditionErrored; | |
function complete() { | |
if (preconditionErrored) { | |
return callback(preconditionErrored); | |
} | |
Changes.notify(api._name); | |
api._docCount = -1; // invalidate | |
callback(null, results); | |
} | |
function verifyAttachment(digest, callback) { | |
var sql = 'SELECT count(*) as cnt FROM ' + ATTACH_STORE + | |
' WHERE digest=?'; | |
tx.executeSql(sql, [digest], function (tx, result) { | |
if (result.rows.item(0).cnt === 0) { | |
var err = errors.error(errors.MISSING_STUB, | |
'unknown stub attachment with digest ' + | |
digest); | |
callback(err); | |
} else { | |
callback(); | |
} | |
}); | |
} | |
function verifyAttachments(finish) { | |
var digests = []; | |
docInfos.forEach(function (docInfo) { | |
if (docInfo.data && docInfo.data._attachments) { | |
Object.keys(docInfo.data._attachments).forEach(function (filename) { | |
var att = docInfo.data._attachments[filename]; | |
if (att.stub) { | |
digests.push(att.digest); | |
} | |
}); | |
} | |
}); | |
if (!digests.length) { | |
return finish(); | |
} | |
var numDone = 0; | |
var err; | |
function checkDone() { | |
if (++numDone === digests.length) { | |
finish(err); | |
} | |
} | |
digests.forEach(function (digest) { | |
verifyAttachment(digest, function (attErr) { | |
if (attErr && !err) { | |
err = attErr; | |
} | |
checkDone(); | |
}); | |
}); | |
} | |
function writeDoc(docInfo, winningRev, winningRevIsDeleted, newRevIsDeleted, | |
isUpdate, delta, resultsIdx, callback) { | |
function finish() { | |
var data = docInfo.data; | |
var deletedInt = newRevIsDeleted ? 1 : 0; | |
var id = data._id; | |
var rev = data._rev; | |
var json = stringifyDoc(data); | |
var sql = 'INSERT INTO ' + BY_SEQ_STORE + | |
' (doc_id, rev, json, deleted) VALUES (?, ?, ?, ?);'; | |
var sqlArgs = [id, rev, json, deletedInt]; | |
// map seqs to attachment digests, which | |
// we will need later during compaction | |
function insertAttachmentMappings(seq, callback) { | |
var attsAdded = 0; | |
var attsToAdd = Object.keys(data._attachments || {}); | |
if (!attsToAdd.length) { | |
return callback(); | |
} | |
function checkDone() { | |
if (++attsAdded === attsToAdd.length) { | |
callback(); | |
} | |
return false; // ack handling a constraint error | |
} | |
function add(att) { | |
var sql = 'INSERT INTO ' + ATTACH_AND_SEQ_STORE + | |
' (digest, seq) VALUES (?,?)'; | |
var sqlArgs = [data._attachments[att].digest, seq]; | |
tx.executeSql(sql, sqlArgs, checkDone, checkDone); | |
// second callback is for a constaint error, which we ignore | |
// because this docid/rev has already been associated with | |
// the digest (e.g. when new_edits == false) | |
} | |
for (var i = 0; i < attsToAdd.length; i++) { | |
add(attsToAdd[i]); // do in parallel | |
} | |
} | |
tx.executeSql(sql, sqlArgs, function (tx, result) { | |
var seq = result.insertId; | |
insertAttachmentMappings(seq, function () { | |
dataWritten(tx, seq); | |
}); | |
}, function () { | |
// constraint error, recover by updating instead (see #1638) | |
var fetchSql = select('seq', BY_SEQ_STORE, null, | |
'doc_id=? AND rev=?'); | |
tx.executeSql(fetchSql, [id, rev], function (tx, res) { | |
var seq = res.rows.item(0).seq; | |
var sql = 'UPDATE ' + BY_SEQ_STORE + | |
' SET json=?, deleted=? WHERE doc_id=? AND rev=?;'; | |
var sqlArgs = [json, deletedInt, id, rev]; | |
tx.executeSql(sql, sqlArgs, function (tx) { | |
insertAttachmentMappings(seq, function () { | |
dataWritten(tx, seq); | |
}); | |
}); | |
}); | |
return false; // ack that we've handled the error | |
}); | |
} | |
function collectResults(attachmentErr) { | |
if (!err) { | |
if (attachmentErr) { | |
err = attachmentErr; | |
callback(err); | |
} else if (recv === attachments.length) { | |
finish(); | |
} | |
} | |
} | |
var err = null; | |
var recv = 0; | |
docInfo.data._id = docInfo.metadata.id; | |
docInfo.data._rev = docInfo.metadata.rev; | |
var attachments = Object.keys(docInfo.data._attachments || {}); | |
if (newRevIsDeleted) { | |
docInfo.data._deleted = true; | |
} | |
function attachmentSaved(err) { | |
recv++; | |
collectResults(err); | |
} | |
attachments.forEach(function (key) { | |
var att = docInfo.data._attachments[key]; | |
if (!att.stub) { | |
var data = att.data; | |
delete att.data; | |
var digest = att.digest; | |
saveAttachment(digest, data, attachmentSaved); | |
} else { | |
recv++; | |
collectResults(); | |
} | |
}); | |
if (!attachments.length) { | |
finish(); | |
} | |
function autoCompact() { | |
if (!isUpdate || !api.auto_compaction) { | |
return; // nothing to do | |
} | |
var id = docInfo.metadata.id; | |
var revsToDelete = utils.compactTree(docInfo.metadata); | |
compactRevs(revsToDelete, id, tx); | |
} | |
function dataWritten(tx, seq) { | |
autoCompact(); | |
docInfo.metadata.seq = seq; | |
delete docInfo.metadata.rev; | |
var sql = isUpdate ? | |
'UPDATE ' + DOC_STORE + | |
' SET json=?, max_seq=?, winningseq=' + | |
'(SELECT seq FROM ' + BY_SEQ_STORE + | |
' WHERE doc_id=' + DOC_STORE + '.id AND rev=?) WHERE id=?' | |
: 'INSERT INTO ' + DOC_STORE + | |
' (id, winningseq, max_seq, json) VALUES (?,?,?,?);'; | |
var metadataStr = utils.safeJsonStringify(docInfo.metadata); | |
var id = docInfo.metadata.id; | |
var params = isUpdate ? | |
[metadataStr, seq, winningRev, id] : | |
[id, seq, seq, metadataStr]; | |
tx.executeSql(sql, params, function () { | |
results[resultsIdx] = { | |
ok: true, | |
id: docInfo.metadata.id, | |
rev: winningRev | |
}; | |
fetchedDocs.set(id, docInfo.metadata); | |
callback(); | |
}); | |
} | |
} | |
function processDocs() { | |
utils.processDocs(docInfos, api, fetchedDocs, | |
tx, results, writeDoc, opts); | |
} | |
function fetchExistingDocs(callback) { | |
if (!docInfos.length) { | |
return callback(); | |
} | |
var numFetched = 0; | |
function checkDone() { | |
if (++numFetched === docInfos.length) { | |
callback(); | |
} | |
} | |
docInfos.forEach(function (docInfo) { | |
if (docInfo._id && utils.isLocalId(docInfo._id)) { | |
return checkDone(); // skip local docs | |
} | |
var id = docInfo.metadata.id; | |
tx.executeSql('SELECT json FROM ' + DOC_STORE + | |
' WHERE id = ?', [id], function (tx, result) { | |
if (result.rows.length) { | |
var metadata = utils.safeJsonParse(result.rows.item(0).json); | |
fetchedDocs.set(id, metadata); | |
} | |
checkDone(); | |
}); | |
}); | |
} | |
function saveAttachment(digest, data, callback) { | |
var sql = 'SELECT digest FROM ' + ATTACH_STORE + ' WHERE digest=?'; | |
tx.executeSql(sql, [digest], function (tx, result) { | |
if (result.rows.length) { // attachment already exists | |
return callback(); | |
} | |
// we could just insert before selecting and catch the error, | |
// but my hunch is that it's cheaper not to serialize the blob | |
// from JS to C if we don't have to (TODO: confirm this) | |
sql = 'INSERT INTO ' + ATTACH_STORE + | |
' (digest, body, escaped) VALUES (?,?,1)'; | |
tx.executeSql(sql, [digest, websqlUtils.escapeBlob(data)], function () { | |
callback(); | |
}, function () { | |
// ignore constaint errors, means it already exists | |
callback(); | |
return false; // ack we handled the error | |
}); | |
}); | |
} | |
utils.preprocessAttachments(docInfos, 'binary', function (err) { | |
if (err) { | |
return callback(err); | |
} | |
db.transaction(function (txn) { | |
tx = txn; | |
verifyAttachments(function (err) { | |
if (err) { | |
preconditionErrored = err; | |
} else { | |
fetchExistingDocs(processDocs); | |
} | |
}); | |
}, unknownError(callback), complete); | |
}); | |
} | |
module.exports = websqlBulkDocs; | |
},{"../../deps/errors":120,"../../utils":138,"./websql-constants":111,"./websql-utils":112}],111:[function(require,module,exports){ | |
'use strict'; | |
function quote(str) { | |
return "'" + str + "'"; | |
} | |
exports.ADAPTER_VERSION = 7; // used to manage migrations | |
// The object stores created for each database | |
// DOC_STORE stores the document meta data, its revision history and state | |
exports.DOC_STORE = quote('document-store'); | |
// BY_SEQ_STORE stores a particular version of a document, keyed by its | |
// sequence id | |
exports.BY_SEQ_STORE = quote('by-sequence'); | |
// Where we store attachments | |
exports.ATTACH_STORE = quote('attach-store'); | |
exports.LOCAL_STORE = quote('local-store'); | |
exports.META_STORE = quote('metadata-store'); | |
// where we store many-to-many relations between attachment | |
// digests and seqs | |
exports.ATTACH_AND_SEQ_STORE = quote('attach-seq-store'); | |
},{}],112:[function(require,module,exports){ | |
'use strict'; | |
var utils = require('../../utils'); | |
var errors = require('../../deps/errors'); | |
var websqlConstants = require('./websql-constants'); | |
var BY_SEQ_STORE = websqlConstants.BY_SEQ_STORE; | |
var ATTACH_STORE = websqlConstants.ATTACH_STORE; | |
var ATTACH_AND_SEQ_STORE = websqlConstants.ATTACH_AND_SEQ_STORE; | |
// escapeBlob and unescapeBlob are workarounds for a websql bug: | |
// https://code.google.com/p/chromium/issues/detail?id=422690 | |
// https://bugs.webkit.org/show_bug.cgi?id=137637 | |
// The goal is to never actually insert the \u0000 character | |
// in the database. | |
function escapeBlob(str) { | |
return str | |
.replace(/\u0002/g, '\u0002\u0002') | |
.replace(/\u0001/g, '\u0001\u0002') | |
.replace(/\u0000/g, '\u0001\u0001'); | |
} | |
function unescapeBlob(str) { | |
return str | |
.replace(/\u0001\u0001/g, '\u0000') | |
.replace(/\u0001\u0002/g, '\u0001') | |
.replace(/\u0002\u0002/g, '\u0002'); | |
} | |
function stringifyDoc(doc) { | |
// don't bother storing the id/rev. it uses lots of space, | |
// in persistent map/reduce especially | |
delete doc._id; | |
delete doc._rev; | |
return JSON.stringify(doc); | |
} | |
function unstringifyDoc(doc, id, rev) { | |
doc = JSON.parse(doc); | |
doc._id = id; | |
doc._rev = rev; | |
return doc; | |
} | |
// question mark groups IN queries, e.g. 3 -> '(?,?,?)' | |
function qMarks(num) { | |
var s = '('; | |
while (num--) { | |
s += '?'; | |
if (num) { | |
s += ','; | |
} | |
} | |
return s + ')'; | |
} | |
function select(selector, table, joiner, where, orderBy) { | |
return 'SELECT ' + selector + ' FROM ' + | |
(typeof table === 'string' ? table : table.join(' JOIN ')) + | |
(joiner ? (' ON ' + joiner) : '') + | |
(where ? (' WHERE ' + | |
(typeof where === 'string' ? where : where.join(' AND '))) : '') + | |
(orderBy ? (' ORDER BY ' + orderBy) : ''); | |
} | |
function compactRevs(revs, docId, tx) { | |
if (!revs.length) { | |
return; | |
} | |
var numDone = 0; | |
var seqs = []; | |
function checkDone() { | |
if (++numDone === revs.length) { // done | |
deleteOrphans(); | |
} | |
} | |
function deleteOrphans() { | |
// find orphaned attachment digests | |
if (!seqs.length) { | |
return; | |
} | |
var sql = 'SELECT DISTINCT digest AS digest FROM ' + | |
ATTACH_AND_SEQ_STORE + ' WHERE seq IN ' + qMarks(seqs.length); | |
tx.executeSql(sql, seqs, function (tx, res) { | |
var digestsToCheck = []; | |
for (var i = 0; i < res.rows.length; i++) { | |
digestsToCheck.push(res.rows.item(i).digest); | |
} | |
if (!digestsToCheck.length) { | |
return; | |
} | |
var sql = 'DELETE FROM ' + ATTACH_AND_SEQ_STORE + | |
' WHERE seq IN (' + | |
seqs.map(function () { return '?'; }).join(',') + | |
')'; | |
tx.executeSql(sql, seqs, function (tx) { | |
var sql = 'SELECT digest FROM ' + ATTACH_AND_SEQ_STORE + | |
' WHERE digest IN (' + | |
digestsToCheck.map(function () { return '?'; }).join(',') + | |
')'; | |
tx.executeSql(sql, digestsToCheck, function (tx, res) { | |
var nonOrphanedDigests = new utils.Set(); | |
for (var i = 0; i < res.rows.length; i++) { | |
nonOrphanedDigests.add(res.rows.item(i).digest); | |
} | |
digestsToCheck.forEach(function (digest) { | |
if (nonOrphanedDigests.has(digest)) { | |
return; | |
} | |
tx.executeSql( | |
'DELETE FROM ' + ATTACH_AND_SEQ_STORE + ' WHERE digest=?', | |
[digest]); | |
tx.executeSql( | |
'DELETE FROM ' + ATTACH_STORE + ' WHERE digest=?', [digest]); | |
}); | |
}); | |
}); | |
}); | |
} | |
// update by-seq and attach stores in parallel | |
revs.forEach(function (rev) { | |
var sql = 'SELECT seq FROM ' + BY_SEQ_STORE + | |
' WHERE doc_id=? AND rev=?'; | |
tx.executeSql(sql, [docId, rev], function (tx, res) { | |
if (!res.rows.length) { // already deleted | |
return checkDone(); | |
} | |
var seq = res.rows.item(0).seq; | |
seqs.push(seq); | |
tx.executeSql( | |
'DELETE FROM ' + BY_SEQ_STORE + ' WHERE seq=?', [seq], checkDone); | |
}); | |
}); | |
} | |
function unknownError(callback) { | |
return function (event) { | |
// event may actually be a SQLError object, so report is as such | |
var errorNameMatch = event && event.constructor.toString() | |
.match(/function ([^\(]+)/); | |
var errorName = (errorNameMatch && errorNameMatch[1]) || event.type; | |
var errorReason = event.target || event.message; | |
callback(errors.error(errors.WSQ_ERROR, errorReason, errorName)); | |
}; | |
} | |
function getSize(opts) { | |
if ('size' in opts) { | |
// triggers immediate popup in iOS, fixes #2347 | |
// e.g. 5000001 asks for 5 MB, 10000001 asks for 10 MB, | |
return opts.size * 1000000; | |
} | |
// In iOS, doesn't matter as long as it's <= 5000000. | |
// Except that if you request too much, our tests fail | |
// because of the native "do you accept?" popup. | |
// In Android <=4.3, this value is actually used as an | |
// honest-to-god ceiling for data, so we need to | |
// set it to a decently high number. | |
var isAndroid = /Android/.test(window.navigator.userAgent); | |
return isAndroid ? 5000000 : 1; // in PhantomJS, if you use 0 it will crash | |
} | |
function createOpenDBFunction() { | |
if (typeof sqlitePlugin !== 'undefined') { | |
// The SQLite Plugin started deviating pretty heavily from the | |
// standard openDatabase() function, as they started adding more features. | |
// It's better to just use their "new" format and pass in a big ol' | |
// options object. | |
return sqlitePlugin.openDatabase.bind(sqlitePlugin); | |
} | |
if (typeof openDatabase !== 'undefined') { | |
return function openDB(opts) { | |
// Traditional WebSQL API | |
return openDatabase(opts.name, opts.version, opts.description, opts.size); | |
}; | |
} | |
} | |
var cachedDatabases = {}; | |
function openDB(opts) { | |
var openDBFunction = createOpenDBFunction(); | |
var db = cachedDatabases[opts.name]; | |
if (!db) { | |
db = cachedDatabases[opts.name] = openDBFunction(opts); | |
db._sqlitePlugin = typeof sqlitePlugin !== 'undefined'; | |
} | |
return db; | |
} | |
function valid() { | |
// SQLitePlugin leaks this global object, which we can use | |
// to detect if it's installed or not. The benefit is that it's | |
// declared immediately, before the 'deviceready' event has fired. | |
return typeof openDatabase !== 'undefined' || | |
typeof SQLitePlugin !== 'undefined'; | |
} | |
module.exports = { | |
escapeBlob: escapeBlob, | |
unescapeBlob: unescapeBlob, | |
stringifyDoc: stringifyDoc, | |
unstringifyDoc: unstringifyDoc, | |
qMarks: qMarks, | |
select: select, | |
compactRevs: compactRevs, | |
unknownError: unknownError, | |
getSize: getSize, | |
openDB: openDB, | |
valid: valid | |
}; | |
},{"../../deps/errors":120,"../../utils":138,"./websql-constants":111}],113:[function(require,module,exports){ | |
'use strict'; | |
var utils = require('../../utils'); | |
var merge = require('../../merge'); | |
var errors = require('../../deps/errors'); | |
var parseHexString = require('../../deps/parse-hex'); | |
var websqlConstants = require('./websql-constants'); | |
var websqlUtils = require('./websql-utils'); | |
var websqlBulkDocs = require('./websql-bulk-docs'); | |
var ADAPTER_VERSION = websqlConstants.ADAPTER_VERSION; | |
var DOC_STORE = websqlConstants.DOC_STORE; | |
var BY_SEQ_STORE = websqlConstants.BY_SEQ_STORE; | |
var ATTACH_STORE = websqlConstants.ATTACH_STORE; | |
var LOCAL_STORE = websqlConstants.LOCAL_STORE; | |
var META_STORE = websqlConstants.META_STORE; | |
var ATTACH_AND_SEQ_STORE = websqlConstants.ATTACH_AND_SEQ_STORE; | |
var qMarks = websqlUtils.qMarks; | |
var stringifyDoc = websqlUtils.stringifyDoc; | |
var unstringifyDoc = websqlUtils.unstringifyDoc; | |
var select = websqlUtils.select; | |
var compactRevs = websqlUtils.compactRevs; | |
var unknownError = websqlUtils.unknownError; | |
var getSize = websqlUtils.getSize; | |
var openDB = websqlUtils.openDB; | |
function fetchAttachmentsIfNecessary(doc, opts, api, txn, cb) { | |
var attachments = Object.keys(doc._attachments || {}); | |
if (!attachments.length) { | |
return cb && cb(); | |
} | |
var numDone = 0; | |
function checkDone() { | |
if (++numDone === attachments.length && cb) { | |
cb(); | |
} | |
} | |
function fetchAttachment(doc, att) { | |
var attObj = doc._attachments[att]; | |
var attOpts = {encode: true, ctx: txn}; | |
api._getAttachment(attObj, attOpts, function (_, base64) { | |
doc._attachments[att] = utils.extend( | |
utils.pick(attObj, ['digest', 'content_type']), | |
{ data: base64 } | |
); | |
checkDone(); | |
}); | |
} | |
attachments.forEach(function (att) { | |
if (opts.attachments && opts.include_docs) { | |
fetchAttachment(doc, att); | |
} else { | |
doc._attachments[att].stub = true; | |
checkDone(); | |
} | |
}); | |
} | |
var POUCH_VERSION = 1; | |
// these indexes cover the ground for most allDocs queries | |
var BY_SEQ_STORE_DELETED_INDEX_SQL = | |
'CREATE INDEX IF NOT EXISTS \'by-seq-deleted-idx\' ON ' + | |
BY_SEQ_STORE + ' (seq, deleted)'; | |
var BY_SEQ_STORE_DOC_ID_REV_INDEX_SQL = | |
'CREATE UNIQUE INDEX IF NOT EXISTS \'by-seq-doc-id-rev\' ON ' + | |
BY_SEQ_STORE + ' (doc_id, rev)'; | |
var DOC_STORE_WINNINGSEQ_INDEX_SQL = | |
'CREATE INDEX IF NOT EXISTS \'doc-winningseq-idx\' ON ' + | |
DOC_STORE + ' (winningseq)'; | |
var ATTACH_AND_SEQ_STORE_SEQ_INDEX_SQL = | |
'CREATE INDEX IF NOT EXISTS \'attach-seq-seq-idx\' ON ' + | |
ATTACH_AND_SEQ_STORE + ' (seq)'; | |
var ATTACH_AND_SEQ_STORE_ATTACH_INDEX_SQL = | |
'CREATE UNIQUE INDEX IF NOT EXISTS \'attach-seq-digest-idx\' ON ' + | |
ATTACH_AND_SEQ_STORE + ' (digest, seq)'; | |
var DOC_STORE_AND_BY_SEQ_JOINER = BY_SEQ_STORE + | |
'.seq = ' + DOC_STORE + '.winningseq'; | |
var SELECT_DOCS = BY_SEQ_STORE + '.seq AS seq, ' + | |
BY_SEQ_STORE + '.deleted AS deleted, ' + | |
BY_SEQ_STORE + '.json AS data, ' + | |
BY_SEQ_STORE + '.rev AS rev, ' + | |
DOC_STORE + '.json AS metadata'; | |
function WebSqlPouch(opts, callback) { | |
var api = this; | |
var instanceId = null; | |
var size = getSize(opts); | |
var idRequests = []; | |
var encoding; | |
api._docCount = -1; // cache sqlite count(*) for performance | |
api._name = opts.name; | |
var db = openDB({ | |
name: api._name, | |
version: POUCH_VERSION, | |
description: api._name, | |
size: size, | |
location: opts.location, | |
createFromLocation: opts.createFromLocation | |
}); | |
if (!db) { | |
return callback(errors.error(errors.UNKNOWN_ERROR)); | |
} else if (typeof db.readTransaction !== 'function') { | |
// doesn't exist in sqlite plugin | |
db.readTransaction = db.transaction; | |
} | |
function dbCreated() { | |
// note the db name in case the browser upgrades to idb | |
if (utils.hasLocalStorage()) { | |
window.localStorage['_pouch__websqldb_' + api._name] = true; | |
} | |
callback(null, api); | |
} | |
// In this migration, we added the 'deleted' and 'local' columns to the | |
// by-seq and doc store tables. | |
// To preserve existing user data, we re-process all the existing JSON | |
// and add these values. | |
// Called migration2 because it corresponds to adapter version (db_version) #2 | |
function runMigration2(tx, callback) { | |
// index used for the join in the allDocs query | |
tx.executeSql(DOC_STORE_WINNINGSEQ_INDEX_SQL); | |
tx.executeSql('ALTER TABLE ' + BY_SEQ_STORE + | |
' ADD COLUMN deleted TINYINT(1) DEFAULT 0', [], function () { | |
tx.executeSql(BY_SEQ_STORE_DELETED_INDEX_SQL); | |
tx.executeSql('ALTER TABLE ' + DOC_STORE + | |
' ADD COLUMN local TINYINT(1) DEFAULT 0', [], function () { | |
tx.executeSql('CREATE INDEX IF NOT EXISTS \'doc-store-local-idx\' ON ' + | |
DOC_STORE + ' (local, id)'); | |
var sql = 'SELECT ' + DOC_STORE + '.winningseq AS seq, ' + DOC_STORE + | |
'.json AS metadata FROM ' + BY_SEQ_STORE + ' JOIN ' + DOC_STORE + | |
' ON ' + BY_SEQ_STORE + '.seq = ' + DOC_STORE + '.winningseq'; | |
tx.executeSql(sql, [], function (tx, result) { | |
var deleted = []; | |
var local = []; | |
for (var i = 0; i < result.rows.length; i++) { | |
var item = result.rows.item(i); | |
var seq = item.seq; | |
var metadata = JSON.parse(item.metadata); | |
if (utils.isDeleted(metadata)) { | |
deleted.push(seq); | |
} | |
if (utils.isLocalId(metadata.id)) { | |
local.push(metadata.id); | |
} | |
} | |
tx.executeSql('UPDATE ' + DOC_STORE + 'SET local = 1 WHERE id IN ' + | |
qMarks(local.length), local, function () { | |
tx.executeSql('UPDATE ' + BY_SEQ_STORE + | |
' SET deleted = 1 WHERE seq IN ' + | |
qMarks(deleted.length), deleted, callback); | |
}); | |
}); | |
}); | |
}); | |
} | |
// in this migration, we make all the local docs unversioned | |
function runMigration3(tx, callback) { | |
var local = 'CREATE TABLE IF NOT EXISTS ' + LOCAL_STORE + | |
' (id UNIQUE, rev, json)'; | |
tx.executeSql(local, [], function () { | |
var sql = 'SELECT ' + DOC_STORE + '.id AS id, ' + | |
BY_SEQ_STORE + '.json AS data ' + | |
'FROM ' + BY_SEQ_STORE + ' JOIN ' + | |
DOC_STORE + ' ON ' + BY_SEQ_STORE + '.seq = ' + | |
DOC_STORE + '.winningseq WHERE local = 1'; | |
tx.executeSql(sql, [], function (tx, res) { | |
var rows = []; | |
for (var i = 0; i < res.rows.length; i++) { | |
rows.push(res.rows.item(i)); | |
} | |
function doNext() { | |
if (!rows.length) { | |
return callback(tx); | |
} | |
var row = rows.shift(); | |
var rev = JSON.parse(row.data)._rev; | |
tx.executeSql('INSERT INTO ' + LOCAL_STORE + | |
' (id, rev, json) VALUES (?,?,?)', | |
[row.id, rev, row.data], function (tx) { | |
tx.executeSql('DELETE FROM ' + DOC_STORE + ' WHERE id=?', | |
[row.id], function (tx) { | |
tx.executeSql('DELETE FROM ' + BY_SEQ_STORE + ' WHERE seq=?', | |
[row.seq], function () { | |
doNext(); | |
}); | |
}); | |
}); | |
} | |
doNext(); | |
}); | |
}); | |
} | |
// in this migration, we remove doc_id_rev and just use rev | |
function runMigration4(tx, callback) { | |
function updateRows(rows) { | |
function doNext() { | |
if (!rows.length) { | |
return callback(tx); | |
} | |
var row = rows.shift(); | |
var doc_id_rev = parseHexString(row.hex, encoding); | |
var idx = doc_id_rev.lastIndexOf('::'); | |
var doc_id = doc_id_rev.substring(0, idx); | |
var rev = doc_id_rev.substring(idx + 2); | |
var sql = 'UPDATE ' + BY_SEQ_STORE + | |
' SET doc_id=?, rev=? WHERE doc_id_rev=?'; | |
tx.executeSql(sql, [doc_id, rev, doc_id_rev], function () { | |
doNext(); | |
}); | |
} | |
doNext(); | |
} | |
var sql = 'ALTER TABLE ' + BY_SEQ_STORE + ' ADD COLUMN doc_id'; | |
tx.executeSql(sql, [], function (tx) { | |
var sql = 'ALTER TABLE ' + BY_SEQ_STORE + ' ADD COLUMN rev'; | |
tx.executeSql(sql, [], function (tx) { | |
tx.executeSql(BY_SEQ_STORE_DOC_ID_REV_INDEX_SQL, [], function (tx) { | |
var sql = 'SELECT hex(doc_id_rev) as hex FROM ' + BY_SEQ_STORE; | |
tx.executeSql(sql, [], function (tx, res) { | |
var rows = []; | |
for (var i = 0; i < res.rows.length; i++) { | |
rows.push(res.rows.item(i)); | |
} | |
updateRows(rows); | |
}); | |
}); | |
}); | |
}); | |
} | |
// in this migration, we add the attach_and_seq table | |
// for issue #2818 | |
function runMigration5(tx, callback) { | |
function migrateAttsAndSeqs(tx) { | |
// need to actually populate the table. this is the expensive part, | |
// so as an optimization, check first that this database even | |
// contains attachments | |
var sql = 'SELECT COUNT(*) AS cnt FROM ' + ATTACH_STORE; | |
tx.executeSql(sql, [], function (tx, res) { | |
var count = res.rows.item(0).cnt; | |
if (!count) { | |
return callback(tx); | |
} | |
var offset = 0; | |
var pageSize = 10; | |
function nextPage() { | |
var sql = select( | |
SELECT_DOCS + ', ' + DOC_STORE + '.id AS id', | |
[DOC_STORE, BY_SEQ_STORE], | |
DOC_STORE_AND_BY_SEQ_JOINER, | |
null, | |
DOC_STORE + '.id ' | |
); | |
sql += ' LIMIT ' + pageSize + ' OFFSET ' + offset; | |
offset += pageSize; | |
tx.executeSql(sql, [], function (tx, res) { | |
if (!res.rows.length) { | |
return callback(tx); | |
} | |
var digestSeqs = {}; | |
function addDigestSeq(digest, seq) { | |
// uniq digest/seq pairs, just in case there are dups | |
var seqs = digestSeqs[digest] = (digestSeqs[digest] || []); | |
if (seqs.indexOf(seq) === -1) { | |
seqs.push(seq); | |
} | |
} | |
for (var i = 0; i < res.rows.length; i++) { | |
var row = res.rows.item(i); | |
var doc = unstringifyDoc(row.data, row.id, row.rev); | |
var atts = Object.keys(doc._attachments || {}); | |
for (var j = 0; j < atts.length; j++) { | |
var att = doc._attachments[atts[j]]; | |
addDigestSeq(att.digest, row.seq); | |
} | |
} | |
var digestSeqPairs = []; | |
Object.keys(digestSeqs).forEach(function (digest) { | |
var seqs = digestSeqs[digest]; | |
seqs.forEach(function (seq) { | |
digestSeqPairs.push([digest, seq]); | |
}); | |
}); | |
if (!digestSeqPairs.length) { | |
return nextPage(); | |
} | |
var numDone = 0; | |
digestSeqPairs.forEach(function (pair) { | |
var sql = 'INSERT INTO ' + ATTACH_AND_SEQ_STORE + | |
' (digest, seq) VALUES (?,?)'; | |
tx.executeSql(sql, pair, function () { | |
if (++numDone === digestSeqPairs.length) { | |
nextPage(); | |
} | |
}); | |
}); | |
}); | |
} | |
nextPage(); | |
}); | |
} | |
var attachAndRev = 'CREATE TABLE IF NOT EXISTS ' + | |
ATTACH_AND_SEQ_STORE + ' (digest, seq INTEGER)'; | |
tx.executeSql(attachAndRev, [], function (tx) { | |
tx.executeSql( | |
ATTACH_AND_SEQ_STORE_ATTACH_INDEX_SQL, [], function (tx) { | |
tx.executeSql( | |
ATTACH_AND_SEQ_STORE_SEQ_INDEX_SQL, [], | |
migrateAttsAndSeqs); | |
}); | |
}); | |
} | |
// in this migration, we use escapeBlob() and unescapeBlob() | |
// instead of reading out the binary as HEX, which is slow | |
function runMigration6(tx, callback) { | |
var sql = 'ALTER TABLE ' + ATTACH_STORE + | |
' ADD COLUMN escaped TINYINT(1) DEFAULT 0'; | |
tx.executeSql(sql, [], callback); | |
} | |
// issue #3136, in this migration we need a "latest seq" as well | |
// as the "winning seq" in the doc store | |
function runMigration7(tx, callback) { | |
var sql = 'ALTER TABLE ' + DOC_STORE + | |
' ADD COLUMN max_seq INTEGER'; | |
tx.executeSql(sql, [], function (tx) { | |
var sql = 'UPDATE ' + DOC_STORE + ' SET max_seq=(SELECT MAX(seq) FROM ' + | |
BY_SEQ_STORE + ' WHERE doc_id=id)'; | |
tx.executeSql(sql, [], function (tx) { | |
// add unique index after filling, else we'll get a constraint | |
// error when we do the ALTER TABLE | |
var sql = | |
'CREATE UNIQUE INDEX IF NOT EXISTS \'doc-max-seq-idx\' ON ' + | |
DOC_STORE + ' (max_seq)'; | |
tx.executeSql(sql, [], callback); | |
}); | |
}); | |
} | |
function checkEncoding(tx, cb) { | |
// UTF-8 on chrome/android, UTF-16 on safari < 7.1 | |
tx.executeSql('SELECT HEX("a") AS hex', [], function (tx, res) { | |
var hex = res.rows.item(0).hex; | |
encoding = hex.length === 2 ? 'UTF-8' : 'UTF-16'; | |
cb(); | |
} | |
); | |
} | |
function onGetInstanceId() { | |
while (idRequests.length > 0) { | |
var idCallback = idRequests.pop(); | |
idCallback(null, instanceId); | |
} | |
} | |
function onGetVersion(tx, dbVersion) { | |
if (dbVersion === 0) { | |
// initial schema | |
var meta = 'CREATE TABLE IF NOT EXISTS ' + META_STORE + | |
' (dbid, db_version INTEGER)'; | |
var attach = 'CREATE TABLE IF NOT EXISTS ' + ATTACH_STORE + | |
' (digest UNIQUE, escaped TINYINT(1), body BLOB)'; | |
var attachAndRev = 'CREATE TABLE IF NOT EXISTS ' + | |
ATTACH_AND_SEQ_STORE + ' (digest, seq INTEGER)'; | |
// TODO: migrate winningseq to INTEGER | |
var doc = 'CREATE TABLE IF NOT EXISTS ' + DOC_STORE + | |
' (id unique, json, winningseq, max_seq INTEGER UNIQUE)'; | |
var seq = 'CREATE TABLE IF NOT EXISTS ' + BY_SEQ_STORE + | |
' (seq INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, ' + | |
'json, deleted TINYINT(1), doc_id, rev)'; | |
var local = 'CREATE TABLE IF NOT EXISTS ' + LOCAL_STORE + | |
' (id UNIQUE, rev, json)'; | |
// creates | |
tx.executeSql(attach); | |
tx.executeSql(local); | |
tx.executeSql(attachAndRev, [], function () { | |
tx.executeSql(ATTACH_AND_SEQ_STORE_SEQ_INDEX_SQL); | |
tx.executeSql(ATTACH_AND_SEQ_STORE_ATTACH_INDEX_SQL); | |
}); | |
tx.executeSql(doc, [], function () { | |
tx.executeSql(DOC_STORE_WINNINGSEQ_INDEX_SQL); | |
tx.executeSql(seq, [], function () { | |
tx.executeSql(BY_SEQ_STORE_DELETED_INDEX_SQL); | |
tx.executeSql(BY_SEQ_STORE_DOC_ID_REV_INDEX_SQL); | |
tx.executeSql(meta, [], function () { | |
// mark the db version, and new dbid | |
var initSeq = 'INSERT INTO ' + META_STORE + | |
' (db_version, dbid) VALUES (?,?)'; | |
instanceId = utils.uuid(); | |
var initSeqArgs = [ADAPTER_VERSION, instanceId]; | |
tx.executeSql(initSeq, initSeqArgs, function () { | |
onGetInstanceId(); | |
}); | |
}); | |
}); | |
}); | |
} else { // version > 0 | |
var setupDone = function () { | |
var migrated = dbVersion < ADAPTER_VERSION; | |
if (migrated) { | |
// update the db version within this transaction | |
tx.executeSql('UPDATE ' + META_STORE + ' SET db_version = ' + | |
ADAPTER_VERSION); | |
} | |
// notify db.id() callers | |
var sql = 'SELECT dbid FROM ' + META_STORE; | |
tx.executeSql(sql, [], function (tx, result) { | |
instanceId = result.rows.item(0).dbid; | |
onGetInstanceId(); | |
}); | |
}; | |
// would love to use promises here, but then websql | |
// ends the transaction early | |
var tasks = [ | |
runMigration2, | |
runMigration3, | |
runMigration4, | |
runMigration5, | |
runMigration6, | |
runMigration7, | |
setupDone | |
]; | |
// run each migration sequentially | |
var i = dbVersion; | |
var nextMigration = function (tx) { | |
tasks[i - 1](tx, nextMigration); | |
i++; | |
}; | |
nextMigration(tx); | |
} | |
} | |
function setup() { | |
db.transaction(function (tx) { | |
// first check the encoding | |
checkEncoding(tx, function () { | |
// then get the version | |
fetchVersion(tx); | |
}); | |
}, unknownError(callback), dbCreated); | |
} | |
function fetchVersion(tx) { | |
var sql = 'SELECT sql FROM sqlite_master WHERE tbl_name = ' + META_STORE; | |
tx.executeSql(sql, [], function (tx, result) { | |
if (!result.rows.length) { | |
// database hasn't even been created yet (version 0) | |
onGetVersion(tx, 0); | |
} else if (!/db_version/.test(result.rows.item(0).sql)) { | |
// table was created, but without the new db_version column, | |
// so add it. | |
tx.executeSql('ALTER TABLE ' + META_STORE + | |
' ADD COLUMN db_version INTEGER', [], function () { | |
// before version 2, this column didn't even exist | |
onGetVersion(tx, 1); | |
}); | |
} else { // column exists, we can safely get it | |
tx.executeSql('SELECT db_version FROM ' + META_STORE, | |
[], function (tx, result) { | |
var dbVersion = result.rows.item(0).db_version; | |
onGetVersion(tx, dbVersion); | |
}); | |
} | |
}); | |
} | |
if (utils.isCordova()) { | |
//to wait until custom api is made in pouch.adapters before doing setup | |
window.addEventListener(api._name + '_pouch', function cordova_init() { | |
window.removeEventListener(api._name + '_pouch', cordova_init, false); | |
setup(); | |
}, false); | |
} else { | |
setup(); | |
} | |
api.type = function () { | |
return 'websql'; | |
}; | |
api._id = utils.toPromise(function (callback) { | |
callback(null, instanceId); | |
}); | |
api._info = function (callback) { | |
db.readTransaction(function (tx) { | |
countDocs(tx, function (docCount) { | |
var sql = 'SELECT MAX(seq) AS seq FROM ' + BY_SEQ_STORE; | |
tx.executeSql(sql, [], function (tx, res) { | |
var updateSeq = res.rows.item(0).seq || 0; | |
callback(null, { | |
doc_count: docCount, | |
update_seq: updateSeq, | |
// for debugging | |
sqlite_plugin: db._sqlitePlugin, | |
websql_encoding: encoding | |
}); | |
}); | |
}); | |
}, unknownError(callback)); | |
}; | |
api._bulkDocs = function (req, opts, callback) { | |
websqlBulkDocs(req, opts, api, db, WebSqlPouch.Changes, callback); | |
}; | |
api._get = function (id, opts, callback) { | |
opts = utils.clone(opts); | |
var doc; | |
var metadata; | |
var err; | |
if (!opts.ctx) { | |
db.readTransaction(function (txn) { | |
opts.ctx = txn; | |
api._get(id, opts, callback); | |
}); | |
return; | |
} | |
var tx = opts.ctx; | |
function finish() { | |
callback(err, {doc: doc, metadata: metadata, ctx: tx}); | |
} | |
var sql; | |
var sqlArgs; | |
if (opts.rev) { | |
sql = select( | |
SELECT_DOCS, | |
[DOC_STORE, BY_SEQ_STORE], | |
DOC_STORE + '.id=' + BY_SEQ_STORE + '.doc_id', | |
[BY_SEQ_STORE + '.doc_id=?', BY_SEQ_STORE + '.rev=?']); | |
sqlArgs = [id, opts.rev]; | |
} else { | |
sql = select( | |
SELECT_DOCS, | |
[DOC_STORE, BY_SEQ_STORE], | |
DOC_STORE_AND_BY_SEQ_JOINER, | |
DOC_STORE + '.id=?'); | |
sqlArgs = [id]; | |
} | |
tx.executeSql(sql, sqlArgs, function (a, results) { | |
if (!results.rows.length) { | |
err = errors.error(errors.MISSING_DOC, 'missing'); | |
return finish(); | |
} | |
var item = results.rows.item(0); | |
metadata = utils.safeJsonParse(item.metadata); | |
if (item.deleted && !opts.rev) { | |
err = errors.error(errors.MISSING_DOC, 'deleted'); | |
return finish(); | |
} | |
doc = unstringifyDoc(item.data, metadata.id, item.rev); | |
finish(); | |
}); | |
}; | |
function countDocs(tx, callback) { | |
if (api._docCount !== -1) { | |
return callback(api._docCount); | |
} | |
// count the total rows | |
var sql = select( | |
'COUNT(' + DOC_STORE + '.id) AS \'num\'', | |
[DOC_STORE, BY_SEQ_STORE], | |
DOC_STORE_AND_BY_SEQ_JOINER, | |
BY_SEQ_STORE + '.deleted=0'); | |
tx.executeSql(sql, [], function (tx, result) { | |
api._docCount = result.rows.item(0).num; | |
callback(api._docCount); | |
}); | |
} | |
api._allDocs = function (opts, callback) { | |
var results = []; | |
var totalRows; | |
var start = 'startkey' in opts ? opts.startkey : false; | |
var end = 'endkey' in opts ? opts.endkey : false; | |
var key = 'key' in opts ? opts.key : false; | |
var descending = 'descending' in opts ? opts.descending : false; | |
var limit = 'limit' in opts ? opts.limit : -1; | |
var offset = 'skip' in opts ? opts.skip : 0; | |
var inclusiveEnd = opts.inclusive_end !== false; | |
var sqlArgs = []; | |
var criteria = []; | |
if (key !== false) { | |
criteria.push(DOC_STORE + '.id = ?'); | |
sqlArgs.push(key); | |
} else if (start !== false || end !== false) { | |
if (start !== false) { | |
criteria.push(DOC_STORE + '.id ' + (descending ? '<=' : '>=') + ' ?'); | |
sqlArgs.push(start); | |
} | |
if (end !== false) { | |
var comparator = descending ? '>' : '<'; | |
if (inclusiveEnd) { | |
comparator += '='; | |
} | |
criteria.push(DOC_STORE + '.id ' + comparator + ' ?'); | |
sqlArgs.push(end); | |
} | |
if (key !== false) { | |
criteria.push(DOC_STORE + '.id = ?'); | |
sqlArgs.push(key); | |
} | |
} | |
if (opts.deleted !== 'ok') { | |
// report deleted if keys are specified | |
criteria.push(BY_SEQ_STORE + '.deleted = 0'); | |
} | |
db.readTransaction(function (tx) { | |
// first count up the total rows | |
countDocs(tx, function (count) { | |
totalRows = count; | |
if (limit === 0) { | |
return; | |
} | |
// then actually fetch the documents | |
var sql = select( | |
SELECT_DOCS, | |
[DOC_STORE, BY_SEQ_STORE], | |
DOC_STORE_AND_BY_SEQ_JOINER, | |
criteria, | |
DOC_STORE + '.id ' + (descending ? 'DESC' : 'ASC') | |
); | |
sql += ' LIMIT ' + limit + ' OFFSET ' + offset; | |
tx.executeSql(sql, sqlArgs, function (tx, result) { | |
for (var i = 0, l = result.rows.length; i < l; i++) { | |
var item = result.rows.item(i); | |
var metadata = utils.safeJsonParse(item.metadata); | |
var id = metadata.id; | |
var data = unstringifyDoc(item.data, id, item.rev); | |
var winningRev = data._rev; | |
var doc = { | |
id: id, | |
key: id, | |
value: {rev: winningRev} | |
}; | |
if (opts.include_docs) { | |
doc.doc = data; | |
doc.doc._rev = winningRev; | |
if (opts.conflicts) { | |
doc.doc._conflicts = merge.collectConflicts(metadata); | |
} | |
fetchAttachmentsIfNecessary(doc.doc, opts, api, tx); | |
} | |
if (item.deleted) { | |
if (opts.deleted === 'ok') { | |
doc.value.deleted = true; | |
doc.doc = null; | |
} else { | |
continue; | |
} | |
} | |
results.push(doc); | |
} | |
}); | |
}); | |
}, unknownError(callback), function () { | |
callback(null, { | |
total_rows: totalRows, | |
offset: opts.skip, | |
rows: results | |
}); | |
}); | |
}; | |
api._changes = function (opts) { | |
opts = utils.clone(opts); | |
if (opts.continuous) { | |
var id = api._name + ':' + utils.uuid(); | |
WebSqlPouch.Changes.addListener(api._name, id, api, opts); | |
WebSqlPouch.Changes.notify(api._name); | |
return { | |
cancel: function () { | |
WebSqlPouch.Changes.removeListener(api._name, id); | |
} | |
}; | |
} | |
var descending = opts.descending; | |
// Ignore the `since` parameter when `descending` is true | |
opts.since = opts.since && !descending ? opts.since : 0; | |
var limit = 'limit' in opts ? opts.limit : -1; | |
if (limit === 0) { | |
limit = 1; // per CouchDB _changes spec | |
} | |
var returnDocs; | |
if ('returnDocs' in opts) { | |
returnDocs = opts.returnDocs; | |
} else { | |
returnDocs = true; | |
} | |
var results = []; | |
var numResults = 0; | |
function fetchChanges() { | |
var selectStmt = | |
DOC_STORE + '.json AS metadata, ' + | |
DOC_STORE + '.max_seq AS maxSeq, ' + | |
BY_SEQ_STORE + '.json AS winningDoc, ' + | |
BY_SEQ_STORE + '.rev AS winningRev '; | |
var from = DOC_STORE + ' JOIN ' + BY_SEQ_STORE; | |
var joiner = DOC_STORE + '.id=' + BY_SEQ_STORE + '.doc_id' + | |
' AND ' + DOC_STORE + '.winningseq=' + BY_SEQ_STORE + '.seq'; | |
var criteria = ['maxSeq > ?']; | |
var sqlArgs = [opts.since]; | |
if (opts.doc_ids) { | |
criteria.push(DOC_STORE + '.id IN ' + qMarks(opts.doc_ids.length)); | |
sqlArgs = sqlArgs.concat(opts.doc_ids); | |
} | |
var orderBy = 'maxSeq ' + (descending ? 'DESC' : 'ASC'); | |
var sql = select(selectStmt, from, joiner, criteria, orderBy); | |
var filter = utils.filterChange(opts); | |
if (!opts.view && !opts.filter) { | |
// we can just limit in the query | |
sql += ' LIMIT ' + limit; | |
} | |
var lastSeq = opts.since || 0; | |
db.readTransaction(function (tx) { | |
tx.executeSql(sql, sqlArgs, function (tx, result) { | |
function reportChange(change) { | |
return function () { | |
opts.onChange(change); | |
}; | |
} | |
for (var i = 0, l = result.rows.length; i < l; i++) { | |
var item = result.rows.item(i); | |
var metadata = utils.safeJsonParse(item.metadata); | |
lastSeq = item.maxSeq; | |
var doc = unstringifyDoc(item.winningDoc, metadata.id, | |
item.winningRev); | |
var change = opts.processChange(doc, metadata, opts); | |
change.seq = item.maxSeq; | |
if (filter(change)) { | |
numResults++; | |
if (returnDocs) { | |
results.push(change); | |
} | |
// process the attachment immediately | |
// for the benefit of live listeners | |
if (opts.attachments && opts.include_docs) { | |
fetchAttachmentsIfNecessary(doc, opts, api, tx, | |
reportChange(change)); | |
} else { | |
reportChange(change)(); | |
} | |
} | |
if (numResults === limit) { | |
break; | |
} | |
} | |
}); | |
}, unknownError(opts.complete), function () { | |
if (!opts.continuous) { | |
opts.complete(null, { | |
results: results, | |
last_seq: lastSeq | |
}); | |
} | |
}); | |
} | |
fetchChanges(); | |
}; | |
api._close = function (callback) { | |
//WebSQL databases do not need to be closed | |
callback(); | |
}; | |
api._getAttachment = function (attachment, opts, callback) { | |
var res; | |
var tx = opts.ctx; | |
var digest = attachment.digest; | |
var type = attachment.content_type; | |
var sql = 'SELECT escaped, ' + | |
'CASE WHEN escaped = 1 THEN body ELSE HEX(body) END AS body FROM ' + | |
ATTACH_STORE + ' WHERE digest=?'; | |
tx.executeSql(sql, [digest], function (tx, result) { | |
// websql has a bug where \u0000 causes early truncation in strings | |
// and blobs. to work around this, we used to use the hex() function, | |
// but that's not performant. after migration 6, we remove \u0000 | |
// and add it back in afterwards | |
var item = result.rows.item(0); | |
var data = item.escaped ? websqlUtils.unescapeBlob(item.body) : | |
parseHexString(item.body, encoding); | |
if (opts.encode) { | |
res = btoa(data); | |
} else { | |
data = utils.fixBinary(data); | |
res = utils.createBlob([data], {type: type}); | |
} | |
callback(null, res); | |
}); | |
}; | |
api._getRevisionTree = function (docId, callback) { | |
db.readTransaction(function (tx) { | |
var sql = 'SELECT json AS metadata FROM ' + DOC_STORE + ' WHERE id = ?'; | |
tx.executeSql(sql, [docId], function (tx, result) { | |
if (!result.rows.length) { | |
callback(errors.error(errors.MISSING_DOC)); | |
} else { | |
var data = utils.safeJsonParse(result.rows.item(0).metadata); | |
callback(null, data.rev_tree); | |
} | |
}); | |
}); | |
}; | |
api._doCompaction = function (docId, revs, callback) { | |
if (!revs.length) { | |
return callback(); | |
} | |
db.transaction(function (tx) { | |
// update doc store | |
var sql = 'SELECT json AS metadata FROM ' + DOC_STORE + ' WHERE id = ?'; | |
tx.executeSql(sql, [docId], function (tx, result) { | |
var metadata = utils.safeJsonParse(result.rows.item(0).metadata); | |
merge.traverseRevTree(metadata.rev_tree, function (isLeaf, pos, | |
revHash, ctx, opts) { | |
var rev = pos + '-' + revHash; | |
if (revs.indexOf(rev) !== -1) { | |
opts.status = 'missing'; | |
} | |
}); | |
var sql = 'UPDATE ' + DOC_STORE + ' SET json = ? WHERE id = ?'; | |
tx.executeSql(sql, [utils.safeJsonStringify(metadata), docId]); | |
}); | |
compactRevs(revs, docId, tx); | |
}, unknownError(callback), function () { | |
callback(); | |
}); | |
}; | |
api._getLocal = function (id, callback) { | |
db.readTransaction(function (tx) { | |
var sql = 'SELECT json, rev FROM ' + LOCAL_STORE + ' WHERE id=?'; | |
tx.executeSql(sql, [id], function (tx, res) { | |
if (res.rows.length) { | |
var item = res.rows.item(0); | |
var doc = unstringifyDoc(item.json, id, item.rev); | |
callback(null, doc); | |
} else { | |
callback(errors.error(errors.MISSING_DOC)); | |
} | |
}); | |
}); | |
}; | |
api._putLocal = function (doc, opts, callback) { | |
if (typeof opts === 'function') { | |
callback = opts; | |
opts = {}; | |
} | |
delete doc._revisions; // ignore this, trust the rev | |
var oldRev = doc._rev; | |
var id = doc._id; | |
var newRev; | |
if (!oldRev) { | |
newRev = doc._rev = '0-1'; | |
} else { | |
newRev = doc._rev = '0-' + (parseInt(oldRev.split('-')[1], 10) + 1); | |
} | |
var json = stringifyDoc(doc); | |
var ret; | |
function putLocal(tx) { | |
var sql; | |
var values; | |
if (oldRev) { | |
sql = 'UPDATE ' + LOCAL_STORE + ' SET rev=?, json=? ' + | |
'WHERE id=? AND rev=?'; | |
values = [newRev, json, id, oldRev]; | |
} else { | |
sql = 'INSERT INTO ' + LOCAL_STORE + ' (id, rev, json) VALUES (?,?,?)'; | |
values = [id, newRev, json]; | |
} | |
tx.executeSql(sql, values, function (tx, res) { | |
if (res.rowsAffected) { | |
ret = {ok: true, id: id, rev: newRev}; | |
if (opts.ctx) { // return immediately | |
callback(null, ret); | |
} | |
} else { | |
callback(errors.error(errors.REV_CONFLICT)); | |
} | |
}, function () { | |
callback(errors.error(errors.REV_CONFLICT)); | |
return false; // ack that we handled the error | |
}); | |
} | |
if (opts.ctx) { | |
putLocal(opts.ctx); | |
} else { | |
db.transaction(function (tx) { | |
putLocal(tx); | |
}, unknownError(callback), function () { | |
if (ret) { | |
callback(null, ret); | |
} | |
}); | |
} | |
}; | |
api._removeLocal = function (doc, callback) { | |
var ret; | |
db.transaction(function (tx) { | |
var sql = 'DELETE FROM ' + LOCAL_STORE + ' WHERE id=? AND rev=?'; | |
var params = [doc._id, doc._rev]; | |
tx.executeSql(sql, params, function (tx, res) { | |
if (!res.rowsAffected) { | |
return callback(errors.error(errors.MISSING_DOC)); | |
} | |
ret = {ok: true, id: doc._id, rev: '0-0'}; | |
}); | |
}, unknownError(callback), function () { | |
if (ret) { | |
callback(null, ret); | |
} | |
}); | |
}; | |
api._destroy = function (callback) { | |
WebSqlPouch.Changes.removeAllListeners(api._name); | |
db.transaction(function (tx) { | |
var stores = [DOC_STORE, BY_SEQ_STORE, ATTACH_STORE, META_STORE, | |
LOCAL_STORE, ATTACH_AND_SEQ_STORE]; | |
stores.forEach(function (store) { | |
tx.executeSql('DROP TABLE IF EXISTS ' + store, []); | |
}); | |
}, unknownError(callback), function () { | |
if (utils.hasLocalStorage()) { | |
delete window.localStorage['_pouch__websqldb_' + api._name]; | |
delete window.localStorage[api._name]; | |
} | |
callback(null, {'ok': true}); | |
}); | |
}; | |
} | |
WebSqlPouch.valid = websqlUtils.valid; | |
WebSqlPouch.Changes = new utils.Changes(); | |
module.exports = WebSqlPouch; | |
},{"../../deps/errors":120,"../../deps/parse-hex":124,"../../merge":133,"../../utils":138,"./websql-bulk-docs":110,"./websql-constants":111,"./websql-utils":112}],114:[function(require,module,exports){ | |
'use strict'; | |
var utils = require('./utils'); | |
var merge = require('./merge'); | |
var errors = require('./deps/errors'); | |
var EE = require('events').EventEmitter; | |
var evalFilter = require('./evalFilter'); | |
var evalView = require('./evalView'); | |
module.exports = Changes; | |
utils.inherits(Changes, EE); | |
function Changes(db, opts, callback) { | |
EE.call(this); | |
var self = this; | |
this.db = db; | |
opts = opts ? utils.clone(opts) : {}; | |
var oldComplete = callback || opts.complete || function () {}; | |
var complete = opts.complete = utils.once(function (err, resp) { | |
if (err) { | |
self.emit('error', err); | |
} else { | |
self.emit('complete', resp); | |
} | |
self.removeAllListeners(); | |
db.removeListener('destroyed', onDestroy); | |
}); | |
if (oldComplete) { | |
self.on('complete', function (resp) { | |
oldComplete(null, resp); | |
}); | |
self.on('error', function (err) { | |
oldComplete(err); | |
}); | |
} | |
var oldOnChange = opts.onChange; | |
if (oldOnChange) { | |
self.on('change', oldOnChange); | |
} | |
function onDestroy() { | |
self.cancel(); | |
} | |
db.once('destroyed', onDestroy); | |
opts.onChange = function (change) { | |
if (opts.isCancelled) { | |
return; | |
} | |
self.emit('change', change); | |
if (self.startSeq && self.startSeq <= change.seq) { | |
self.emit('uptodate'); | |
self.startSeq = false; | |
} | |
if (change.deleted) { | |
self.emit('delete', change); | |
} else if (change.changes.length === 1 && | |
change.changes[0].rev.slice(0, 2) === '1-') { | |
self.emit('create', change); | |
} else { | |
self.emit('update', change); | |
} | |
}; | |
var promise = new utils.Promise(function (fulfill, reject) { | |
opts.complete = function (err, res) { | |
if (err) { | |
reject(err); | |
} else { | |
fulfill(res); | |
} | |
}; | |
}); | |
self.once('cancel', function () { | |
if (oldOnChange) { | |
self.removeListener('change', oldOnChange); | |
} | |
opts.complete(null, {status: 'cancelled'}); | |
}); | |
this.then = promise.then.bind(promise); | |
this['catch'] = promise['catch'].bind(promise); | |
this.then(function (result) { | |
complete(null, result); | |
}, complete); | |
if (!db.taskqueue.isReady) { | |
db.taskqueue.addTask(function () { | |
if (self.isCancelled) { | |
self.emit('cancel'); | |
} else { | |
self.doChanges(opts); | |
} | |
}); | |
} else { | |
self.doChanges(opts); | |
} | |
} | |
Changes.prototype.cancel = function () { | |
this.isCancelled = true; | |
if (this.db.taskqueue.isReady) { | |
this.emit('cancel'); | |
} | |
}; | |
function processChange(doc, metadata, opts) { | |
var changeList = [{rev: doc._rev}]; | |
if (opts.style === 'all_docs') { | |
changeList = merge.collectLeaves(metadata.rev_tree) | |
.map(function (x) { return {rev: x.rev}; }); | |
} | |
var change = { | |
id: metadata.id, | |
changes: changeList, | |
doc: doc | |
}; | |
if (utils.isDeleted(metadata, doc._rev)) { | |
change.deleted = true; | |
} | |
if (opts.conflicts) { | |
change.doc._conflicts = merge.collectConflicts(metadata); | |
if (!change.doc._conflicts.length) { | |
delete change.doc._conflicts; | |
} | |
} | |
return change; | |
} | |
Changes.prototype.doChanges = function (opts) { | |
var self = this; | |
var callback = opts.complete; | |
opts = utils.clone(opts); | |
if ('live' in opts && !('continuous' in opts)) { | |
opts.continuous = opts.live; | |
} | |
opts.processChange = processChange; | |
if (opts.since === 'latest') { | |
opts.since = 'now'; | |
} | |
if (!opts.since) { | |
opts.since = 0; | |
} | |
if (opts.since === 'now') { | |
this.db.info().then(function (info) { | |
if (self.isCancelled) { | |
callback(null, {status: 'cancelled'}); | |
return; | |
} | |
opts.since = info.update_seq; | |
self.doChanges(opts); | |
}, callback); | |
return; | |
} | |
if (opts.continuous && opts.since !== 'now') { | |
this.db.info().then(function (info) { | |
self.startSeq = info.update_seq; | |
}, function (err) { | |
if (err.id === 'idbNull') { | |
//db closed before this returned | |
//thats ok | |
return; | |
} | |
throw err; | |
}); | |
} | |
if (this.db.type() !== 'http' && | |
opts.filter && typeof opts.filter === 'string' && | |
!opts.doc_ids) { | |
return this.filterChanges(opts); | |
} | |
if (!('descending' in opts)) { | |
opts.descending = false; | |
} | |
// 0 and 1 should return 1 document | |
opts.limit = opts.limit === 0 ? 1 : opts.limit; | |
opts.complete = callback; | |
var newPromise = this.db._changes(opts); | |
if (newPromise && typeof newPromise.cancel === 'function') { | |
var cancel = self.cancel; | |
self.cancel = utils.getArguments(function (args) { | |
newPromise.cancel(); | |
cancel.apply(this, args); | |
}); | |
} | |
}; | |
Changes.prototype.filterChanges = function (opts) { | |
var self = this; | |
var callback = opts.complete; | |
if (opts.filter === '_view') { | |
if (!opts.view || typeof opts.view !== 'string') { | |
var err = errors.error(errors.BAD_REQUEST, | |
'`view` filter parameter is not provided.'); | |
callback(err); | |
return; | |
} | |
// fetch a view from a design doc, make it behave like a filter | |
var viewName = opts.view.split('/'); | |
this.db.get('_design/' + viewName[0], function (err, ddoc) { | |
if (self.isCancelled) { | |
callback(null, {status: 'cancelled'}); | |
return; | |
} | |
if (err) { | |
callback(errors.generateErrorFromResponse(err)); | |
return; | |
} | |
if (ddoc && ddoc.views && ddoc.views[viewName[1]]) { | |
var filter = evalView(ddoc.views[viewName[1]].map); | |
opts.filter = filter; | |
self.doChanges(opts); | |
return; | |
} | |
var msg = ddoc.views ? 'missing json key: ' + viewName[1] : | |
'missing json key: views'; | |
if (!err) { | |
err = errors.error(errors.MISSING_DOC, msg); | |
} | |
callback(err); | |
return; | |
}); | |
} else { | |
// fetch a filter from a design doc | |
var filterName = opts.filter.split('/'); | |
this.db.get('_design/' + filterName[0], function (err, ddoc) { | |
if (self.isCancelled) { | |
callback(null, {status: 'cancelled'}); | |
return; | |
} | |
if (err) { | |
callback(errors.generateErrorFromResponse(err)); | |
return; | |
} | |
if (ddoc && ddoc.filters && ddoc.filters[filterName[1]]) { | |
var filter = evalFilter(ddoc.filters[filterName[1]]); | |
opts.filter = filter; | |
self.doChanges(opts); | |
return; | |
} else { | |
var msg = (ddoc && ddoc.filters) ? 'missing json key: ' + filterName[1] | |
: 'missing json key: filters'; | |
if (!err) { | |
err = errors.error(errors.MISSING_DOC, msg); | |
} | |
callback(err); | |
return; | |
} | |
}); | |
} | |
}; | |
},{"./deps/errors":120,"./evalFilter":130,"./evalView":131,"./merge":133,"./utils":138,"events":45}],115:[function(require,module,exports){ | |
'use strict'; | |
var Promise = require('./deps/promise'); | |
var explain404 = require('./deps/explain404'); | |
var pouchCollate = require('pouchdb-collate'); | |
var collate = pouchCollate.collate; | |
function updateCheckpoint(db, id, checkpoint, returnValue) { | |
return db.get(id).catch(function (err) { | |
if (err.status === 404) { | |
if (db.type() === 'http') { | |
explain404( | |
'PouchDB is just checking if a remote checkpoint exists.'); | |
} | |
return {_id: id}; | |
} | |
throw err; | |
}).then(function (doc) { | |
if (returnValue.cancelled) { | |
return; | |
} | |
doc.last_seq = checkpoint; | |
return db.put(doc).catch(function (err) { | |
if (err.status === 409) { | |
// retry; someone is trying to write a checkpoint simultaneously | |
return updateCheckpoint(db, id, checkpoint, returnValue); | |
} | |
throw err; | |
}); | |
}); | |
} | |
function Checkpointer(src, target, id, returnValue) { | |
this.src = src; | |
this.target = target; | |
this.id = id; | |
this.returnValue = returnValue; | |
} | |
Checkpointer.prototype.writeCheckpoint = function (checkpoint) { | |
var self = this; | |
return this.updateTarget(checkpoint).then(function () { | |
return self.updateSource(checkpoint); | |
}); | |
}; | |
Checkpointer.prototype.updateTarget = function (checkpoint) { | |
return updateCheckpoint(this.target, this.id, checkpoint, this.returnValue); | |
}; | |
Checkpointer.prototype.updateSource = function (checkpoint) { | |
var self = this; | |
if (this.readOnlySource) { | |
return Promise.resolve(true); | |
} | |
return updateCheckpoint(this.src, this.id, checkpoint, this.returnValue) | |
.catch(function (err) { | |
var isForbidden = typeof err.status === 'number' && | |
Math.floor(err.status / 100) === 4; | |
if (isForbidden) { | |
self.readOnlySource = true; | |
return true; | |
} | |
throw err; | |
}); | |
}; | |
Checkpointer.prototype.getCheckpoint = function () { | |
var self = this; | |
return self.target.get(self.id).then(function (targetDoc) { | |
return self.src.get(self.id).then(function (sourceDoc) { | |
if (collate(targetDoc.last_seq, sourceDoc.last_seq) === 0) { | |
return sourceDoc.last_seq; | |
} | |
return 0; | |
}, function (err) { | |
if (err.status === 404 && targetDoc.last_seq) { | |
return self.src.put({ | |
_id: self.id, | |
last_seq: 0 | |
}).then(function () { | |
return 0; | |
}, function (err) { | |
if (err.status === 401) { | |
self.readOnlySource = true; | |
return targetDoc.last_seq; | |
} | |
return 0; | |
}); | |
} | |
throw err; | |
}); | |
}).catch(function (err) { | |
if (err.status !== 404) { | |
throw err; | |
} | |
return 0; | |
}); | |
}; | |
module.exports = Checkpointer; | |
},{"./deps/explain404":121,"./deps/promise":126,"pouchdb-collate":144}],116:[function(require,module,exports){ | |
(function (process,global){ | |
/*globals cordova */ | |
"use strict"; | |
var Adapter = require('./adapter'); | |
var utils = require('./utils'); | |
var TaskQueue = require('./taskqueue'); | |
var Promise = utils.Promise; | |
function defaultCallback(err) { | |
if (err && global.debug) { | |
console.error(err); | |
} | |
} | |
utils.inherits(PouchDB, Adapter); | |
function PouchDB(name, opts, callback) { | |
if (!(this instanceof PouchDB)) { | |
return new PouchDB(name, opts, callback); | |
} | |
var self = this; | |
if (typeof opts === 'function' || typeof opts === 'undefined') { | |
callback = opts; | |
opts = {}; | |
} | |
if (name && typeof name === 'object') { | |
opts = name; | |
name = undefined; | |
} | |
if (typeof callback === 'undefined') { | |
callback = defaultCallback; | |
} | |
name = name || opts.name; | |
opts = opts ? utils.clone(opts) : {}; | |
// if name was specified via opts, ignore for the sake of dependentDbs | |
delete opts.name; | |
this.__opts = opts; | |
var oldCB = callback; | |
self.auto_compaction = opts.auto_compaction; | |
self.prefix = PouchDB.prefix; | |
Adapter.call(self); | |
self.taskqueue = new TaskQueue(); | |
var promise = new Promise(function (fulfill, reject) { | |
callback = function (err, resp) { | |
if (err) { | |
return reject(err); | |
} | |
delete resp.then; | |
fulfill(resp); | |
}; | |
opts = utils.clone(opts); | |
var originalName = opts.name || name; | |
var backend, error; | |
(function () { | |
try { | |
if (typeof originalName !== 'string') { | |
error = new Error('Missing/invalid DB name'); | |
error.code = 400; | |
throw error; | |
} | |
backend = PouchDB.parseAdapter(originalName, opts); | |
opts.originalName = originalName; | |
opts.name = backend.name; | |
if (opts.prefix && backend.adapter !== 'http' && | |
backend.adapter !== 'https') { | |
opts.name = opts.prefix + opts.name; | |
} | |
opts.adapter = opts.adapter || backend.adapter; | |
self._adapter = opts.adapter; | |
self._db_name = originalName; | |
if (!PouchDB.adapters[opts.adapter]) { | |
error = new Error('Adapter is missing'); | |
error.code = 404; | |
throw error; | |
} | |
if (!PouchDB.adapters[opts.adapter].valid()) { | |
error = new Error('Invalid Adapter'); | |
error.code = 404; | |
throw error; | |
} | |
} catch (err) { | |
self.taskqueue.fail(err); | |
self.changes = utils.toPromise(function (opts) { | |
if (opts.complete) { | |
opts.complete(err); | |
} | |
}); | |
} | |
}()); | |
if (error) { | |
return reject(error); // constructor error, see above | |
} | |
self.adapter = opts.adapter; | |
// needs access to PouchDB; | |
self.replicate = {}; | |
self.replicate.from = function (url, opts, callback) { | |
return self.constructor.replicate(url, self, opts, callback); | |
}; | |
self.replicate.to = function (url, opts, callback) { | |
return self.constructor.replicate(self, url, opts, callback); | |
}; | |
self.sync = function (dbName, opts, callback) { | |
return self.constructor.sync(self, dbName, opts, callback); | |
}; | |
self.replicate.sync = self.sync; | |
PouchDB.adapters[opts.adapter].call(self, opts, function (err) { | |
if (err) { | |
if (callback) { | |
self.taskqueue.fail(err); | |
callback(err); | |
} | |
return; | |
} | |
function destructionListener() { | |
PouchDB.emit('destroyed', opts.originalName); | |
//so we don't have to sift through all dbnames | |
PouchDB.emit(opts.originalName, 'destroyed'); | |
self.removeListener('destroyed', destructionListener); | |
} | |
self.on('destroyed', destructionListener); | |
self.emit('created', self); | |
PouchDB.emit('created', opts.originalName); | |
self.taskqueue.ready(self); | |
callback(null, self); | |
}); | |
if (opts.skipSetup) { | |
self.taskqueue.ready(self); | |
process.nextTick(function () { | |
callback(null, self); | |
}); | |
} | |
if (utils.isCordova()) { | |
//to inform websql adapter that we can use api | |
cordova.fireWindowEvent(opts.name + "_pouch", {}); | |
} | |
}); | |
promise.then(function (resp) { | |
oldCB(null, resp); | |
}, oldCB); | |
self.then = promise.then.bind(promise); | |
self.catch = promise.catch.bind(promise); | |
} | |
PouchDB.debug = require('debug'); | |
module.exports = PouchDB; | |
}).call(this,require("/Users/nlawson/workspace-node/pouchdb-authentication/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
},{"./adapter":102,"./taskqueue":137,"./utils":138,"/Users/nlawson/workspace-node/pouchdb-authentication/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":46,"debug":141}],117:[function(require,module,exports){ | |
(function (process){ | |
"use strict"; | |
var request = require('request'); | |
var buffer = require('./buffer'); | |
var errors = require('./errors'); | |
var utils = require('../utils'); | |
function ajax(options, adapterCallback) { | |
var requestCompleted = false; | |
var callback = utils.getArguments(function (args) { | |
if (requestCompleted) { | |
return; | |
} | |
adapterCallback.apply(this, args); | |
requestCompleted = true; | |
}); | |
if (typeof options === "function") { | |
callback = options; | |
options = {}; | |
} | |
options = utils.clone(options); | |
var defaultOptions = { | |
method : "GET", | |
headers: {}, | |
json: true, | |
processData: true, | |
timeout: 10000, | |
cache: false | |
}; | |
options = utils.extend(true, defaultOptions, options); | |
function onSuccess(obj, resp, cb) { | |
if (!options.binary && !options.json && options.processData && | |
typeof obj !== 'string') { | |
obj = JSON.stringify(obj); | |
} else if (!options.binary && options.json && typeof obj === 'string') { | |
try { | |
obj = JSON.parse(obj); | |
} catch (e) { | |
// Probably a malformed JSON from server | |
return cb(e); | |
} | |
} | |
if (Array.isArray(obj)) { | |
obj = obj.map(function (v) { | |
if (v.error || v.missing) { | |
return errors.generateErrorFromResponse(v); | |
} else { | |
return v; | |
} | |
}); | |
} | |
cb(null, obj, resp); | |
} | |
function onError(err, cb) { | |
var errParsed, errObj; | |
if (err.code && err.status) { | |
var err2 = new Error(err.message || err.code); | |
err2.status = err.status; | |
return cb(err2); | |
} | |
try { | |
errParsed = JSON.parse(err.responseText); | |
//would prefer not to have a try/catch clause | |
errObj = errors.generateErrorFromResponse(errParsed); | |
} catch (e) { | |
errObj = errors.generateErrorFromResponse(err); | |
} | |
cb(errObj); | |
} | |
if (options.json) { | |
if (!options.binary) { | |
options.headers.Accept = 'application/json'; | |
} | |
options.headers['Content-Type'] = options.headers['Content-Type'] || | |
'application/json'; | |
} | |
if (options.binary) { | |
options.encoding = null; | |
options.json = false; | |
} | |
if (!options.processData) { | |
options.json = false; | |
} | |
function defaultBody(data) { | |
if (process.browser) { | |
return ''; | |
} | |
return new buffer('', 'binary'); | |
} | |
return request(options, function (err, response, body) { | |
if (err) { | |
err.status = response ? response.statusCode : 400; | |
return onError(err, callback); | |
} | |
var error; | |
var content_type = response.headers && response.headers['content-type']; | |
var data = body || defaultBody(); | |
// CouchDB doesn't always return the right content-type for JSON data, so | |
// we check for ^{ and }$ (ignoring leading/trailing whitespace) | |
if (!options.binary && (options.json || !options.processData) && | |
typeof data !== 'object' && | |
(/json/.test(content_type) || | |
(/^[\s]*\{/.test(data) && /\}[\s]*$/.test(data)))) { | |
data = JSON.parse(data); | |
} | |
if (response.statusCode >= 200 && response.statusCode < 300) { | |
onSuccess(data, response, callback); | |
} else { | |
if (options.binary) { | |
data = JSON.parse(data.toString()); | |
} | |
error = errors.generateErrorFromResponse(data); | |
error.status = response.statusCode; | |
callback(error); | |
} | |
}); | |
} | |
module.exports = ajax; | |
}).call(this,require("/Users/nlawson/workspace-node/pouchdb-authentication/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js")) | |
},{"../utils":138,"./buffer":119,"./errors":120,"/Users/nlawson/workspace-node/pouchdb-authentication/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":46,"request":127}],118:[function(require,module,exports){ | |
(function (global){ | |
"use strict"; | |
//Abstracts constructing a Blob object, so it also works in older | |
//browsers that don't support the native Blob constructor. (i.e. | |
//old QtWebKit versions, at least). | |
function createBlob(parts, properties) { | |
parts = parts || []; | |
properties = properties || {}; | |
try { | |
return new Blob(parts, properties); | |
} catch (e) { | |
if (e.name !== "TypeError") { | |
throw e; | |
} | |
var BlobBuilder = global.BlobBuilder || | |
global.MSBlobBuilder || | |
global.MozBlobBuilder || | |
global.WebKitBlobBuilder; | |
var builder = new BlobBuilder(); | |
for (var i = 0; i < parts.length; i += 1) { | |
builder.append(parts[i]); | |
} | |
return builder.getBlob(properties.type); | |
} | |
} | |
module.exports = createBlob; | |
}).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
},{}],119:[function(require,module,exports){ | |
// hey guess what, we don't need this in the browser | |
module.exports = {}; | |
},{}],120:[function(require,module,exports){ | |
"use strict"; | |
var inherits = require('inherits'); | |
inherits(PouchError, Error); | |
function PouchError(opts) { | |
Error.call(opts.reason); | |
this.status = opts.status; | |
this.name = opts.error; | |
this.message = opts.reason; | |
this.error = true; | |
} | |
PouchError.prototype.toString = function () { | |
return JSON.stringify({ | |
status: this.status, | |
name: this.name, | |
message: this.message | |
}); | |
}; | |
exports.UNAUTHORIZED = new PouchError({ | |
status: 401, | |
error: 'unauthorized', | |
reason: "Name or password is incorrect." | |
}); | |
exports.MISSING_BULK_DOCS = new PouchError({ | |
status: 400, | |
error: 'bad_request', | |
reason: "Missing JSON list of 'docs'" | |
}); | |
exports.MISSING_DOC = new PouchError({ | |
status: 404, | |
error: 'not_found', | |
reason: 'missing' | |
}); | |
exports.REV_CONFLICT = new PouchError({ | |
status: 409, | |
error: 'conflict', | |
reason: 'Document update conflict' | |
}); | |
exports.INVALID_ID = new PouchError({ | |
status: 400, | |
error: 'invalid_id', | |
reason: '_id field must contain a string' | |
}); | |
exports.MISSING_ID = new PouchError({ | |
status: 412, | |
error: 'missing_id', | |
reason: '_id is required for puts' | |
}); | |
exports.RESERVED_ID = new PouchError({ | |
status: 400, | |
error: 'bad_request', | |
reason: 'Only reserved document ids may start with underscore.' | |
}); | |
exports.NOT_OPEN = new PouchError({ | |
status: 412, | |
error: 'precondition_failed', | |
reason: 'Database not open' | |
}); | |
exports.UNKNOWN_ERROR = new PouchError({ | |
status: 500, | |
error: 'unknown_error', | |
reason: 'Database encountered an unknown error' | |
}); | |
exports.BAD_ARG = new PouchError({ | |
status: 500, | |
error: 'badarg', | |
reason: 'Some query argument is invalid' | |
}); | |
exports.INVALID_REQUEST = new PouchError({ | |
status: 400, | |
error: 'invalid_request', | |
reason: 'Request was invalid' | |
}); | |
exports.QUERY_PARSE_ERROR = new PouchError({ | |
status: 400, | |
error: 'query_parse_error', | |
reason: 'Some query parameter is invalid' | |
}); | |
exports.DOC_VALIDATION = new PouchError({ | |
status: 500, | |
error: 'doc_validation', | |
reason: 'Bad special document member' | |
}); | |
exports.BAD_REQUEST = new PouchError({ | |
status: 400, | |
error: 'bad_request', | |
reason: 'Something wrong with the request' | |
}); | |
exports.NOT_AN_OBJECT = new PouchError({ | |
status: 400, | |
error: 'bad_request', | |
reason: 'Document must be a JSON object' | |
}); | |
exports.DB_MISSING = new PouchError({ | |
status: 404, | |
error: 'not_found', | |
reason: 'Database not found' | |
}); | |
exports.IDB_ERROR = new PouchError({ | |
status: 500, | |
error: 'indexed_db_went_bad', | |
reason: 'unknown' | |
}); | |
exports.WSQ_ERROR = new PouchError({ | |
status: 500, | |
error: 'web_sql_went_bad', | |
reason: 'unknown' | |
}); | |
exports.LDB_ERROR = new PouchError({ | |
status: 500, | |
error: 'levelDB_went_went_bad', | |
reason: 'unknown' | |
}); | |
exports.FORBIDDEN = new PouchError({ | |
status: 403, | |
error: 'forbidden', | |
reason: 'Forbidden by design doc validate_doc_update function' | |
}); | |
exports.INVALID_REV = new PouchError({ | |
status: 400, | |
error: 'bad_request', | |
reason: 'Invalid rev format' | |
}); | |
exports.FILE_EXISTS = new PouchError({ | |
status: 412, | |
error: 'file_exists', | |
reason: 'The database could not be created, the file already exists.' | |
}); | |
exports.MISSING_STUB = new PouchError({ | |
status: 412, | |
error: 'missing_stub' | |
}); | |
exports.error = function (error, reason, name) { | |
function CustomPouchError(reason) { | |
// inherit error properties from our parent error manually | |
// so as to allow proper JSON parsing. | |
/* jshint ignore:start */ | |
for (var p in error) { | |
if (typeof error[p] !== 'function') { | |
this[p] = error[p]; | |
} | |
} | |
/* jshint ignore:end */ | |
if (name !== undefined) { | |
this.name = name; | |
} | |
if (reason !== undefined) { | |
this.reason = reason; | |
} | |
} | |
CustomPouchError.prototype = PouchError.prototype; | |
return new CustomPouchError(reason); | |
}; | |
// Find one of the errors defined above based on the value | |
// of the specified property. | |
// If reason is provided prefer the error matching that reason. | |
// This is for differentiating between errors with the same name and status, | |
// eg, bad_request. | |
exports.getErrorTypeByProp = function (prop, value, reason) { | |
var errors = exports; | |
var keys = Object.keys(errors).filter(function (key) { | |
var error = errors[key]; | |
return typeof error !== 'function' && error[prop] === value; | |
}); | |
var key = reason && keys.filter(function (key) { | |
var error = errors[key]; | |
return error.message === reason; | |
})[0] || keys[0]; | |
return (key) ? errors[key] : null; | |
}; | |
exports.generateErrorFromResponse = function (res) { | |
var error, errName, errType, errMsg, errReason; | |
var errors = exports; | |
errName = (res.error === true && typeof res.name === 'string') ? | |
res.name : | |
res.error; | |
errReason = res.reason; | |
errType = errors.getErrorTypeByProp('name', errName, errReason); | |
if (res.missing || | |
errReason === 'missing' || | |
errReason === 'deleted' || | |
errName === 'not_found') { | |
errType = errors.MISSING_DOC; | |
} else if (errName === 'doc_validation') { | |
// doc validation needs special treatment since | |
// res.reason depends on the validation error. | |
// see utils.js | |
errType = errors.DOC_VALIDATION; | |
errMsg = errReason; | |
} else if (errName === 'bad_request' && errType.message !== errReason) { | |
// if bad_request error already found based on reason don't override. | |
// attachment errors. | |
if (errReason.indexOf('unknown stub attachment') === 0) { | |
errType = errors.MISSING_STUB; | |
errMsg = errReason; | |
} else { | |
errType = errors.BAD_REQUEST; | |
} | |
} | |
// fallback to error by statys or unknown error. | |
if (!errType) { | |
errType = errors.getErrorTypeByProp('status', res.status, errReason) || | |
errors.UNKNOWN_ERROR; | |
} | |
error = errors.error(errType, errReason, errName); | |
// Keep custom message. | |
if (errMsg) { | |
error.message = errMsg; | |
} | |
// Keep helpful response data in our error messages. | |
if (res.id) { | |
error.id = res.id; | |
} | |
if (res.status) { | |
error.status = res.status; | |
} | |
if (res.statusText) { | |
error.name = res.statusText; | |
} | |
if (res.missing) { | |
error.missing = res.missing; | |
} | |
return error; | |
}; | |
},{"inherits":79}],121:[function(require,module,exports){ | |
(function (process,global){ | |
'use strict'; | |
// designed to give info to browser users, who are disturbed | |
// when they see 404s in the console | |
function explain404(str) { | |
if (process.browser && 'console' in global && 'info' in console) { | |
console.info('The above 404 is totally normal. ' + str); | |
} | |
} | |
module.exports = explain404; | |
}).call(this,require("/Users/nlawson/workspace-node/pouchdb-authentication/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
},{"/Users/nlawson/workspace-node/pouchdb-authentication/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":46}],122:[function(require,module,exports){ | |
(function (process,global){ | |
'use strict'; | |
var crypto = require('crypto'); | |
var Md5 = require('spark-md5'); | |
var setImmediateShim = global.setImmediate || global.setTimeout; | |
var MD5_CHUNK_SIZE = 32768; | |
// convert a 64-bit int to a binary string | |
function intToString(int) { | |
var bytes = [ | |
(int & 0xff), | |
((int >>> 8) & 0xff), | |
((int >>> 16) & 0xff), | |
((int >>> 24) & 0xff) | |
]; | |
return bytes.map(function (byte) { | |
return String.fromCharCode(byte); | |
}).join(''); | |
} | |
// convert an array of 64-bit ints into | |
// a base64-encoded string | |
function rawToBase64(raw) { | |
var res = ''; | |
for (var i = 0; i < raw.length; i++) { | |
res += intToString(raw[i]); | |
} | |
return btoa(res); | |
} | |
function appendBuffer(buffer, data, start, end) { | |
if (start > 0 || end < data.byteLength) { | |
// only create a subarray if we really need to | |
data = new Uint8Array(data, start, | |
Math.min(end, data.byteLength) - start); | |
} | |
buffer.append(data); | |
} | |
function appendString(buffer, data, start, end) { | |
if (start > 0 || end < data.length) { | |
// only create a substring if we really need to | |
data = data.substring(start, end); | |
} | |
buffer.appendBinary(data); | |
} | |
module.exports = function (data, callback) { | |
if (!process.browser) { | |
var base64 = crypto.createHash('md5').update(data).digest('base64'); | |
callback(null, base64); | |
return; | |
} | |
var inputIsString = typeof data === 'string'; | |
var len = inputIsString ? data.length : data.byteLength; | |
var chunkSize = Math.min(MD5_CHUNK_SIZE, len); | |
var chunks = Math.ceil(len / chunkSize); | |
var currentChunk = 0; | |
var buffer = inputIsString ? new Md5() : new Md5.ArrayBuffer(); | |
var append = inputIsString ? appendString : appendBuffer; | |
function loadNextChunk() { | |
var start = currentChunk * chunkSize; | |
var end = start + chunkSize; | |
currentChunk++; | |
if (currentChunk < chunks) { | |
append(buffer, data, start, end); | |
setImmediateShim(loadNextChunk); | |
} else { | |
append(buffer, data, start, end); | |
var raw = buffer.end(true); | |
var base64 = rawToBase64(raw); | |
callback(null, base64); | |
buffer.destroy(); | |
} | |
} | |
loadNextChunk(); | |
}; | |
}).call(this,require("/Users/nlawson/workspace-node/pouchdb-authentication/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
},{"/Users/nlawson/workspace-node/pouchdb-authentication/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":46,"crypto":41,"spark-md5":154}],123:[function(require,module,exports){ | |
'use strict'; | |
var errors = require('./errors'); | |
var uuid = require('./uuid'); | |
function toObject(array) { | |
return array.reduce(function (obj, item) { | |
obj[item] = true; | |
return obj; | |
}, {}); | |
} | |
// List of top level reserved words for doc | |
var reservedWords = toObject([ | |
'_id', | |
'_rev', | |
'_attachments', | |
'_deleted', | |
'_revisions', | |
'_revs_info', | |
'_conflicts', | |
'_deleted_conflicts', | |
'_local_seq', | |
'_rev_tree', | |
//replication documents | |
'_replication_id', | |
'_replication_state', | |
'_replication_state_time', | |
'_replication_state_reason', | |
'_replication_stats', | |
// Specific to Couchbase Sync Gateway | |
'_removed' | |
]); | |
// List of reserved words that should end up the document | |
var dataWords = toObject([ | |
'_attachments', | |
//replication documents | |
'_replication_id', | |
'_replication_state', | |
'_replication_state_time', | |
'_replication_state_reason', | |
'_replication_stats' | |
]); | |
// Determine id an ID is valid | |
// - invalid IDs begin with an underescore that does not begin '_design' or | |
// '_local' | |
// - any other string value is a valid id | |
// Returns the specific error object for each case | |
exports.invalidIdError = function (id) { | |
var err; | |
if (!id) { | |
err = errors.error(errors.MISSING_ID); | |
} else if (typeof id !== 'string') { | |
err = errors.error(errors.INVALID_ID); | |
} else if (/^_/.test(id) && !(/^_(design|local)/).test(id)) { | |
err = errors.error(errors.RESERVED_ID); | |
} | |
if (err) { | |
throw err; | |
} | |
}; | |
function parseRevisionInfo(rev) { | |
if (!/^\d+\-./.test(rev)) { | |
return errors.error(errors.INVALID_REV); | |
} | |
var idx = rev.indexOf('-'); | |
var left = rev.substring(0, idx); | |
var right = rev.substring(idx + 1); | |
return { | |
prefix: parseInt(left, 10), | |
id: right | |
}; | |
} | |
function makeRevTreeFromRevisions(revisions, opts) { | |
var pos = revisions.start - revisions.ids.length + 1; | |
var revisionIds = revisions.ids; | |
var ids = [revisionIds[0], opts, []]; | |
for (var i = 1, len = revisionIds.length; i < len; i++) { | |
ids = [revisionIds[i], {status: 'missing'}, [ids]]; | |
} | |
return [{ | |
pos: pos, | |
ids: ids | |
}]; | |
} | |
// Preprocess documents, parse their revisions, assign an id and a | |
// revision for new writes that are missing them, etc | |
exports.parseDoc = function (doc, newEdits) { | |
var nRevNum; | |
var newRevId; | |
var revInfo; | |
var opts = {status: 'available'}; | |
if (doc._deleted) { | |
opts.deleted = true; | |
} | |
if (newEdits) { | |
if (!doc._id) { | |
doc._id = uuid(); | |
} | |
newRevId = uuid(32, 16).toLowerCase(); | |
if (doc._rev) { | |
revInfo = parseRevisionInfo(doc._rev); | |
if (revInfo.error) { | |
return revInfo; | |
} | |
doc._rev_tree = [{ | |
pos: revInfo.prefix, | |
ids: [revInfo.id, {status: 'missing'}, [[newRevId, opts, []]]] | |
}]; | |
nRevNum = revInfo.prefix + 1; | |
} else { | |
doc._rev_tree = [{ | |
pos: 1, | |
ids : [newRevId, opts, []] | |
}]; | |
nRevNum = 1; | |
} | |
} else { | |
if (doc._revisions) { | |
doc._rev_tree = makeRevTreeFromRevisions(doc._revisions, opts); | |
nRevNum = doc._revisions.start; | |
newRevId = doc._revisions.ids[0]; | |
} | |
if (!doc._rev_tree) { | |
revInfo = parseRevisionInfo(doc._rev); | |
if (revInfo.error) { | |
return revInfo; | |
} | |
nRevNum = revInfo.prefix; | |
newRevId = revInfo.id; | |
doc._rev_tree = [{ | |
pos: nRevNum, | |
ids: [newRevId, opts, []] | |
}]; | |
} | |
} | |
exports.invalidIdError(doc._id); | |
doc._rev = nRevNum + '-' + newRevId; | |
var result = {metadata : {}, data : {}}; | |
for (var key in doc) { | |
if (doc.hasOwnProperty(key)) { | |
var specialKey = key[0] === '_'; | |
if (specialKey && !reservedWords[key]) { | |
var error = errors.error(errors.DOC_VALIDATION, key); | |
error.message = errors.DOC_VALIDATION.message + ': ' + key; | |
throw error; | |
} else if (specialKey && !dataWords[key]) { | |
result.metadata[key.slice(1)] = doc[key]; | |
} else { | |
result.data[key] = doc[key]; | |
} | |
} | |
} | |
return result; | |
}; | |
},{"./errors":120,"./uuid":129}],124:[function(require,module,exports){ | |
'use strict'; | |
// | |
// Parsing hex strings. Yeah. | |
// | |
// So basically we need this because of a bug in WebSQL: | |
// https://code.google.com/p/chromium/issues/detail?id=422690 | |
// https://bugs.webkit.org/show_bug.cgi?id=137637 | |
// | |
// UTF-8 and UTF-16 are provided as separate functions | |
// for meager performance improvements | |
// | |
function decodeUtf8(str) { | |
return decodeURIComponent(window.escape(str)); | |
} | |
function hexToInt(charCode) { | |
// '0'-'9' is 48-57 | |
// 'A'-'F' is 65-70 | |
// SQLite will only give us uppercase hex | |
return charCode < 65 ? (charCode - 48) : (charCode - 55); | |
} | |
// Example: | |
// pragma encoding=utf8; | |
// select hex('A'); | |
// returns '41' | |
function parseHexUtf8(str, start, end) { | |
var result = ''; | |
while (start < end) { | |
result += String.fromCharCode( | |
(hexToInt(str.charCodeAt(start++)) << 4) | | |
hexToInt(str.charCodeAt(start++))); | |
} | |
return result; | |
} | |
// Example: | |
// pragma encoding=utf16; | |
// select hex('A'); | |
// returns '4100' | |
// notice that the 00 comes after the 41 (i.e. it's swizzled) | |
function parseHexUtf16(str, start, end) { | |
var result = ''; | |
while (start < end) { | |
// UTF-16, so swizzle the bytes | |
result += String.fromCharCode( | |
(hexToInt(str.charCodeAt(start + 2)) << 12) | | |
(hexToInt(str.charCodeAt(start + 3)) << 8) | | |
(hexToInt(str.charCodeAt(start)) << 4) | | |
hexToInt(str.charCodeAt(start + 1))); | |
start += 4; | |
} | |
return result; | |
} | |
function parseHexString(str, encoding) { | |
if (encoding === 'UTF-8') { | |
return decodeUtf8(parseHexUtf8(str, 0, str.length)); | |
} else { | |
return parseHexUtf16(str, 0, str.length); | |
} | |
} | |
module.exports = parseHexString; | |
},{}],125:[function(require,module,exports){ | |
'use strict'; | |
// originally parseUri 1.2.2, now patched by us | |
// (c) Steven Levithan <stevenlevithan.com> | |
// MIT License | |
var options = { | |
strictMode: false, | |
key: ["source", "protocol", "authority", "userInfo", "user", "password", | |
"host", "port", "relative", "path", "directory", "file", "query", | |
"anchor"], | |
q: { | |
name: "queryKey", | |
parser: /(?:^|&)([^&=]*)=?([^&]*)/g | |
}, | |
parser: { | |
/* jshint maxlen: false */ | |
strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/, | |
loose: /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/ | |
} | |
}; | |
function parseUri(str) { | |
var o = options; | |
var m = o.parser[o.strictMode ? "strict" : "loose"].exec(str); | |
var uri = {}; | |
var i = 14; | |
while (i--) { | |
var key = o.key[i]; | |
var value = m[i] || ""; | |
var encoded = ['user', 'password'].indexOf(key) !== -1; | |
uri[key] = encoded ? decodeURIComponent(value) : value; | |
} | |
uri[o.q.name] = {}; | |
uri[o.key[12]].replace(o.q.parser, function ($0, $1, $2) { | |
if ($1) { | |
uri[o.q.name][$1] = $2; | |
} | |
}); | |
return uri; | |
} | |
module.exports = parseUri; | |
},{}],126:[function(require,module,exports){ | |
'use strict'; | |
if (typeof Promise === 'function') { | |
module.exports = Promise; | |
} else { | |
module.exports = require('bluebird'); | |
} | |
},{"bluebird":83}],127:[function(require,module,exports){ | |
/* global fetch */ | |
/* global Headers */ | |
'use strict'; | |
var createBlob = require('./blob.js'); | |
var utils = require('../utils'); | |
function wrappedFetch() { | |
var wrappedPromise = {}; | |
var promise = new utils.Promise(function(resolve, reject) { | |
wrappedPromise.resolve = resolve; | |
wrappedPromise.reject = reject; | |
}); | |
var args = new Array(arguments.length); | |
for (var i = 0; i < args.length; i++) { | |
args[i] = arguments[i]; | |
} | |
wrappedPromise.then = promise.then.bind(promise); | |
wrappedPromise.catch = promise.catch.bind(promise); | |
wrappedPromise.promise = promise; | |
fetch.apply(null, args).then(function(response) { | |
wrappedPromise.resolve(response); | |
}, function(error) { | |
wrappedPromise.reject(error); | |
}).catch(function(error) { | |
wrappedPromise.catch(error); | |
}); | |
return wrappedPromise; | |
} | |
function fetchRequest(options, callback) { | |
var wrappedPromise, timer, fetchResponse; | |
var headers = new Headers(); | |
var fetchOptions = { | |
method: options.method, | |
credentials: 'include', | |
headers: headers | |
}; | |
if (options.json) { | |
headers.set('Accept', 'application/json'); | |
headers.set('Content-Type', options.headers['Content-Type'] || | |
'application/json'); | |
} | |
if (options.body && (options.body instanceof Blob)) { | |
utils.readAsBinaryString(options.body, function(binary) { | |
fetchOptions.body = utils.fixBinary(binary); | |
}); | |
} else if (options.body && | |
options.processData && | |
typeof options.body !== 'string') { | |
fetchOptions.body = JSON.stringify(options.body); | |
} else if ('body' in options) { | |
fetchOptions.body = options.body; | |
} else { | |
fetchOptions.body = null; | |
} | |
Object.keys(options.headers).forEach(function(key) { | |
if (options.headers.hasOwnProperty(key)) { | |
headers.set(key, options.headers[key]); | |
} | |
}); | |
wrappedPromise = wrappedFetch(options.url, fetchOptions); | |
if (options.timeout > 0) { | |
timer = setTimeout(function() { | |
wrappedPromise.reject(new Error('Load timeout for resource: ' + | |
options.url)); | |
}, options.timeout); | |
} | |
wrappedPromise.promise.then(function(response) { | |
var result; | |
fetchResponse = response; | |
if (options.timeout > 0) { | |
clearTimeout(timer); | |
} | |
if (response.status >= 200 && response.status < 300) { | |
return options.binary ? response.blob() : response.text(); | |
} | |
return result.json(); | |
}).then(function(result) { | |
if (fetchResponse.status >= 200 && fetchResponse.status < 300) { | |
callback(null, fetchResponse, result); | |
} else { | |
callback(result, fetchResponse); | |
} | |
}).catch(function(error) { | |
callback(error, fetchResponse); | |
}); | |
return {abort: wrappedPromise.reject}; | |
} | |
function xhRequest(options, callback) { | |
var xhr, timer, hasUpload; | |
var abortReq = function () { | |
xhr.abort(); | |
}; | |
if (options.xhr) { | |
xhr = new options.xhr(); | |
} else { | |
xhr = new XMLHttpRequest(); | |
} | |
// cache-buster, specifically designed to work around IE's aggressive caching | |
// see http://www.dashbay.com/2011/05/internet-explorer-caches-ajax/ | |
if (options.method === 'GET' && !options.cache) { | |
var hasArgs = options.url.indexOf('?') !== -1; | |
options.url += (hasArgs ? '&' : '?') + '_nonce=' + Date.now(); | |
} | |
xhr.open(options.method, options.url); | |
xhr.withCredentials = true; | |
if (options.method === 'GET') { | |
delete options.headers['Content-Type']; | |
} else if (options.json) { | |
options.headers.Accept = 'application/json'; | |
options.headers['Content-Type'] = options.headers['Content-Type'] || | |
'application/json'; | |
if (options.body && | |
options.processData && | |
typeof options.body !== "string") { | |
options.body = JSON.stringify(options.body); | |
} | |
} | |
if (options.binary) { | |
xhr.responseType = 'arraybuffer'; | |
} | |
if (!('body' in options)) { | |
options.body = null; | |
} | |
for (var key in options.headers) { | |
if (options.headers.hasOwnProperty(key)) { | |
xhr.setRequestHeader(key, options.headers[key]); | |
} | |
} | |
if (options.timeout > 0) { | |
timer = setTimeout(abortReq, options.timeout); | |
xhr.onprogress = function () { | |
clearTimeout(timer); | |
timer = setTimeout(abortReq, options.timeout); | |
}; | |
if (typeof hasUpload === 'undefined') { | |
// IE throws an error if you try to access it directly | |
hasUpload = Object.keys(xhr).indexOf('upload') !== -1 && | |
typeof xhr.upload !== 'undefined'; | |
} | |
if (hasUpload) { // does not exist in ie9 | |
xhr.upload.onprogress = xhr.onprogress; | |
} | |
} | |
xhr.onreadystatechange = function () { | |
if (xhr.readyState !== 4) { | |
return; | |
} | |
var response = { | |
statusCode: xhr.status | |
}; | |
if (xhr.status >= 200 && xhr.status < 300) { | |
var data; | |
if (options.binary) { | |
data = createBlob([xhr.response || ''], { | |
type: xhr.getResponseHeader('Content-Type') | |
}); | |
} else { | |
data = xhr.responseText; | |
} | |
callback(null, response, data); | |
} else { | |
var err = {}; | |
try { | |
err = JSON.parse(xhr.response); | |
} catch(e) {} | |
callback(err, response); | |
} | |
}; | |
if (options.body && (options.body instanceof Blob)) { | |
utils.readAsBinaryString(options.body, function (binary) { | |
xhr.send(utils.fixBinary(binary)); | |
}); | |
} else { | |
xhr.send(options.body); | |
} | |
return {abort: abortReq}; | |
} | |
module.exports = function(options, callback) { | |
if (typeof XMLHttpRequest === 'undefined' && !options.xhr) { | |
return fetchRequest(options, callback); | |
} else { | |
return xhRequest(options, callback); | |
} | |
}; | |
},{"../utils":138,"./blob.js":118}],128:[function(require,module,exports){ | |
'use strict'; | |
var upsert = require('pouchdb-upsert').upsert; | |
module.exports = function (db, doc, diffFun, cb) { | |
return upsert.call(db, doc, diffFun, cb); | |
}; | |
},{"pouchdb-upsert":153}],129:[function(require,module,exports){ | |
module.exports=require(3) | |
},{}],130:[function(require,module,exports){ | |
'use strict'; | |
module.exports = evalFilter; | |
function evalFilter(input) { | |
/*jshint evil: true */ | |
return eval([ | |
'(function () { return ', | |
input, | |
' })()' | |
].join('')); | |
} | |
},{}],131:[function(require,module,exports){ | |
'use strict'; | |
module.exports = evalView; | |
function evalView(input) { | |
/*jshint evil: true */ | |
return eval([ | |
'(function () {', | |
' return function (doc) {', | |
' var emitted = false;', | |
' var emit = function (a, b) {', | |
' emitted = true;', | |
' };', | |
' var view = ' + input + ';', | |
' view(doc);', | |
' if (emitted) {', | |
' return true;', | |
' }', | |
' }', | |
'})()' | |
].join('\n')); | |
} | |
},{}],132:[function(require,module,exports){ | |
(function (process){ | |
"use strict"; | |
var PouchDB = require('./setup'); | |
module.exports = PouchDB; | |
PouchDB.ajax = require('./deps/ajax'); | |
PouchDB.utils = require('./utils'); | |
PouchDB.Errors = require('./deps/errors'); | |
PouchDB.replicate = require('./replicate').replicate; | |
PouchDB.sync = require('./sync'); | |
PouchDB.version = require('./version'); | |
var httpAdapter = require('./adapters/http/http'); | |
PouchDB.adapter('http', httpAdapter); | |
PouchDB.adapter('https', httpAdapter); | |
PouchDB.adapter('idb', require('./adapters/idb/idb'), true); | |
PouchDB.adapter('websql', require('./adapters/websql/websql'), true); | |
PouchDB.plugin(require('pouchdb-mapreduce')); | |
if (!process.browser) { | |
var ldbAdapter = require('./adapters/leveldb/leveldb'); | |
PouchDB.adapter('leveldb', ldbAdapter, true); | |
} | |
}).call(this,require("/Users/nlawson/workspace-node/pouchdb-authentication/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js")) | |
},{"./adapters/http/http":103,"./adapters/idb/idb":109,"./adapters/leveldb/leveldb":41,"./adapters/websql/websql":113,"./deps/ajax":117,"./deps/errors":120,"./replicate":134,"./setup":135,"./sync":136,"./utils":138,"./version":139,"/Users/nlawson/workspace-node/pouchdb-authentication/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":46,"pouchdb-mapreduce":149}],133:[function(require,module,exports){ | |
'use strict'; | |
var extend = require('pouchdb-extend'); | |
// for a better overview of what this is doing, read: | |
// https://github.com/apache/couchdb/blob/master/src/couchdb/couch_key_tree.erl | |
// | |
// But for a quick intro, CouchDB uses a revision tree to store a documents | |
// history, A -> B -> C, when a document has conflicts, that is a branch in the | |
// tree, A -> (B1 | B2 -> C), We store these as a nested array in the format | |
// | |
// KeyTree = [Path ... ] | |
// Path = {pos: position_from_root, ids: Tree} | |
// Tree = [Key, Opts, [Tree, ...]], in particular single node: [Key, []] | |
// classic binary search | |
function binarySearch(arr, item, comparator) { | |
var low = 0; | |
var high = arr.length; | |
var mid; | |
while (low < high) { | |
mid = (low + high) >>> 1; | |
if (comparator(arr[mid], item) < 0) { | |
low = mid + 1; | |
} else { | |
high = mid; | |
} | |
} | |
return low; | |
} | |
// assuming the arr is sorted, insert the item in the proper place | |
function insertSorted(arr, item, comparator) { | |
var idx = binarySearch(arr, item, comparator); | |
arr.splice(idx, 0, item); | |
} | |
// Turn a path as a flat array into a tree with a single branch | |
function pathToTree(path) { | |
var doc = path.shift(); | |
var root = [doc.id, doc.opts, []]; | |
var leaf = root; | |
var nleaf; | |
while (path.length) { | |
doc = path.shift(); | |
nleaf = [doc.id, doc.opts, []]; | |
leaf[2].push(nleaf); | |
leaf = nleaf; | |
} | |
return root; | |
} | |
// compare the IDs of two trees | |
function compareTree(a, b) { | |
return a[0] < b[0] ? -1 : 1; | |
} | |
// Merge two trees together | |
// The roots of tree1 and tree2 must be the same revision | |
function mergeTree(in_tree1, in_tree2) { | |
var queue = [{tree1: in_tree1, tree2: in_tree2}]; | |
var conflicts = false; | |
while (queue.length > 0) { | |
var item = queue.pop(); | |
var tree1 = item.tree1; | |
var tree2 = item.tree2; | |
if (tree1[1].status || tree2[1].status) { | |
tree1[1].status = | |
(tree1[1].status === 'available' || | |
tree2[1].status === 'available') ? 'available' : 'missing'; | |
} | |
for (var i = 0; i < tree2[2].length; i++) { | |
if (!tree1[2][0]) { | |
conflicts = 'new_leaf'; | |
tree1[2][0] = tree2[2][i]; | |
continue; | |
} | |
var merged = false; | |
for (var j = 0; j < tree1[2].length; j++) { | |
if (tree1[2][j][0] === tree2[2][i][0]) { | |
queue.push({tree1: tree1[2][j], tree2: tree2[2][i]}); | |
merged = true; | |
} | |
} | |
if (!merged) { | |
conflicts = 'new_branch'; | |
insertSorted(tree1[2], tree2[2][i], compareTree); | |
} | |
} | |
} | |
return {conflicts: conflicts, tree: in_tree1}; | |
} | |
function doMerge(tree, path, dontExpand) { | |
var restree = []; | |
var conflicts = false; | |
var merged = false; | |
var res; | |
if (!tree.length) { | |
return {tree: [path], conflicts: 'new_leaf'}; | |
} | |
tree.forEach(function (branch) { | |
if (branch.pos === path.pos && branch.ids[0] === path.ids[0]) { | |
// Paths start at the same position and have the same root, so they need | |
// merged | |
res = mergeTree(branch.ids, path.ids); | |
restree.push({pos: branch.pos, ids: res.tree}); | |
conflicts = conflicts || res.conflicts; | |
merged = true; | |
} else if (dontExpand !== true) { | |
// The paths start at a different position, take the earliest path and | |
// traverse up until it as at the same point from root as the path we | |
// want to merge. If the keys match we return the longer path with the | |
// other merged After stemming we dont want to expand the trees | |
var t1 = branch.pos < path.pos ? branch : path; | |
var t2 = branch.pos < path.pos ? path : branch; | |
var diff = t2.pos - t1.pos; | |
var candidateParents = []; | |
var trees = []; | |
trees.push({ids: t1.ids, diff: diff, parent: null, parentIdx: null}); | |
while (trees.length > 0) { | |
var item = trees.pop(); | |
if (item.diff === 0) { | |
if (item.ids[0] === t2.ids[0]) { | |
candidateParents.push(item); | |
} | |
continue; | |
} | |
if (!item.ids) { | |
continue; | |
} | |
/*jshint loopfunc:true */ | |
item.ids[2].forEach(function (el, idx) { | |
trees.push( | |
{ids: el, diff: item.diff - 1, parent: item.ids, parentIdx: idx}); | |
}); | |
} | |
var el = candidateParents[0]; | |
if (!el) { | |
restree.push(branch); | |
} else { | |
res = mergeTree(el.ids, t2.ids); | |
el.parent[2][el.parentIdx] = res.tree; | |
restree.push({pos: t1.pos, ids: t1.ids}); | |
conflicts = conflicts || res.conflicts; | |
merged = true; | |
} | |
} else { | |
restree.push(branch); | |
} | |
}); | |
// We didnt find | |
if (!merged) { | |
restree.push(path); | |
} | |
restree.sort(function (a, b) { | |
return a.pos - b.pos; | |
}); | |
return { | |
tree: restree, | |
conflicts: conflicts || 'internal_node' | |
}; | |
} | |
// To ensure we dont grow the revision tree infinitely, we stem old revisions | |
function stem(tree, depth) { | |
// First we break out the tree into a complete list of root to leaf paths, | |
// we cut off the start of the path and generate a new set of flat trees | |
var stemmedPaths = PouchMerge.rootToLeaf(tree).map(function (path) { | |
var stemmed = path.ids.slice(-depth); | |
return { | |
pos: path.pos + (path.ids.length - stemmed.length), | |
ids: pathToTree(stemmed) | |
}; | |
}); | |
// Then we remerge all those flat trees together, ensuring that we dont | |
// connect trees that would go beyond the depth limit | |
return stemmedPaths.reduce(function (prev, current) { | |
return doMerge(prev, current, true).tree; | |
}, [stemmedPaths.shift()]); | |
} | |
var PouchMerge = {}; | |
PouchMerge.merge = function (tree, path, depth) { | |
// Ugh, nicer way to not modify arguments in place? | |
tree = extend(true, [], tree); | |
path = extend(true, {}, path); | |
var newTree = doMerge(tree, path); | |
return { | |
tree: stem(newTree.tree, depth), | |
conflicts: newTree.conflicts | |
}; | |
}; | |
// We fetch all leafs of the revision tree, and sort them based on tree length | |
// and whether they were deleted, undeleted documents with the longest revision | |
// tree (most edits) win | |
// The final sort algorithm is slightly documented in a sidebar here: | |
// http://guide.couchdb.org/draft/conflicts.html | |
PouchMerge.winningRev = function (metadata) { | |
var leafs = []; | |
PouchMerge.traverseRevTree(metadata.rev_tree, | |
function (isLeaf, pos, id, something, opts) { | |
if (isLeaf) { | |
leafs.push({pos: pos, id: id, deleted: !!opts.deleted}); | |
} | |
}); | |
leafs.sort(function (a, b) { | |
if (a.deleted !== b.deleted) { | |
return a.deleted > b.deleted ? 1 : -1; | |
} | |
if (a.pos !== b.pos) { | |
return b.pos - a.pos; | |
} | |
return a.id < b.id ? 1 : -1; | |
}); | |
return leafs[0].pos + '-' + leafs[0].id; | |
}; | |
// Pretty much all below can be combined into a higher order function to | |
// traverse revisions | |
// The return value from the callback will be passed as context to all | |
// children of that node | |
PouchMerge.traverseRevTree = function (revs, callback) { | |
var toVisit = revs.slice(); | |
var node; | |
while ((node = toVisit.pop())) { | |
var pos = node.pos; | |
var tree = node.ids; | |
var branches = tree[2]; | |
var newCtx = | |
callback(branches.length === 0, pos, tree[0], node.ctx, tree[1]); | |
for (var i = 0, len = branches.length; i < len; i++) { | |
toVisit.push({pos: pos + 1, ids: branches[i], ctx: newCtx}); | |
} | |
} | |
}; | |
PouchMerge.collectLeaves = function (revs) { | |
var leaves = []; | |
PouchMerge.traverseRevTree(revs, function (isLeaf, pos, id, acc, opts) { | |
if (isLeaf) { | |
leaves.push({rev: pos + "-" + id, pos: pos, opts: opts}); | |
} | |
}); | |
leaves.sort(function (a, b) { | |
return b.pos - a.pos; | |
}); | |
leaves.forEach(function (leaf) { delete leaf.pos; }); | |
return leaves; | |
}; | |
// returns revs of all conflicts that is leaves such that | |
// 1. are not deleted and | |
// 2. are different than winning revision | |
PouchMerge.collectConflicts = function (metadata) { | |
var win = PouchMerge.winningRev(metadata); | |
var leaves = PouchMerge.collectLeaves(metadata.rev_tree); | |
var conflicts = []; | |
leaves.forEach(function (leaf) { | |
if (leaf.rev !== win && !leaf.opts.deleted) { | |
conflicts.push(leaf.rev); | |
} | |
}); | |
return conflicts; | |
}; | |
PouchMerge.rootToLeaf = function (tree) { | |
var paths = []; | |
PouchMerge.traverseRevTree(tree, function (isLeaf, pos, id, history, opts) { | |
history = history ? history.slice(0) : []; | |
history.push({id: id, opts: opts}); | |
if (isLeaf) { | |
var rootPos = pos + 1 - history.length; | |
paths.unshift({pos: rootPos, ids: history}); | |
} | |
return history; | |
}); | |
return paths; | |
}; | |
module.exports = PouchMerge; | |
},{"pouchdb-extend":99}],134:[function(require,module,exports){ | |
'use strict'; | |
var utils = require('./utils'); | |
var EE = require('events').EventEmitter; | |
var Checkpointer = require('./checkpointer'); | |
var MAX_SIMULTANEOUS_REVS = 50; | |
var RETRY_DEFAULT = false; | |
function randomNumber(min, max) { | |
min = parseInt(min, 10); | |
max = parseInt(max, 10); | |
if (min !== min) { | |
min = 0; | |
} | |
if (max !== max || max <= min) { | |
max = (min || 1) << 1; //doubling | |
} else { | |
max = max + 1; | |
} | |
var ratio = Math.random(); | |
var range = max - min; | |
return ~~(range * ratio + min); // ~~ coerces to an int, but fast. | |
} | |
function defaultBackOff(min) { | |
var max = 0; | |
if (!min) { | |
max = 2000; | |
} | |
return randomNumber(min, max); | |
} | |
function backOff(repId, src, target, opts, returnValue, result, error) { | |
if (opts.retry === false) { | |
returnValue.emit('error', error); | |
returnValue.removeAllListeners(); | |
return; | |
} | |
opts.default_back_off = opts.default_back_off || 0; | |
opts.retries = opts.retries || 0; | |
if (typeof opts.back_off_function !== 'function') { | |
opts.back_off_function = defaultBackOff; | |
} | |
opts.retries++; | |
if (opts.max_retries && opts.retries > opts.max_retries) { | |
returnValue.emit('error', new Error('tried ' + | |
opts.retries + ' times but replication failed')); | |
returnValue.removeAllListeners(); | |
return; | |
} | |
returnValue.emit('requestError', error); | |
if (returnValue.state === 'active') { | |
returnValue.emit('paused', error); | |
returnValue.state = 'stopped'; | |
returnValue.once('active', function () { | |
opts.current_back_off = opts.default_back_off; | |
}); | |
} | |
opts.current_back_off = opts.current_back_off || opts.default_back_off; | |
opts.current_back_off = opts.back_off_function(opts.current_back_off); | |
setTimeout(function () { | |
replicate(repId, src, target, opts, returnValue); | |
}, opts.current_back_off); | |
} | |
// We create a basic promise so the caller can cancel the replication possibly | |
// before we have actually started listening to changes etc | |
utils.inherits(Replication, EE); | |
function Replication() { | |
EE.call(this); | |
this.cancelled = false; | |
this.state = 'pending'; | |
var self = this; | |
var promise = new utils.Promise(function (fulfill, reject) { | |
self.once('complete', fulfill); | |
self.once('error', reject); | |
}); | |
self.then = function (resolve, reject) { | |
return promise.then(resolve, reject); | |
}; | |
self.catch = function (reject) { | |
return promise.catch(reject); | |
}; | |
// As we allow error handling via "error" event as well, | |
// put a stub in here so that rejecting never throws UnhandledError. | |
self.catch(function () {}); | |
} | |
Replication.prototype.cancel = function () { | |
this.cancelled = true; | |
this.state = 'cancelled'; | |
this.emit('cancel'); | |
}; | |
Replication.prototype.ready = function (src, target) { | |
var self = this; | |
function onDestroy() { | |
self.cancel(); | |
} | |
src.once('destroyed', onDestroy); | |
target.once('destroyed', onDestroy); | |
function cleanup() { | |
src.removeListener('destroyed', onDestroy); | |
target.removeListener('destroyed', onDestroy); | |
} | |
this.then(cleanup, cleanup); | |
}; | |
// TODO: check CouchDB's replication id generation | |
// Generate a unique id particular to this replication | |
function genReplicationId(src, target, opts) { | |
var filterFun = opts.filter ? opts.filter.toString() : ''; | |
return src.id().then(function (src_id) { | |
return target.id().then(function (target_id) { | |
var queryData = src_id + target_id + filterFun + | |
JSON.stringify(opts.query_params) + opts.doc_ids; | |
return utils.MD5(queryData).then(function (md5) { | |
// can't use straight-up md5 alphabet, because | |
// the char '/' is interpreted as being for attachments, | |
// and + is also not url-safe | |
md5 = md5.replace(/\//g, '.').replace(/\+/g, '_'); | |
return '_local/' + md5; | |
}); | |
}); | |
}); | |
} | |
function replicate(repId, src, target, opts, returnValue, result) { | |
var batches = []; // list of batches to be processed | |
var currentBatch; // the batch currently being processed | |
var pendingBatch = { | |
seq: 0, | |
changes: [], | |
docs: [] | |
}; // next batch, not yet ready to be processed | |
var writingCheckpoint = false; // true while checkpoint is being written | |
var changesCompleted = false; // true when all changes received | |
var replicationCompleted = false; // true when replication has completed | |
var last_seq = 0; | |
var continuous = opts.continuous || opts.live || false; | |
var batch_size = opts.batch_size || 100; | |
var batches_limit = opts.batches_limit || 10; | |
var changesPending = false; // true while src.changes is running | |
var doc_ids = opts.doc_ids; | |
var state = { | |
cancelled: false | |
}; | |
var checkpointer = new Checkpointer(src, target, repId, state); | |
var allErrors = []; | |
var changedDocs = []; | |
result = result || { | |
ok: true, | |
start_time: new Date(), | |
docs_read: 0, | |
docs_written: 0, | |
doc_write_failures: 0, | |
errors: [] | |
}; | |
var changesOpts = {}; | |
returnValue.ready(src, target); | |
function writeDocs() { | |
if (currentBatch.docs.length === 0) { | |
return; | |
} | |
var docs = currentBatch.docs; | |
return target.bulkDocs({docs: docs, new_edits: false}).then(function (res) { | |
if (state.cancelled) { | |
completeReplication(); | |
throw new Error('cancelled'); | |
} | |
var errors = []; | |
var errorsById = {}; | |
res.forEach(function (res) { | |
if (res.error) { | |
result.doc_write_failures++; | |
errors.push(res); | |
errorsById[res.id] = res; | |
} | |
}); | |
allErrors = allErrors.concat(errors); | |
result.docs_written += currentBatch.docs.length - errors.length; | |
var non403s = errors.filter(function (error) { | |
return error.name !== 'unauthorized' && error.name !== 'forbidden'; | |
}); | |
changedDocs = []; | |
docs.forEach(function(doc) { | |
var error = errorsById[doc._id]; | |
if (error) { | |
returnValue.emit('denied', utils.clone(error)); | |
} else { | |
changedDocs.push(doc); | |
} | |
}); | |
if (non403s.length > 0) { | |
var error = new Error('bulkDocs error'); | |
error.other_errors = errors; | |
abortReplication('target.bulkDocs failed to write docs', error); | |
throw new Error('bulkWrite partial failure'); | |
} | |
}, function (err) { | |
result.doc_write_failures += docs.length; | |
throw err; | |
}); | |
} | |
function processDiffDoc(id) { | |
var diffs = currentBatch.diffs; | |
var allMissing = diffs[id].missing; | |
// avoid url too long error by batching | |
var missingBatches = []; | |
for (var i = 0; i < allMissing.length; i += MAX_SIMULTANEOUS_REVS) { | |
missingBatches.push(allMissing.slice(i, Math.min(allMissing.length, | |
i + MAX_SIMULTANEOUS_REVS))); | |
} | |
return utils.Promise.all(missingBatches.map(function (missing) { | |
var opts = { | |
revs: true, | |
open_revs: missing, | |
attachments: true | |
}; | |
return src.get(id, opts).then(function (docs) { | |
docs.forEach(function (doc) { | |
if (state.cancelled) { | |
return completeReplication(); | |
} | |
if (doc.ok) { | |
result.docs_read++; | |
currentBatch.pendingRevs++; | |
currentBatch.docs.push(doc.ok); | |
} | |
}); | |
delete diffs[id]; | |
}); | |
})); | |
} | |
function getAllDocs() { | |
var diffKeys = Object.keys(currentBatch.diffs); | |
return utils.Promise.all(diffKeys.map(processDiffDoc)); | |
} | |
function getRevisionOneDocs() { | |
// filter out the generation 1 docs and get them | |
// leaving the non-generation one docs to be got otherwise | |
var ids = Object.keys(currentBatch.diffs).filter(function (id) { | |
var missing = currentBatch.diffs[id].missing; | |
return missing.length === 1 && missing[0].slice(0, 2) === '1-'; | |
}); | |
if (!ids.length) { // nothing to fetch | |
return utils.Promise.resolve(); | |
} | |
return src.allDocs({ | |
keys: ids, | |
include_docs: true | |
}).then(function (res) { | |
if (state.cancelled) { | |
completeReplication(); | |
throw (new Error('cancelled')); | |
} | |
res.rows.forEach(function (row) { | |
if (row.doc && !row.deleted && | |
row.value.rev.slice(0, 2) === '1-' && ( | |
!row.doc._attachments || | |
Object.keys(row.doc._attachments).length === 0 | |
) | |
) { | |
result.docs_read++; | |
currentBatch.pendingRevs++; | |
currentBatch.docs.push(row.doc); | |
delete currentBatch.diffs[row.id]; | |
} | |
}); | |
}); | |
} | |
function getDocs() { | |
return getRevisionOneDocs().then(getAllDocs); | |
} | |
function finishBatch() { | |
writingCheckpoint = true; | |
return checkpointer.writeCheckpoint(currentBatch.seq).then(function () { | |
writingCheckpoint = false; | |
if (state.cancelled) { | |
completeReplication(); | |
throw new Error('cancelled'); | |
} | |
result.last_seq = last_seq = currentBatch.seq; | |
var outResult = utils.clone(result); | |
outResult.docs = changedDocs; | |
returnValue.emit('change', outResult); | |
currentBatch = undefined; | |
getChanges(); | |
}).catch(function (err) { | |
writingCheckpoint = false; | |
abortReplication('writeCheckpoint completed with error', err); | |
throw err; | |
}); | |
} | |
function getDiffs() { | |
var diff = {}; | |
currentBatch.changes.forEach(function (change) { | |
// Couchbase Sync Gateway emits these, but we can ignore them | |
if (change.id === "_user/") { | |
return; | |
} | |
diff[change.id] = change.changes.map(function (x) { | |
return x.rev; | |
}); | |
}); | |
return target.revsDiff(diff).then(function (diffs) { | |
if (state.cancelled) { | |
completeReplication(); | |
throw new Error('cancelled'); | |
} | |
// currentBatch.diffs elements are deleted as the documents are written | |
currentBatch.diffs = diffs; | |
currentBatch.pendingRevs = 0; | |
}); | |
} | |
function startNextBatch() { | |
if (state.cancelled || currentBatch) { | |
return; | |
} | |
if (batches.length === 0) { | |
processPendingBatch(true); | |
return; | |
} | |
currentBatch = batches.shift(); | |
getDiffs() | |
.then(getDocs) | |
.then(writeDocs) | |
.then(finishBatch) | |
.then(startNextBatch) | |
.catch(function (err) { | |
abortReplication('batch processing terminated with error', err); | |
}); | |
} | |
function processPendingBatch(immediate) { | |
if (pendingBatch.changes.length === 0) { | |
if (batches.length === 0 && !currentBatch) { | |
if ((continuous && changesOpts.live) || changesCompleted) { | |
returnValue.state = 'pending'; | |
returnValue.emit('paused'); | |
returnValue.emit('uptodate', result); | |
} | |
if (changesCompleted) { | |
completeReplication(); | |
} | |
} | |
return; | |
} | |
if ( | |
immediate || | |
changesCompleted || | |
pendingBatch.changes.length >= batch_size | |
) { | |
batches.push(pendingBatch); | |
pendingBatch = { | |
seq: 0, | |
changes: [], | |
docs: [] | |
}; | |
if (returnValue.state === 'pending' || returnValue.state === 'stopped') { | |
returnValue.state = 'active'; | |
returnValue.emit('active'); | |
} | |
startNextBatch(); | |
} | |
} | |
function abortReplication(reason, err) { | |
if (replicationCompleted) { | |
return; | |
} | |
if (!err.message) { | |
err.message = reason; | |
} | |
result.ok = false; | |
result.status = 'aborting'; | |
result.errors.push(err); | |
allErrors = allErrors.concat(err); | |
batches = []; | |
pendingBatch = { | |
seq: 0, | |
changes: [], | |
docs: [] | |
}; | |
completeReplication(); | |
} | |
function completeReplication() { | |
if (replicationCompleted) { | |
return; | |
} | |
if (state.cancelled) { | |
result.status = 'cancelled'; | |
if (writingCheckpoint) { | |
return; | |
} | |
} | |
result.status = result.status || 'complete'; | |
result.end_time = new Date(); | |
result.last_seq = last_seq; | |
replicationCompleted = state.cancelled = true; | |
var non403s = allErrors.filter(function (error) { | |
return error.name !== 'unauthorized' && error.name !== 'forbidden'; | |
}); | |
if (non403s.length > 0) { | |
var error = allErrors.pop(); | |
if (allErrors.length > 0) { | |
error.other_errors = allErrors; | |
} | |
error.result = result; | |
backOff(repId, src, target, opts, returnValue, result, error); | |
} else { | |
result.errors = allErrors; | |
returnValue.emit('complete', result); | |
returnValue.removeAllListeners(); | |
} | |
} | |
function onChange(change) { | |
if (state.cancelled) { | |
return completeReplication(); | |
} | |
var filter = utils.filterChange(opts)(change); | |
if (!filter) { | |
return; | |
} | |
if ( | |
pendingBatch.changes.length === 0 && | |
batches.length === 0 && | |
!currentBatch | |
) { | |
returnValue.emit('outofdate', result); | |
} | |
pendingBatch.seq = change.seq; | |
pendingBatch.changes.push(change); | |
processPendingBatch(batches.length === 0); | |
} | |
function onChangesComplete(changes) { | |
changesPending = false; | |
if (state.cancelled) { | |
return completeReplication(); | |
} | |
// if no results were returned then we're done, | |
// else fetch more | |
if (changes.results.length > 0) { | |
changesOpts.since = changes.last_seq; | |
getChanges(); | |
} else { | |
if (continuous) { | |
changesOpts.live = true; | |
getChanges(); | |
} else { | |
changesCompleted = true; | |
} | |
} | |
processPendingBatch(true); | |
} | |
function onChangesError(err) { | |
changesPending = false; | |
if (state.cancelled) { | |
return completeReplication(); | |
} | |
abortReplication('changes rejected', err); | |
} | |
function getChanges() { | |
if (!( | |
!changesPending && | |
!changesCompleted && | |
batches.length < batches_limit | |
)) { | |
return; | |
} | |
changesPending = true; | |
function abortChanges() { | |
changes.cancel(); | |
} | |
function removeListener() { | |
returnValue.removeListener('cancel', abortChanges); | |
} | |
returnValue.once('cancel', abortChanges); | |
var changes = src.changes(changesOpts) | |
.on('change', onChange); | |
changes.then(removeListener, removeListener); | |
changes.then(onChangesComplete) | |
.catch(onChangesError); | |
} | |
function startChanges() { | |
checkpointer.getCheckpoint().then(function (checkpoint) { | |
last_seq = checkpoint; | |
changesOpts = { | |
since: last_seq, | |
limit: batch_size, | |
batch_size: batch_size, | |
style: 'all_docs', | |
doc_ids: doc_ids, | |
returnDocs: true // required so we know when we're done | |
}; | |
if (opts.filter) { | |
if (typeof opts.filter !== 'string') { | |
// required for the client-side filter in onChange | |
changesOpts.include_docs = true; | |
} else { // ddoc filter | |
changesOpts.filter = opts.filter; | |
} | |
} | |
if (opts.query_params) { | |
changesOpts.query_params = opts.query_params; | |
} | |
if (opts.view) { | |
changesOpts.view = opts.view; | |
} | |
getChanges(); | |
}).catch(function (err) { | |
abortReplication('getCheckpoint rejected with ', err); | |
}); | |
} | |
if (returnValue.cancelled) { // cancelled immediately | |
completeReplication(); | |
return; | |
} | |
returnValue.once('cancel', completeReplication); | |
if (typeof opts.onChange === 'function') { | |
returnValue.on('change', opts.onChange); | |
} | |
if (typeof opts.complete === 'function') { | |
returnValue.once('error', opts.complete); | |
returnValue.once('complete', function (result) { | |
opts.complete(null, result); | |
}); | |
} | |
if (typeof opts.since === 'undefined') { | |
startChanges(); | |
} else { | |
writingCheckpoint = true; | |
checkpointer.writeCheckpoint(opts.since).then(function () { | |
writingCheckpoint = false; | |
if (state.cancelled) { | |
completeReplication(); | |
return; | |
} | |
last_seq = opts.since; | |
startChanges(); | |
}).catch(function (err) { | |
writingCheckpoint = false; | |
abortReplication('writeCheckpoint completed with error', err); | |
throw err; | |
}); | |
} | |
} | |
exports.toPouch = toPouch; | |
function toPouch(db, opts) { | |
var PouchConstructor = opts.PouchConstructor; | |
if (typeof db === 'string') { | |
return new PouchConstructor(db, opts); | |
} else if (db.then) { | |
return db; | |
} else { | |
return utils.Promise.resolve(db); | |
} | |
} | |
exports.replicate = replicateWrapper; | |
function replicateWrapper(src, target, opts, callback) { | |
if (typeof opts === 'function') { | |
callback = opts; | |
opts = {}; | |
} | |
if (typeof opts === 'undefined') { | |
opts = {}; | |
} | |
if (!opts.complete) { | |
opts.complete = callback || function () {}; | |
} | |
opts = utils.clone(opts); | |
opts.continuous = opts.continuous || opts.live; | |
opts.retry = ('retry' in opts) ? opts.retry : RETRY_DEFAULT; | |
/*jshint validthis:true */ | |
opts.PouchConstructor = opts.PouchConstructor || this; | |
var replicateRet = new Replication(opts); | |
toPouch(src, opts).then(function (src) { | |
return toPouch(target, opts).then(function (target) { | |
return genReplicationId(src, target, opts).then(function (repId) { | |
replicate(repId, src, target, opts, replicateRet); | |
}); | |
}); | |
}).catch(function (err) { | |
replicateRet.emit('error', err); | |
opts.complete(err); | |
}); | |
return replicateRet; | |
} | |
},{"./checkpointer":115,"./utils":138,"events":45}],135:[function(require,module,exports){ | |
"use strict"; | |
var PouchDB = require("./constructor"); | |
var utils = require('./utils'); | |
var EventEmitter = require('events').EventEmitter; | |
PouchDB.adapters = {}; | |
PouchDB.preferredAdapters = []; | |
PouchDB.prefix = '_pouch_'; | |
var eventEmitter = new EventEmitter(); | |
var eventEmitterMethods = [ | |
'on', | |
'addListener', | |
'emit', | |
'listeners', | |
'once', | |
'removeAllListeners', | |
'removeListener', | |
'setMaxListeners' | |
]; | |
eventEmitterMethods.forEach(function (method) { | |
PouchDB[method] = eventEmitter[method].bind(eventEmitter); | |
}); | |
PouchDB.setMaxListeners(0); | |
PouchDB.parseAdapter = function (name, opts) { | |
var match = name.match(/([a-z\-]*):\/\/(.*)/); | |
var adapter, adapterName; | |
if (match) { | |
// the http adapter expects the fully qualified name | |
name = /http(s?)/.test(match[1]) ? match[1] + '://' + match[2] : match[2]; | |
adapter = match[1]; | |
if (!PouchDB.adapters[adapter].valid()) { | |
throw 'Invalid adapter'; | |
} | |
return {name: name, adapter: match[1]}; | |
} | |
// check for browsers that have been upgraded from websql-only to websql+idb | |
var skipIdb = 'idb' in PouchDB.adapters && 'websql' in PouchDB.adapters && | |
utils.hasLocalStorage() && | |
localStorage['_pouch__websqldb_' + PouchDB.prefix + name]; | |
if (opts.adapter) { | |
adapterName = opts.adapter; | |
} else if (typeof opts !== 'undefined' && opts.db) { | |
adapterName = 'leveldb'; | |
} else { // automatically determine adapter | |
for (var i = 0; i < PouchDB.preferredAdapters.length; ++i) { | |
adapterName = PouchDB.preferredAdapters[i]; | |
if (adapterName in PouchDB.adapters) { | |
if (skipIdb && adapterName === 'idb') { | |
// log it, because this can be confusing during development | |
console.log('PouchDB is downgrading "' + name + '" to WebSQL to' + | |
' avoid data loss, because it was already opened with WebSQL.'); | |
continue; // keep using websql to avoid user data loss | |
} | |
break; | |
} | |
} | |
} | |
adapter = PouchDB.adapters[adapterName]; | |
// if adapter is invalid, then an error will be thrown later | |
var usePrefix = (adapter && 'use_prefix' in adapter) ? | |
adapter.use_prefix : true; | |
return { | |
name: usePrefix ? (PouchDB.prefix + name) : name, | |
adapter: adapterName | |
}; | |
}; | |
PouchDB.destroy = utils.toPromise(function (name, opts, callback) { | |
console.log('PouchDB.destroy() is deprecated and will be removed. ' + | |
'Please use db.destroy() instead.'); | |
if (typeof opts === 'function' || typeof opts === 'undefined') { | |
callback = opts; | |
opts = {}; | |
} | |
if (name && typeof name === 'object') { | |
opts = name; | |
name = undefined; | |
} | |
new PouchDB(name, opts, function (err, db) { | |
if (err) { | |
return callback(err); | |
} | |
db.destroy(callback); | |
}); | |
}); | |
PouchDB.adapter = function (id, obj, addToPreferredAdapters) { | |
if (obj.valid()) { | |
PouchDB.adapters[id] = obj; | |
if (addToPreferredAdapters) { | |
PouchDB.preferredAdapters.push(id); | |
} | |
} | |
}; | |
PouchDB.plugin = function (obj) { | |
Object.keys(obj).forEach(function (id) { | |
PouchDB.prototype[id] = obj[id]; | |
}); | |
}; | |
PouchDB.defaults = function (defaultOpts) { | |
function PouchAlt(name, opts, callback) { | |
if (typeof opts === 'function' || typeof opts === 'undefined') { | |
callback = opts; | |
opts = {}; | |
} | |
if (name && typeof name === 'object') { | |
opts = name; | |
name = undefined; | |
} | |
opts = utils.extend(true, {}, defaultOpts, opts); | |
PouchDB.call(this, name, opts, callback); | |
} | |
utils.inherits(PouchAlt, PouchDB); | |
PouchAlt.destroy = utils.toPromise(function (name, opts, callback) { | |
if (typeof opts === 'function' || typeof opts === 'undefined') { | |
callback = opts; | |
opts = {}; | |
} | |
if (name && typeof name === 'object') { | |
opts = name; | |
name = undefined; | |
} | |
opts = utils.extend(true, {}, defaultOpts, opts); | |
return PouchDB.destroy(name, opts, callback); | |
}); | |
eventEmitterMethods.forEach(function (method) { | |
PouchAlt[method] = eventEmitter[method].bind(eventEmitter); | |
}); | |
PouchAlt.setMaxListeners(0); | |
PouchAlt.preferredAdapters = PouchDB.preferredAdapters.slice(); | |
Object.keys(PouchDB).forEach(function (key) { | |
if (!(key in PouchAlt)) { | |
PouchAlt[key] = PouchDB[key]; | |
} | |
}); | |
return PouchAlt; | |
}; | |
module.exports = PouchDB; | |
},{"./constructor":116,"./utils":138,"events":45}],136:[function(require,module,exports){ | |
'use strict'; | |
var utils = require('./utils'); | |
var replication = require('./replicate'); | |
var replicate = replication.replicate; | |
var EE = require('events').EventEmitter; | |
utils.inherits(Sync, EE); | |
module.exports = sync; | |
function sync(src, target, opts, callback) { | |
if (typeof opts === 'function') { | |
callback = opts; | |
opts = {}; | |
} | |
if (typeof opts === 'undefined') { | |
opts = {}; | |
} | |
opts = utils.clone(opts); | |
/*jshint validthis:true */ | |
opts.PouchConstructor = opts.PouchConstructor || this; | |
src = replication.toPouch(src, opts); | |
target = replication.toPouch(target, opts); | |
return new Sync(src, target, opts, callback); | |
} | |
function Sync(src, target, opts, callback) { | |
var self = this; | |
this.canceled = false; | |
var onChange, complete; | |
if ('onChange' in opts) { | |
onChange = opts.onChange; | |
delete opts.onChange; | |
} | |
if (typeof callback === 'function' && !opts.complete) { | |
complete = callback; | |
} else if ('complete' in opts) { | |
complete = opts.complete; | |
delete opts.complete; | |
} | |
this.push = replicate(src, target, opts); | |
this.pull = replicate(target, src, opts); | |
var emittedCancel = false; | |
function onCancel(data) { | |
if (!emittedCancel) { | |
emittedCancel = true; | |
self.emit('cancel', data); | |
} | |
} | |
function pullChange(change) { | |
self.emit('change', { | |
direction: 'pull', | |
change: change | |
}); | |
} | |
function pushChange(change) { | |
self.emit('change', { | |
direction: 'push', | |
change: change | |
}); | |
} | |
function pushDenied(doc) { | |
self.emit('denied', { | |
direction: 'push', | |
doc: doc | |
}); | |
} | |
function pullDenied(doc) { | |
self.emit('denied', { | |
direction: 'pull', | |
doc: doc | |
}); | |
} | |
var listeners = {}; | |
var removed = {}; | |
function removeAll(type) { // type is 'push' or 'pull' | |
return function (event, func) { | |
var isChange = event === 'change' && | |
(func === pullChange || func === pushChange); | |
var isCancel = event === 'cancel' && func === onCancel; | |
var isOtherEvent = event in listeners && func === listeners[event]; | |
if (isChange || isCancel || isOtherEvent) { | |
if (!(event in removed)) { | |
removed[event] = {}; | |
} | |
removed[event][type] = true; | |
if (Object.keys(removed[event]).length === 2) { | |
// both push and pull have asked to be removed | |
self.removeAllListeners(event); | |
} | |
} | |
}; | |
} | |
if (opts.live) { | |
this.push.on('complete', self.pull.cancel.bind(self.pull)); | |
this.pull.on('complete', self.push.cancel.bind(self.push)); | |
} | |
this.on('newListener', function (event) { | |
if (event === 'change') { | |
self.pull.on('change', pullChange); | |
self.push.on('change', pushChange); | |
} else if (event === 'denied') { | |
self.pull.on('denied', pullDenied); | |
self.push.on('denied', pushDenied); | |
} else if (event === 'cancel') { | |
self.pull.on('cancel', onCancel); | |
self.push.on('cancel', onCancel); | |
} else if (event !== 'error' && | |
event !== 'removeListener' && | |
event !== 'complete' && !(event in listeners)) { | |
listeners[event] = function (e) { | |
self.emit(event, e); | |
}; | |
self.pull.on(event, listeners[event]); | |
self.push.on(event, listeners[event]); | |
} | |
}); | |
this.on('removeListener', function (event) { | |
if (event === 'change') { | |
self.pull.removeListener('change', pullChange); | |
self.push.removeListener('change', pushChange); | |
} else if (event === 'cancel') { | |
self.pull.removeListener('cancel', onCancel); | |
self.push.removeListener('cancel', onCancel); | |
} else if (event in listeners) { | |
if (typeof listeners[event] === 'function') { | |
self.pull.removeListener(event, listeners[event]); | |
self.push.removeListener(event, listeners[event]); | |
delete listeners[event]; | |
} | |
} | |
}); | |
this.pull.on('removeListener', removeAll('pull')); | |
this.push.on('removeListener', removeAll('push')); | |
var promise = utils.Promise.all([ | |
this.push, | |
this.pull | |
]).then(function (resp) { | |
var out = { | |
push: resp[0], | |
pull: resp[1] | |
}; | |
self.emit('complete', out); | |
if (complete) { | |
complete(null, out); | |
} | |
self.removeAllListeners(); | |
return out; | |
}, function (err) { | |
self.cancel(); | |
self.emit('error', err); | |
if (complete) { | |
complete(err); | |
} | |
self.removeAllListeners(); | |
throw err; | |
}); | |
this.then = function (success, err) { | |
return promise.then(success, err); | |
}; | |
this.catch = function (err) { | |
return promise.catch(err); | |
}; | |
} | |
Sync.prototype.cancel = function () { | |
if (!this.canceled) { | |
this.canceled = true; | |
this.push.cancel(); | |
this.pull.cancel(); | |
} | |
}; | |
},{"./replicate":134,"./utils":138,"events":45}],137:[function(require,module,exports){ | |
'use strict'; | |
module.exports = TaskQueue; | |
function TaskQueue() { | |
this.isReady = false; | |
this.failed = false; | |
this.queue = []; | |
} | |
TaskQueue.prototype.execute = function () { | |
var d, func; | |
if (this.failed) { | |
while ((d = this.queue.shift())) { | |
if (typeof d === 'function') { | |
d(this.failed); | |
continue; | |
} | |
func = d.parameters[d.parameters.length - 1]; | |
if (typeof func === 'function') { | |
func(this.failed); | |
} else if (d.name === 'changes' && typeof func.complete === 'function') { | |
func.complete(this.failed); | |
} | |
} | |
} else if (this.isReady) { | |
while ((d = this.queue.shift())) { | |
if (typeof d === 'function') { | |
d(); | |
} else { | |
d.task = this.db[d.name].apply(this.db, d.parameters); | |
} | |
} | |
} | |
}; | |
TaskQueue.prototype.fail = function (err) { | |
this.failed = err; | |
this.execute(); | |
}; | |
TaskQueue.prototype.ready = function (db) { | |
if (this.failed) { | |
return false; | |
} else if (arguments.length === 0) { | |
return this.isReady; | |
} | |
this.isReady = db ? true: false; | |
this.db = db; | |
this.execute(); | |
}; | |
TaskQueue.prototype.addTask = function (name, parameters) { | |
if (typeof name === 'function') { | |
this.queue.push(name); | |
if (this.failed) { | |
this.execute(); | |
} | |
} else { | |
var task = { name: name, parameters: parameters }; | |
this.queue.push(task); | |
if (this.failed) { | |
this.execute(); | |
} | |
return task; | |
} | |
}; | |
},{}],138:[function(require,module,exports){ | |
(function (process){ | |
/*jshint strict: false */ | |
/*global chrome */ | |
var merge = require('./merge'); | |
exports.extend = require('pouchdb-extend'); | |
exports.ajax = require('./deps/ajax'); | |
exports.createBlob = require('./deps/blob'); | |
exports.uuid = require('./deps/uuid'); | |
exports.getArguments = require('argsarray'); | |
var buffer = require('./deps/buffer'); | |
var errors = require('./deps/errors'); | |
var EventEmitter = require('events').EventEmitter; | |
var collections = require('pouchdb-collections'); | |
exports.Map = collections.Map; | |
exports.Set = collections.Set; | |
var parseDoc = require('./deps/parse-doc'); | |
var Promise = require('./deps/promise'); | |
exports.Promise = Promise; | |
exports.lastIndexOf = function (str, char) { | |
for (var i = str.length - 1; i >= 0; i--) { | |
if (str.charAt(i) === char) { | |
return i; | |
} | |
} | |
return -1; | |
}; | |
exports.clone = function (obj) { | |
return exports.extend(true, {}, obj); | |
}; | |
// like underscore/lodash _.pick() | |
exports.pick = function (obj, arr) { | |
var res = {}; | |
for (var i = 0, len = arr.length; i < len; i++) { | |
var prop = arr[i]; | |
res[prop] = obj[prop]; | |
} | |
return res; | |
}; | |
exports.inherits = require('inherits'); | |
function isChromeApp() { | |
return (typeof chrome !== "undefined" && | |
typeof chrome.storage !== "undefined" && | |
typeof chrome.storage.local !== "undefined"); | |
} | |
// Pretty dumb name for a function, just wraps callback calls so we dont | |
// to if (callback) callback() everywhere | |
exports.call = exports.getArguments(function (args) { | |
if (!args.length) { | |
return; | |
} | |
var fun = args.shift(); | |
if (typeof fun === 'function') { | |
fun.apply(this, args); | |
} | |
}); | |
exports.isLocalId = function (id) { | |
return (/^_local/).test(id); | |
}; | |
// check if a specific revision of a doc has been deleted | |
// - metadata: the metadata object from the doc store | |
// - rev: (optional) the revision to check. defaults to winning revision | |
exports.isDeleted = function (metadata, rev) { | |
if (!rev) { | |
rev = merge.winningRev(metadata); | |
} | |
var dashIndex = rev.indexOf('-'); | |
if (dashIndex !== -1) { | |
rev = rev.substring(dashIndex + 1); | |
} | |
var deleted = false; | |
merge.traverseRevTree(metadata.rev_tree, | |
function (isLeaf, pos, id, acc, opts) { | |
if (id === rev) { | |
deleted = !!opts.deleted; | |
} | |
}); | |
return deleted; | |
}; | |
exports.revExists = function (metadata, rev) { | |
var found = false; | |
merge.traverseRevTree(metadata.rev_tree, function (leaf, pos, id) { | |
if ((pos + '-' + id) === rev) { | |
found = true; | |
} | |
}); | |
return found; | |
}; | |
exports.filterChange = function filterChange(opts) { | |
var req = {}; | |
var hasFilter = opts.filter && typeof opts.filter === 'function'; | |
req.query = opts.query_params; | |
return function filter(change) { | |
if (!change.doc) { | |
// CSG sends events on the changes feed that don't have documents, | |
// this hack makes a whole lot of existing code robust. | |
change.doc = {}; | |
} | |
if (opts.filter && hasFilter && !opts.filter.call(this, change.doc, req)) { | |
return false; | |
} | |
if (!opts.include_docs) { | |
delete change.doc; | |
} else if (!opts.attachments) { | |
for (var att in change.doc._attachments) { | |
if (change.doc._attachments.hasOwnProperty(att)) { | |
change.doc._attachments[att].stub = true; | |
} | |
} | |
} | |
return true; | |
}; | |
}; | |
exports.parseDoc = parseDoc.parseDoc; | |
exports.invalidIdError = parseDoc.invalidIdError; | |
exports.isCordova = function () { | |
return (typeof cordova !== "undefined" || | |
typeof PhoneGap !== "undefined" || | |
typeof phonegap !== "undefined"); | |
}; | |
exports.hasLocalStorage = function () { | |
if (isChromeApp()) { | |
return false; | |
} | |
try { | |
return localStorage; | |
} catch (e) { | |
return false; | |
} | |
}; | |
exports.Changes = Changes; | |
exports.inherits(Changes, EventEmitter); | |
function Changes() { | |
if (!(this instanceof Changes)) { | |
return new Changes(); | |
} | |
var self = this; | |
EventEmitter.call(this); | |
this.isChrome = isChromeApp(); | |
this.listeners = {}; | |
this.hasLocal = false; | |
if (!this.isChrome) { | |
this.hasLocal = exports.hasLocalStorage(); | |
} | |
if (this.isChrome) { | |
chrome.storage.onChanged.addListener(function (e) { | |
// make sure it's event addressed to us | |
if (e.db_name != null) { | |
//object only has oldValue, newValue members | |
self.emit(e.dbName.newValue); | |
} | |
}); | |
} else if (this.hasLocal) { | |
if (typeof addEventListener !== 'undefined') { | |
addEventListener("storage", function (e) { | |
self.emit(e.key); | |
}); | |
} else { // old IE | |
window.attachEvent("storage", function (e) { | |
self.emit(e.key); | |
}); | |
} | |
} | |
} | |
Changes.prototype.addListener = function (dbName, id, db, opts) { | |
if (this.listeners[id]) { | |
return; | |
} | |
var self = this; | |
var inprogress = false; | |
function eventFunction() { | |
if (!self.listeners[id]) { | |
return; | |
} | |
if (inprogress) { | |
inprogress = 'waiting'; | |
return; | |
} | |
inprogress = true; | |
db.changes({ | |
style: opts.style, | |
include_docs: opts.include_docs, | |
attachments: opts.attachments, | |
conflicts: opts.conflicts, | |
continuous: false, | |
descending: false, | |
filter: opts.filter, | |
doc_ids: opts.doc_ids, | |
view: opts.view, | |
since: opts.since, | |
query_params: opts.query_params | |
}).on('change', function (c) { | |
if (c.seq > opts.since && !opts.cancelled) { | |
opts.since = c.seq; | |
exports.call(opts.onChange, c); | |
} | |
}).on('complete', function () { | |
if (inprogress === 'waiting') { | |
process.nextTick(function () { | |
self.notify(dbName); | |
}); | |
} | |
inprogress = false; | |
}).on('error', function () { | |
inprogress = false; | |
}); | |
} | |
this.listeners[id] = eventFunction; | |
this.on(dbName, eventFunction); | |
}; | |
Changes.prototype.removeListener = function (dbName, id) { | |
if (!(id in this.listeners)) { | |
return; | |
} | |
EventEmitter.prototype.removeListener.call(this, dbName, | |
this.listeners[id]); | |
}; | |
Changes.prototype.notifyLocalWindows = function (dbName) { | |
//do a useless change on a storage thing | |
//in order to get other windows's listeners to activate | |
if (this.isChrome) { | |
chrome.storage.local.set({dbName: dbName}); | |
} else if (this.hasLocal) { | |
localStorage[dbName] = (localStorage[dbName] === "a") ? "b" : "a"; | |
} | |
}; | |
Changes.prototype.notify = function (dbName) { | |
this.emit(dbName); | |
this.notifyLocalWindows(dbName); | |
}; | |
if (typeof atob === 'function') { | |
exports.atob = function (str) { | |
return atob(str); | |
}; | |
} else { | |
exports.atob = function (str) { | |
var base64 = new buffer(str, 'base64'); | |
// Node.js will just skip the characters it can't encode instead of | |
// throwing and exception | |
if (base64.toString('base64') !== str) { | |
throw ("Cannot base64 encode full string"); | |
} | |
return base64.toString('binary'); | |
}; | |
} | |
if (typeof btoa === 'function') { | |
exports.btoa = function (str) { | |
return btoa(str); | |
}; | |
} else { | |
exports.btoa = function (str) { | |
return new buffer(str, 'binary').toString('base64'); | |
}; | |
} | |
// From http://stackoverflow.com/questions/14967647/ (continues on next line) | |
// encode-decode-image-with-base64-breaks-image (2013-04-21) | |
exports.fixBinary = function (bin) { | |
if (!process.browser) { | |
// don't need to do this in Node | |
return bin; | |
} | |
var length = bin.length; | |
var buf = new ArrayBuffer(length); | |
var arr = new Uint8Array(buf); | |
for (var i = 0; i < length; i++) { | |
arr[i] = bin.charCodeAt(i); | |
} | |
return buf; | |
}; | |
// shim for browsers that don't support it | |
exports.readAsBinaryString = function (blob, callback) { | |
var reader = new FileReader(); | |
var hasBinaryString = typeof reader.readAsBinaryString === 'function'; | |
reader.onloadend = function (e) { | |
var result = e.target.result || ''; | |
if (hasBinaryString) { | |
return callback(result); | |
} | |
callback(exports.arrayBufferToBinaryString(result)); | |
}; | |
if (hasBinaryString) { | |
reader.readAsBinaryString(blob); | |
} else { | |
reader.readAsArrayBuffer(blob); | |
} | |
}; | |
// simplified API. universal browser support is assumed | |
exports.readAsArrayBuffer = function (blob, callback) { | |
var reader = new FileReader(); | |
reader.onloadend = function (e) { | |
var result = e.target.result || new ArrayBuffer(0); | |
callback(result); | |
}; | |
reader.readAsArrayBuffer(blob); | |
}; | |
exports.once = function (fun) { | |
var called = false; | |
return exports.getArguments(function (args) { | |
if (called) { | |
throw new Error('once called more than once'); | |
} else { | |
called = true; | |
fun.apply(this, args); | |
} | |
}); | |
}; | |
exports.toPromise = function (func) { | |
//create the function we will be returning | |
return exports.getArguments(function (args) { | |
var self = this; | |
var tempCB = | |
(typeof args[args.length - 1] === 'function') ? args.pop() : false; | |
// if the last argument is a function, assume its a callback | |
var usedCB; | |
if (tempCB) { | |
// if it was a callback, create a new callback which calls it, | |
// but do so async so we don't trap any errors | |
usedCB = function (err, resp) { | |
process.nextTick(function () { | |
tempCB(err, resp); | |
}); | |
}; | |
} | |
var promise = new Promise(function (fulfill, reject) { | |
var resp; | |
try { | |
var callback = exports.once(function (err, mesg) { | |
if (err) { | |
reject(err); | |
} else { | |
fulfill(mesg); | |
} | |
}); | |
// create a callback for this invocation | |
// apply the function in the orig context | |
args.push(callback); | |
resp = func.apply(self, args); | |
if (resp && typeof resp.then === 'function') { | |
fulfill(resp); | |
} | |
} catch (e) { | |
reject(e); | |
} | |
}); | |
// if there is a callback, call it back | |
if (usedCB) { | |
promise.then(function (result) { | |
usedCB(null, result); | |
}, usedCB); | |
} | |
promise.cancel = function () { | |
return this; | |
}; | |
return promise; | |
}); | |
}; | |
exports.adapterFun = function (name, callback) { | |
var log = require('debug')('pouchdb:api'); | |
function logApiCall(self, name, args) { | |
if (!log.enabled) { | |
return; | |
} | |
var logArgs = [self._db_name, name]; | |
for (var i = 0; i < args.length - 1; i++) { | |
logArgs.push(args[i]); | |
} | |
log.apply(null, logArgs); | |
// override the callback itself to log the response | |
var origCallback = args[args.length - 1]; | |
args[args.length - 1] = function (err, res) { | |
var responseArgs = [self._db_name, name]; | |
responseArgs = responseArgs.concat( | |
err ? ['error', err] : ['success', res] | |
); | |
log.apply(null, responseArgs); | |
origCallback(err, res); | |
}; | |
} | |
return exports.toPromise(exports.getArguments(function (args) { | |
if (this._closed) { | |
return Promise.reject(new Error('database is closed')); | |
} | |
var self = this; | |
logApiCall(self, name, args); | |
if (!this.taskqueue.isReady) { | |
return new Promise(function (fulfill, reject) { | |
self.taskqueue.addTask(function (failed) { | |
if (failed) { | |
reject(failed); | |
} else { | |
fulfill(self[name].apply(self, args)); | |
} | |
}); | |
}); | |
} | |
return callback.apply(this, args); | |
})); | |
}; | |
//Can't find original post, but this is close | |
//http://stackoverflow.com/questions/6965107/ (continues on next line) | |
//converting-between-strings-and-arraybuffers | |
exports.arrayBufferToBinaryString = function (buffer) { | |
var binary = ""; | |
var bytes = new Uint8Array(buffer); | |
var length = bytes.byteLength; | |
for (var i = 0; i < length; i++) { | |
binary += String.fromCharCode(bytes[i]); | |
} | |
return binary; | |
}; | |
exports.cancellableFun = function (fun, self, opts) { | |
opts = opts ? exports.clone(true, {}, opts) : {}; | |
var emitter = new EventEmitter(); | |
var oldComplete = opts.complete || function () { }; | |
var complete = opts.complete = exports.once(function (err, resp) { | |
if (err) { | |
oldComplete(err); | |
} else { | |
emitter.emit('end', resp); | |
oldComplete(null, resp); | |
} | |
emitter.removeAllListeners(); | |
}); | |
var oldOnChange = opts.onChange || function () {}; | |
var lastChange = 0; | |
self.on('destroyed', function () { | |
emitter.removeAllListeners(); | |
}); | |
opts.onChange = function (change) { | |
oldOnChange(change); | |
if (change.seq <= lastChange) { | |
return; | |
} | |
lastChange = change.seq; | |
emitter.emit('change', change); | |
if (change.deleted) { | |
emitter.emit('delete', change); | |
} else if (change.changes.length === 1 && | |
change.changes[0].rev.slice(0, 1) === '1-') { | |
emitter.emit('create', change); | |
} else { | |
emitter.emit('update', change); | |
} | |
}; | |
var promise = new Promise(function (fulfill, reject) { | |
opts.complete = function (err, res) { | |
if (err) { | |
reject(err); | |
} else { | |
fulfill(res); | |
} | |
}; | |
}); | |
promise.then(function (result) { | |
complete(null, result); | |
}, complete); | |
// this needs to be overwridden by caller, dont fire complete until | |
// the task is ready | |
promise.cancel = function () { | |
promise.isCancelled = true; | |
if (self.taskqueue.isReady) { | |
opts.complete(null, {status: 'cancelled'}); | |
} | |
}; | |
if (!self.taskqueue.isReady) { | |
self.taskqueue.addTask(function () { | |
if (promise.isCancelled) { | |
opts.complete(null, {status: 'cancelled'}); | |
} else { | |
fun(self, opts, promise); | |
} | |
}); | |
} else { | |
fun(self, opts, promise); | |
} | |
promise.on = emitter.on.bind(emitter); | |
promise.once = emitter.once.bind(emitter); | |
promise.addListener = emitter.addListener.bind(emitter); | |
promise.removeListener = emitter.removeListener.bind(emitter); | |
promise.removeAllListeners = emitter.removeAllListeners.bind(emitter); | |
promise.setMaxListeners = emitter.setMaxListeners.bind(emitter); | |
promise.listeners = emitter.listeners.bind(emitter); | |
promise.emit = emitter.emit.bind(emitter); | |
return promise; | |
}; | |
exports.MD5 = exports.toPromise(require('./deps/md5')); | |
exports.explain404 = require('./deps/explain404'); | |
exports.info = function (str) { | |
if (typeof console !== 'undefined' && 'info' in console) { | |
console.info(str); | |
} | |
}; | |
exports.parseUri = require('./deps/parse-uri'); | |
exports.compare = function (left, right) { | |
return left < right ? -1 : left > right ? 1 : 0; | |
}; | |
exports.updateDoc = function updateDoc(prev, docInfo, results, | |
i, cb, writeDoc, newEdits) { | |
if (exports.revExists(prev, docInfo.metadata.rev)) { | |
results[i] = docInfo; | |
return cb(); | |
} | |
// TODO: some of these can be pre-calculated, but it's safer to just | |
// call merge.winningRev() and exports.isDeleted() all over again | |
var previousWinningRev = merge.winningRev(prev); | |
var previouslyDeleted = exports.isDeleted(prev, previousWinningRev); | |
var deleted = exports.isDeleted(docInfo.metadata); | |
var isRoot = /^1-/.test(docInfo.metadata.rev); | |
if (previouslyDeleted && !deleted && newEdits && isRoot) { | |
var newDoc = docInfo.data; | |
newDoc._rev = previousWinningRev; | |
newDoc._id = docInfo.metadata.id; | |
docInfo = exports.parseDoc(newDoc, newEdits); | |
} | |
var merged = merge.merge(prev.rev_tree, docInfo.metadata.rev_tree[0], 1000); | |
var inConflict = newEdits && (((previouslyDeleted && deleted) || | |
(!previouslyDeleted && merged.conflicts !== 'new_leaf') || | |
(previouslyDeleted && !deleted && merged.conflicts === 'new_branch'))); | |
if (inConflict) { | |
var err = errors.error(errors.REV_CONFLICT); | |
results[i] = err; | |
return cb(); | |
} | |
var newRev = docInfo.metadata.rev; | |
docInfo.metadata.rev_tree = merged.tree; | |
if (prev.rev_map) { | |
docInfo.metadata.rev_map = prev.rev_map; // used by leveldb | |
} | |
// recalculate | |
var winningRev = merge.winningRev(docInfo.metadata); | |
var winningRevIsDeleted = exports.isDeleted(docInfo.metadata, winningRev); | |
// calculate the total number of documents that were added/removed, | |
// from the perspective of total_rows/doc_count | |
var delta = (previouslyDeleted === winningRevIsDeleted) ? 0 : | |
previouslyDeleted < winningRevIsDeleted ? -1 : 1; | |
var newRevIsDeleted = exports.isDeleted(docInfo.metadata, newRev); | |
writeDoc(docInfo, winningRev, winningRevIsDeleted, newRevIsDeleted, | |
true, delta, i, cb); | |
}; | |
exports.processDocs = function processDocs(docInfos, api, fetchedDocs, | |
tx, results, writeDoc, opts, | |
overallCallback) { | |
if (!docInfos.length) { | |
return; | |
} | |
function insertDoc(docInfo, resultsIdx, callback) { | |
// Cant insert new deleted documents | |
var winningRev = merge.winningRev(docInfo.metadata); | |
var deleted = exports.isDeleted(docInfo.metadata, winningRev); | |
if ('was_delete' in opts && deleted) { | |
results[resultsIdx] = errors.error(errors.MISSING_DOC, 'deleted'); | |
return callback(); | |
} | |
var delta = deleted ? 0 : 1; | |
writeDoc(docInfo, winningRev, deleted, deleted, false, | |
delta, resultsIdx, callback); | |
} | |
var newEdits = opts.new_edits; | |
var idsToDocs = new exports.Map(); | |
var docsDone = 0; | |
var docsToDo = docInfos.length; | |
function checkAllDocsDone() { | |
if (++docsDone === docsToDo && overallCallback) { | |
overallCallback(); | |
} | |
} | |
docInfos.forEach(function (currentDoc, resultsIdx) { | |
if (currentDoc._id && exports.isLocalId(currentDoc._id)) { | |
api[currentDoc._deleted ? '_removeLocal' : '_putLocal']( | |
currentDoc, {ctx: tx}, function (err) { | |
if (err) { | |
results[resultsIdx] = err; | |
} else { | |
results[resultsIdx] = {ok: true}; | |
} | |
checkAllDocsDone(); | |
}); | |
return; | |
} | |
var id = currentDoc.metadata.id; | |
if (idsToDocs.has(id)) { | |
docsToDo--; // duplicate | |
idsToDocs.get(id).push([currentDoc, resultsIdx]); | |
} else { | |
idsToDocs.set(id, [[currentDoc, resultsIdx]]); | |
} | |
}); | |
// in the case of new_edits, the user can provide multiple docs | |
// with the same id. these need to be processed sequentially | |
idsToDocs.forEach(function (docs, id) { | |
var numDone = 0; | |
function docWritten() { | |
if (++numDone < docs.length) { | |
nextDoc(); | |
} else { | |
checkAllDocsDone(); | |
} | |
} | |
function nextDoc() { | |
var value = docs[numDone]; | |
var currentDoc = value[0]; | |
var resultsIdx = value[1]; | |
if (fetchedDocs.has(id)) { | |
exports.updateDoc(fetchedDocs.get(id), currentDoc, results, | |
resultsIdx, docWritten, writeDoc, newEdits); | |
} else { | |
insertDoc(currentDoc, resultsIdx, docWritten); | |
} | |
} | |
nextDoc(); | |
}); | |
}; | |
exports.preprocessAttachments = function preprocessAttachments( | |
docInfos, blobType, callback) { | |
if (!docInfos.length) { | |
return callback(); | |
} | |
var docv = 0; | |
function parseBase64(data) { | |
try { | |
return exports.atob(data); | |
} catch (e) { | |
var err = errors.error(errors.BAD_ARG, | |
'Attachments need to be base64 encoded'); | |
return {error: err}; | |
} | |
} | |
function preprocessAttachment(att, callback) { | |
if (att.stub) { | |
return callback(); | |
} | |
if (typeof att.data === 'string') { | |
// input is a base64 string | |
var asBinary = parseBase64(att.data); | |
if (asBinary.error) { | |
return callback(asBinary.error); | |
} | |
att.length = asBinary.length; | |
if (blobType === 'blob') { | |
att.data = exports.createBlob([exports.fixBinary(asBinary)], | |
{type: att.content_type}); | |
} else if (blobType === 'base64') { | |
att.data = exports.btoa(asBinary); | |
} else { // binary | |
att.data = asBinary; | |
} | |
exports.MD5(asBinary).then(function (result) { | |
att.digest = 'md5-' + result; | |
callback(); | |
}); | |
} else { // input is a blob | |
exports.readAsArrayBuffer(att.data, function (buff) { | |
if (blobType === 'binary') { | |
att.data = exports.arrayBufferToBinaryString(buff); | |
} else if (blobType === 'base64') { | |
att.data = exports.btoa(exports.arrayBufferToBinaryString(buff)); | |
} | |
exports.MD5(buff).then(function (result) { | |
att.digest = 'md5-' + result; | |
att.length = buff.byteLength; | |
callback(); | |
}); | |
}); | |
} | |
} | |
var overallErr; | |
docInfos.forEach(function (docInfo) { | |
var attachments = docInfo.data && docInfo.data._attachments ? | |
Object.keys(docInfo.data._attachments) : []; | |
var recv = 0; | |
if (!attachments.length) { | |
return done(); | |
} | |
function processedAttachment(err) { | |
overallErr = err; | |
recv++; | |
if (recv === attachments.length) { | |
done(); | |
} | |
} | |
for (var key in docInfo.data._attachments) { | |
if (docInfo.data._attachments.hasOwnProperty(key)) { | |
preprocessAttachment(docInfo.data._attachments[key], | |
processedAttachment); | |
} | |
} | |
}); | |
function done() { | |
docv++; | |
if (docInfos.length === docv) { | |
if (overallErr) { | |
callback(overallErr); | |
} else { | |
callback(); | |
} | |
} | |
} | |
}; | |
// compact a tree by marking its non-leafs as missing, | |
// and return a list of revs to delete | |
exports.compactTree = function compactTree(metadata) { | |
var revs = []; | |
merge.traverseRevTree(metadata.rev_tree, function (isLeaf, pos, | |
revHash, ctx, opts) { | |
if (opts.status === 'available' && !isLeaf) { | |
revs.push(pos + '-' + revHash); | |
opts.status = 'missing'; | |
} | |
}); | |
return revs; | |
}; | |
var vuvuzela = require('vuvuzela'); | |
exports.safeJsonParse = function safeJsonParse(str) { | |
try { | |
return JSON.parse(str); | |
} catch (e) { | |
return vuvuzela.parse(str); | |
} | |
}; | |
exports.safeJsonStringify = function safeJsonStringify(json) { | |
try { | |
return JSON.stringify(json); | |
} catch (e) { | |
return vuvuzela.stringify(json); | |
} | |
}; | |
}).call(this,require("/Users/nlawson/workspace-node/pouchdb-authentication/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js")) | |
},{"./deps/ajax":117,"./deps/blob":118,"./deps/buffer":119,"./deps/errors":120,"./deps/explain404":121,"./deps/md5":122,"./deps/parse-doc":123,"./deps/parse-uri":125,"./deps/promise":126,"./deps/uuid":129,"./merge":133,"/Users/nlawson/workspace-node/pouchdb-authentication/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":46,"argsarray":140,"debug":141,"events":45,"inherits":79,"pouchdb-collections":146,"pouchdb-extend":99,"vuvuzela":155}],139:[function(require,module,exports){ | |
module.exports = "3.5.0"; | |
},{}],140:[function(require,module,exports){ | |
'use strict'; | |
module.exports = argsArray; | |
function argsArray(fun) { | |
return function () { | |
var len = arguments.length; | |
if (len) { | |
var args = []; | |
var i = -1; | |
while (++i < len) { | |
args[i] = arguments[i]; | |
} | |
return fun.call(this, args); | |
} else { | |
return fun.call(this, []); | |
} | |
}; | |
} | |
},{}],141:[function(require,module,exports){ | |
/** | |
* This is the web browser implementation of `debug()`. | |
* | |
* Expose `debug()` as the module. | |
*/ | |
exports = module.exports = require('./debug'); | |
exports.log = log; | |
exports.formatArgs = formatArgs; | |
exports.save = save; | |
exports.load = load; | |
exports.useColors = useColors; | |
exports.storage = 'undefined' != typeof chrome | |
&& 'undefined' != typeof chrome.storage | |
? chrome.storage.local | |
: localstorage(); | |
/** | |
* Colors. | |
*/ | |
exports.colors = [ | |
'lightseagreen', | |
'forestgreen', | |
'goldenrod', | |
'dodgerblue', | |
'darkorchid', | |
'crimson' | |
]; | |
/** | |
* Currently only WebKit-based Web Inspectors, Firefox >= v31, | |
* and the Firebug extension (any Firefox version) are known | |
* to support "%c" CSS customizations. | |
* | |
* TODO: add a `localStorage` variable to explicitly enable/disable colors | |
*/ | |
function useColors() { | |
// is webkit? http://stackoverflow.com/a/16459606/376773 | |
return ('WebkitAppearance' in document.documentElement.style) || | |
// is firebug? http://stackoverflow.com/a/398120/376773 | |
(window.console && (console.firebug || (console.exception && console.table))) || | |
// is firefox >= v31? | |
// https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages | |
(navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31); | |
} | |
/** | |
* Map %j to `JSON.stringify()`, since no Web Inspectors do that by default. | |
*/ | |
exports.formatters.j = function(v) { | |
return JSON.stringify(v); | |
}; | |
/** | |
* Colorize log arguments if enabled. | |
* | |
* @api public | |
*/ | |
function formatArgs() { | |
var args = arguments; | |
var useColors = this.useColors; | |
args[0] = (useColors ? '%c' : '') | |
+ this.namespace | |
+ (useColors ? ' %c' : ' ') | |
+ args[0] | |
+ (useColors ? '%c ' : ' ') | |
+ '+' + exports.humanize(this.diff); | |
if (!useColors) return args; | |
var c = 'color: ' + this.color; | |
args = [args[0], c, 'color: inherit'].concat(Array.prototype.slice.call(args, 1)); | |
// the final "%c" is somewhat tricky, because there could be other | |
// arguments passed either before or after the %c, so we need to | |
// figure out the correct index to insert the CSS into | |
var index = 0; | |
var lastC = 0; | |
args[0].replace(/%[a-z%]/g, function(match) { | |
if ('%%' === match) return; | |
index++; | |
if ('%c' === match) { | |
// we only are interested in the *last* %c | |
// (the user may have provided their own) | |
lastC = index; | |
} | |
}); | |
args.splice(lastC, 0, c); | |
return args; | |
} | |
/** | |
* Invokes `console.log()` when available. | |
* No-op when `console.log` is not a "function". | |
* | |
* @api public | |
*/ | |
function log() { | |
// this hackery is required for IE8/9, where | |
// the `console.log` function doesn't have 'apply' | |
return 'object' === typeof console | |
&& console.log | |
&& Function.prototype.apply.call(console.log, console, arguments); | |
} | |
/** | |
* Save `namespaces`. | |
* | |
* @param {String} namespaces | |
* @api private | |
*/ | |
function save(namespaces) { | |
try { | |
if (null == namespaces) { | |
exports.storage.removeItem('debug'); | |
} else { | |
exports.storage.debug = namespaces; | |
} | |
} catch(e) {} | |
} | |
/** | |
* Load `namespaces`. | |
* | |
* @return {String} returns the previously persisted debug modes | |
* @api private | |
*/ | |
function load() { | |
var r; | |
try { | |
r = exports.storage.debug; | |
} catch(e) {} | |
return r; | |
} | |
/** | |
* Enable namespaces listed in `localStorage.debug` initially. | |
*/ | |
exports.enable(load()); | |
/** | |
* Localstorage attempts to return the localstorage. | |
* | |
* This is necessary because safari throws | |
* when a user disables cookies/localstorage | |
* and you attempt to access it. | |
* | |
* @return {LocalStorage} | |
* @api private | |
*/ | |
function localstorage(){ | |
try { | |
return window.localStorage; | |
} catch (e) {} | |
} | |
},{"./debug":142}],142:[function(require,module,exports){ | |
/** | |
* This is the common logic for both the Node.js and web browser | |
* implementations of `debug()`. | |
* | |
* Expose `debug()` as the module. | |
*/ | |
exports = module.exports = debug; | |
exports.coerce = coerce; | |
exports.disable = disable; | |
exports.enable = enable; | |
exports.enabled = enabled; | |
exports.humanize = require('ms'); | |
/** | |
* The currently active debug mode names, and names to skip. | |
*/ | |
exports.names = []; | |
exports.skips = []; | |
/** | |
* Map of special "%n" handling functions, for the debug "format" argument. | |
* | |
* Valid key names are a single, lowercased letter, i.e. "n". | |
*/ | |
exports.formatters = {}; | |
/** | |
* Previously assigned color. | |
*/ | |
var prevColor = 0; | |
/** | |
* Previous log timestamp. | |
*/ | |
var prevTime; | |
/** | |
* Select a color. | |
* | |
* @return {Number} | |
* @api private | |
*/ | |
function selectColor() { | |
return exports.colors[prevColor++ % exports.colors.length]; | |
} | |
/** | |
* Create a debugger with the given `namespace`. | |
* | |
* @param {String} namespace | |
* @return {Function} | |
* @api public | |
*/ | |
function debug(namespace) { | |
// define the `disabled` version | |
function disabled() { | |
} | |
disabled.enabled = false; | |
// define the `enabled` version | |
function enabled() { | |
var self = enabled; | |
// set `diff` timestamp | |
var curr = +new Date(); | |
var ms = curr - (prevTime || curr); | |
self.diff = ms; | |
self.prev = prevTime; | |
self.curr = curr; | |
prevTime = curr; | |
// add the `color` if not set | |
if (null == self.useColors) self.useColors = exports.useColors(); | |
if (null == self.color && self.useColors) self.color = selectColor(); | |
var args = Array.prototype.slice.call(arguments); | |
args[0] = exports.coerce(args[0]); | |
if ('string' !== typeof args[0]) { | |
// anything else let's inspect with %o | |
args = ['%o'].concat(args); | |
} | |
// apply any `formatters` transformations | |
var index = 0; | |
args[0] = args[0].replace(/%([a-z%])/g, function(match, format) { | |
// if we encounter an escaped % then don't increase the array index | |
if (match === '%%') return match; | |
index++; | |
var formatter = exports.formatters[format]; | |
if ('function' === typeof formatter) { | |
var val = args[index]; | |
match = formatter.call(self, val); | |
// now we need to remove `args[index]` since it's inlined in the `format` | |
args.splice(index, 1); | |
index--; | |
} | |
return match; | |
}); | |
if ('function' === typeof exports.formatArgs) { | |
args = exports.formatArgs.apply(self, args); | |
} | |
var logFn = enabled.log || exports.log || console.log.bind(console); | |
logFn.apply(self, args); | |
} | |
enabled.enabled = true; | |
var fn = exports.enabled(namespace) ? enabled : disabled; | |
fn.namespace = namespace; | |
return fn; | |
} | |
/** | |
* Enables a debug mode by namespaces. This can include modes | |
* separated by a colon and wildcards. | |
* | |
* @param {String} namespaces | |
* @api public | |
*/ | |
function enable(namespaces) { | |
exports.save(namespaces); | |
var split = (namespaces || '').split(/[\s,]+/); | |
var len = split.length; | |
for (var i = 0; i < len; i++) { | |
if (!split[i]) continue; // ignore empty strings | |
namespaces = split[i].replace(/\*/g, '.*?'); | |
if (namespaces[0] === '-') { | |
exports.skips.push(new RegExp('^' + namespaces.substr(1) + '$')); | |
} else { | |
exports.names.push(new RegExp('^' + namespaces + '$')); | |
} | |
} | |
} | |
/** | |
* Disable debug output. | |
* | |
* @api public | |
*/ | |
function disable() { | |
exports.enable(''); | |
} | |
/** | |
* Returns true if the given mode name is enabled, false otherwise. | |
* | |
* @param {String} name | |
* @return {Boolean} | |
* @api public | |
*/ | |
function enabled(name) { | |
var i, len; | |
for (i = 0, len = exports.skips.length; i < len; i++) { | |
if (exports.skips[i].test(name)) { | |
return false; | |
} | |
} | |
for (i = 0, len = exports.names.length; i < len; i++) { | |
if (exports.names[i].test(name)) { | |
return true; | |
} | |
} | |
return false; | |
} | |
/** | |
* Coerce `val`. | |
* | |
* @param {Mixed} val | |
* @return {Mixed} | |
* @api private | |
*/ | |
function coerce(val) { | |
if (val instanceof Error) return val.stack || val.message; | |
return val; | |
} | |
},{"ms":143}],143:[function(require,module,exports){ | |
/** | |
* Helpers. | |
*/ | |
var s = 1000; | |
var m = s * 60; | |
var h = m * 60; | |
var d = h * 24; | |
var y = d * 365.25; | |
/** | |
* Parse or format the given `val`. | |
* | |
* Options: | |
* | |
* - `long` verbose formatting [false] | |
* | |
* @param {String|Number} val | |
* @param {Object} options | |
* @return {String|Number} | |
* @api public | |
*/ | |
module.exports = function(val, options){ | |
options = options || {}; | |
if ('string' == typeof val) return parse(val); | |
return options.long | |
? long(val) | |
: short(val); | |
}; | |
/** | |
* Parse the given `str` and return milliseconds. | |
* | |
* @param {String} str | |
* @return {Number} | |
* @api private | |
*/ | |
function parse(str) { | |
str = '' + str; | |
if (str.length > 10000) return; | |
var match = /^((?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec(str); | |
if (!match) return; | |
var n = parseFloat(match[1]); | |
var type = (match[2] || 'ms').toLowerCase(); | |
switch (type) { | |
case 'years': | |
case 'year': | |
case 'yrs': | |
case 'yr': | |
case 'y': | |
return n * y; | |
case 'days': | |
case 'day': | |
case 'd': | |
return n * d; | |
case 'hours': | |
case 'hour': | |
case 'hrs': | |
case 'hr': | |
case 'h': | |
return n * h; | |
case 'minutes': | |
case 'minute': | |
case 'mins': | |
case 'min': | |
case 'm': | |
return n * m; | |
case 'seconds': | |
case 'second': | |
case 'secs': | |
case 'sec': | |
case 's': | |
return n * s; | |
case 'milliseconds': | |
case 'millisecond': | |
case 'msecs': | |
case 'msec': | |
case 'ms': | |
return n; | |
} | |
} | |
/** | |
* Short format for `ms`. | |
* | |
* @param {Number} ms | |
* @return {String} | |
* @api private | |
*/ | |
function short(ms) { | |
if (ms >= d) return Math.round(ms / d) + 'd'; | |
if (ms >= h) return Math.round(ms / h) + 'h'; | |
if (ms >= m) return Math.round(ms / m) + 'm'; | |
if (ms >= s) return Math.round(ms / s) + 's'; | |
return ms + 'ms'; | |
} | |
/** | |
* Long format for `ms`. | |
* | |
* @param {Number} ms | |
* @return {String} | |
* @api private | |
*/ | |
function long(ms) { | |
return plural(ms, d, 'day') | |
|| plural(ms, h, 'hour') | |
|| plural(ms, m, 'minute') | |
|| plural(ms, s, 'second') | |
|| ms + ' ms'; | |
} | |
/** | |
* Pluralization helper. | |
*/ | |
function plural(ms, n, name) { | |
if (ms < n) return; | |
if (ms < n * 1.5) return Math.floor(ms / n) + ' ' + name; | |
return Math.ceil(ms / n) + ' ' + name + 's'; | |
} | |
},{}],144:[function(require,module,exports){ | |
'use strict'; | |
var MIN_MAGNITUDE = -324; // verified by -Number.MIN_VALUE | |
var MAGNITUDE_DIGITS = 3; // ditto | |
var SEP = ''; // set to '_' for easier debugging | |
var utils = require('./utils'); | |
exports.collate = function (a, b) { | |
if (a === b) { | |
return 0; | |
} | |
a = exports.normalizeKey(a); | |
b = exports.normalizeKey(b); | |
var ai = collationIndex(a); | |
var bi = collationIndex(b); | |
if ((ai - bi) !== 0) { | |
return ai - bi; | |
} | |
if (a === null) { | |
return 0; | |
} | |
switch (typeof a) { | |
case 'number': | |
return a - b; | |
case 'boolean': | |
return a === b ? 0 : (a < b ? -1 : 1); | |
case 'string': | |
return stringCollate(a, b); | |
} | |
return Array.isArray(a) ? arrayCollate(a, b) : objectCollate(a, b); | |
}; | |
// couch considers null/NaN/Infinity/-Infinity === undefined, | |
// for the purposes of mapreduce indexes. also, dates get stringified. | |
exports.normalizeKey = function (key) { | |
switch (typeof key) { | |
case 'undefined': | |
return null; | |
case 'number': | |
if (key === Infinity || key === -Infinity || isNaN(key)) { | |
return null; | |
} | |
return key; | |
case 'object': | |
var origKey = key; | |
if (Array.isArray(key)) { | |
var len = key.length; | |
key = new Array(len); | |
for (var i = 0; i < len; i++) { | |
key[i] = exports.normalizeKey(origKey[i]); | |
} | |
} else if (key instanceof Date) { | |
return key.toJSON(); | |
} else if (key !== null) { // generic object | |
key = {}; | |
for (var k in origKey) { | |
if (origKey.hasOwnProperty(k)) { | |
var val = origKey[k]; | |
if (typeof val !== 'undefined') { | |
key[k] = exports.normalizeKey(val); | |
} | |
} | |
} | |
} | |
} | |
return key; | |
}; | |
function indexify(key) { | |
if (key !== null) { | |
switch (typeof key) { | |
case 'boolean': | |
return key ? 1 : 0; | |
case 'number': | |
return numToIndexableString(key); | |
case 'string': | |
// We've to be sure that key does not contain \u0000 | |
// Do order-preserving replacements: | |
// 0 -> 1, 1 | |
// 1 -> 1, 2 | |
// 2 -> 2, 2 | |
return key | |
.replace(/\u0002/g, '\u0002\u0002') | |
.replace(/\u0001/g, '\u0001\u0002') | |
.replace(/\u0000/g, '\u0001\u0001'); | |
case 'object': | |
var isArray = Array.isArray(key); | |
var arr = isArray ? key : Object.keys(key); | |
var i = -1; | |
var len = arr.length; | |
var result = ''; | |
if (isArray) { | |
while (++i < len) { | |
result += exports.toIndexableString(arr[i]); | |
} | |
} else { | |
while (++i < len) { | |
var objKey = arr[i]; | |
result += exports.toIndexableString(objKey) + | |
exports.toIndexableString(key[objKey]); | |
} | |
} | |
return result; | |
} | |
} | |
return ''; | |
} | |
// convert the given key to a string that would be appropriate | |
// for lexical sorting, e.g. within a database, where the | |
// sorting is the same given by the collate() function. | |
exports.toIndexableString = function (key) { | |
var zero = '\u0000'; | |
key = exports.normalizeKey(key); | |
return collationIndex(key) + SEP + indexify(key) + zero; | |
}; | |
function parseNumber(str, i) { | |
var originalIdx = i; | |
var num; | |
var zero = str[i] === '1'; | |
if (zero) { | |
num = 0; | |
i++; | |
} else { | |
var neg = str[i] === '0'; | |
i++; | |
var numAsString = ''; | |
var magAsString = str.substring(i, i + MAGNITUDE_DIGITS); | |
var magnitude = parseInt(magAsString, 10) + MIN_MAGNITUDE; | |
if (neg) { | |
magnitude = -magnitude; | |
} | |
i += MAGNITUDE_DIGITS; | |
while (true) { | |
var ch = str[i]; | |
if (ch === '\u0000') { | |
break; | |
} else { | |
numAsString += ch; | |
} | |
i++; | |
} | |
numAsString = numAsString.split('.'); | |
if (numAsString.length === 1) { | |
num = parseInt(numAsString, 10); | |
} else { | |
num = parseFloat(numAsString[0] + '.' + numAsString[1]); | |
} | |
if (neg) { | |
num = num - 10; | |
} | |
if (magnitude !== 0) { | |
// parseFloat is more reliable than pow due to rounding errors | |
// e.g. Number.MAX_VALUE would return Infinity if we did | |
// num * Math.pow(10, magnitude); | |
num = parseFloat(num + 'e' + magnitude); | |
} | |
} | |
return {num: num, length : i - originalIdx}; | |
} | |
// move up the stack while parsing | |
// this function moved outside of parseIndexableString for performance | |
function pop(stack, metaStack) { | |
var obj = stack.pop(); | |
if (metaStack.length) { | |
var lastMetaElement = metaStack[metaStack.length - 1]; | |
if (obj === lastMetaElement.element) { | |
// popping a meta-element, e.g. an object whose value is another object | |
metaStack.pop(); | |
lastMetaElement = metaStack[metaStack.length - 1]; | |
} | |
var element = lastMetaElement.element; | |
var lastElementIndex = lastMetaElement.index; | |
if (Array.isArray(element)) { | |
element.push(obj); | |
} else if (lastElementIndex === stack.length - 2) { // obj with key+value | |
var key = stack.pop(); | |
element[key] = obj; | |
} else { | |
stack.push(obj); // obj with key only | |
} | |
} | |
} | |
exports.parseIndexableString = function (str) { | |
var stack = []; | |
var metaStack = []; // stack for arrays and objects | |
var i = 0; | |
while (true) { | |
var collationIndex = str[i++]; | |
if (collationIndex === '\u0000') { | |
if (stack.length === 1) { | |
return stack.pop(); | |
} else { | |
pop(stack, metaStack); | |
continue; | |
} | |
} | |
switch (collationIndex) { | |
case '1': | |
stack.push(null); | |
break; | |
case '2': | |
stack.push(str[i] === '1'); | |
i++; | |
break; | |
case '3': | |
var parsedNum = parseNumber(str, i); | |
stack.push(parsedNum.num); | |
i += parsedNum.length; | |
break; | |
case '4': | |
var parsedStr = ''; | |
while (true) { | |
var ch = str[i]; | |
if (ch === '\u0000') { | |
break; | |
} | |
parsedStr += ch; | |
i++; | |
} | |
// perform the reverse of the order-preserving replacement | |
// algorithm (see above) | |
parsedStr = parsedStr.replace(/\u0001\u0001/g, '\u0000') | |
.replace(/\u0001\u0002/g, '\u0001') | |
.replace(/\u0002\u0002/g, '\u0002'); | |
stack.push(parsedStr); | |
break; | |
case '5': | |
var arrayElement = { element: [], index: stack.length }; | |
stack.push(arrayElement.element); | |
metaStack.push(arrayElement); | |
break; | |
case '6': | |
var objElement = { element: {}, index: stack.length }; | |
stack.push(objElement.element); | |
metaStack.push(objElement); | |
break; | |
default: | |
throw new Error( | |
'bad collationIndex or unexpectedly reached end of input: ' + collationIndex); | |
} | |
} | |
}; | |
function arrayCollate(a, b) { | |
var len = Math.min(a.length, b.length); | |
for (var i = 0; i < len; i++) { | |
var sort = exports.collate(a[i], b[i]); | |
if (sort !== 0) { | |
return sort; | |
} | |
} | |
return (a.length === b.length) ? 0 : | |
(a.length > b.length) ? 1 : -1; | |
} | |
function stringCollate(a, b) { | |
// See: https://github.com/daleharvey/pouchdb/issues/40 | |
// This is incompatible with the CouchDB implementation, but its the | |
// best we can do for now | |
return (a === b) ? 0 : ((a > b) ? 1 : -1); | |
} | |
function objectCollate(a, b) { | |
var ak = Object.keys(a), bk = Object.keys(b); | |
var len = Math.min(ak.length, bk.length); | |
for (var i = 0; i < len; i++) { | |
// First sort the keys | |
var sort = exports.collate(ak[i], bk[i]); | |
if (sort !== 0) { | |
return sort; | |
} | |
// if the keys are equal sort the values | |
sort = exports.collate(a[ak[i]], b[bk[i]]); | |
if (sort !== 0) { | |
return sort; | |
} | |
} | |
return (ak.length === bk.length) ? 0 : | |
(ak.length > bk.length) ? 1 : -1; | |
} | |
// The collation is defined by erlangs ordered terms | |
// the atoms null, true, false come first, then numbers, strings, | |
// arrays, then objects | |
// null/undefined/NaN/Infinity/-Infinity are all considered null | |
function collationIndex(x) { | |
var id = ['boolean', 'number', 'string', 'object']; | |
var idx = id.indexOf(typeof x); | |
//false if -1 otherwise true, but fast!!!!1 | |
if (~idx) { | |
if (x === null) { | |
return 1; | |
} | |
if (Array.isArray(x)) { | |
return 5; | |
} | |
return idx < 3 ? (idx + 2) : (idx + 3); | |
} | |
if (Array.isArray(x)) { | |
return 5; | |
} | |
} | |
// conversion: | |
// x yyy zz...zz | |
// x = 0 for negative, 1 for 0, 2 for positive | |
// y = exponent (for negative numbers negated) moved so that it's >= 0 | |
// z = mantisse | |
function numToIndexableString(num) { | |
if (num === 0) { | |
return '1'; | |
} | |
// convert number to exponential format for easier and | |
// more succinct string sorting | |
var expFormat = num.toExponential().split(/e\+?/); | |
var magnitude = parseInt(expFormat[1], 10); | |
var neg = num < 0; | |
var result = neg ? '0' : '2'; | |
// first sort by magnitude | |
// it's easier if all magnitudes are positive | |
var magForComparison = ((neg ? -magnitude : magnitude) - MIN_MAGNITUDE); | |
var magString = utils.padLeft((magForComparison).toString(), '0', MAGNITUDE_DIGITS); | |
result += SEP + magString; | |
// then sort by the factor | |
var factor = Math.abs(parseFloat(expFormat[0])); // [1..10) | |
if (neg) { // for negative reverse ordering | |
factor = 10 - factor; | |
} | |
var factorStr = factor.toFixed(20); | |
// strip zeros from the end | |
factorStr = factorStr.replace(/\.?0+$/, ''); | |
result += SEP + factorStr; | |
return result; | |
} | |
},{"./utils":145}],145:[function(require,module,exports){ | |
'use strict'; | |
function pad(str, padWith, upToLength) { | |
var padding = ''; | |
var targetLength = upToLength - str.length; | |
while (padding.length < targetLength) { | |
padding += padWith; | |
} | |
return padding; | |
} | |
exports.padLeft = function (str, padWith, upToLength) { | |
var padding = pad(str, padWith, upToLength); | |
return padding + str; | |
}; | |
exports.padRight = function (str, padWith, upToLength) { | |
var padding = pad(str, padWith, upToLength); | |
return str + padding; | |
}; | |
exports.stringLexCompare = function (a, b) { | |
var aLen = a.length; | |
var bLen = b.length; | |
var i; | |
for (i = 0; i < aLen; i++) { | |
if (i === bLen) { | |
// b is shorter substring of a | |
return 1; | |
} | |
var aChar = a.charAt(i); | |
var bChar = b.charAt(i); | |
if (aChar !== bChar) { | |
return aChar < bChar ? -1 : 1; | |
} | |
} | |
if (aLen < bLen) { | |
// a is shorter substring of b | |
return -1; | |
} | |
return 0; | |
}; | |
/* | |
* returns the decimal form for the given integer, i.e. writes | |
* out all the digits (in base-10) instead of using scientific notation | |
*/ | |
exports.intToDecimalForm = function (int) { | |
var isNeg = int < 0; | |
var result = ''; | |
do { | |
var remainder = isNeg ? -Math.ceil(int % 10) : Math.floor(int % 10); | |
result = remainder + result; | |
int = isNeg ? Math.ceil(int / 10) : Math.floor(int / 10); | |
} while (int); | |
if (isNeg && result !== '0') { | |
result = '-' + result; | |
} | |
return result; | |
}; | |
},{}],146:[function(require,module,exports){ | |
'use strict'; | |
exports.Map = LazyMap; // TODO: use ES6 map | |
exports.Set = LazySet; // TODO: use ES6 set | |
// based on https://github.com/montagejs/collections | |
function LazyMap() { | |
this.store = {}; | |
} | |
LazyMap.prototype.mangle = function (key) { | |
if (typeof key !== "string") { | |
throw new TypeError("key must be a string but Got " + key); | |
} | |
return '$' + key; | |
}; | |
LazyMap.prototype.unmangle = function (key) { | |
return key.substring(1); | |
}; | |
LazyMap.prototype.get = function (key) { | |
var mangled = this.mangle(key); | |
if (mangled in this.store) { | |
return this.store[mangled]; | |
} else { | |
return void 0; | |
} | |
}; | |
LazyMap.prototype.set = function (key, value) { | |
var mangled = this.mangle(key); | |
this.store[mangled] = value; | |
return true; | |
}; | |
LazyMap.prototype.has = function (key) { | |
var mangled = this.mangle(key); | |
return mangled in this.store; | |
}; | |
LazyMap.prototype.delete = function (key) { | |
var mangled = this.mangle(key); | |
if (mangled in this.store) { | |
delete this.store[mangled]; | |
return true; | |
} | |
return false; | |
}; | |
LazyMap.prototype.forEach = function (cb) { | |
var self = this; | |
var keys = Object.keys(self.store); | |
keys.forEach(function (key) { | |
var value = self.store[key]; | |
key = self.unmangle(key); | |
cb(value, key); | |
}); | |
}; | |
function LazySet(array) { | |
this.store = new LazyMap(); | |
// init with an array | |
if (array && Array.isArray(array)) { | |
for (var i = 0, len = array.length; i < len; i++) { | |
this.add(array[i]); | |
} | |
} | |
} | |
LazySet.prototype.add = function (key) { | |
return this.store.set(key, true); | |
}; | |
LazySet.prototype.has = function (key) { | |
return this.store.has(key); | |
}; | |
LazySet.prototype.delete = function (key) { | |
return this.store.delete(key); | |
}; | |
},{}],147:[function(require,module,exports){ | |
'use strict'; | |
var upsert = require('./upsert'); | |
var utils = require('./utils'); | |
var Promise = utils.Promise; | |
module.exports = function (opts) { | |
var sourceDB = opts.db; | |
var viewName = opts.viewName; | |
var mapFun = opts.map; | |
var reduceFun = opts.reduce; | |
var temporary = opts.temporary; | |
// the "undefined" part is for backwards compatibility | |
var viewSignature = mapFun.toString() + (reduceFun && reduceFun.toString()) + | |
'undefined'; | |
if (!temporary && sourceDB._cachedViews) { | |
var cachedView = sourceDB._cachedViews[viewSignature]; | |
if (cachedView) { | |
return Promise.resolve(cachedView); | |
} | |
} | |
return sourceDB.info().then(function (info) { | |
var depDbName = info.db_name + '-mrview-' + | |
(temporary ? 'temp' : utils.MD5(viewSignature)); | |
// save the view name in the source PouchDB so it can be cleaned up if necessary | |
// (e.g. when the _design doc is deleted, remove all associated view data) | |
function diffFunction(doc) { | |
doc.views = doc.views || {}; | |
var fullViewName = viewName; | |
if (fullViewName.indexOf('/') === -1) { | |
fullViewName = viewName + '/' + viewName; | |
} | |
var depDbs = doc.views[fullViewName] = doc.views[fullViewName] || {}; | |
/* istanbul ignore if */ | |
if (depDbs[depDbName]) { | |
return; // no update necessary | |
} | |
depDbs[depDbName] = true; | |
return doc; | |
} | |
return upsert(sourceDB, '_local/mrviews', diffFunction).then(function () { | |
return sourceDB.registerDependentDatabase(depDbName).then(function (res) { | |
var db = res.db; | |
db.auto_compaction = true; | |
var view = { | |
name: depDbName, | |
db: db, | |
sourceDB: sourceDB, | |
adapter: sourceDB.adapter, | |
mapFun: mapFun, | |
reduceFun: reduceFun | |
}; | |
return view.db.get('_local/lastSeq')["catch"](function (err) { | |
/* istanbul ignore if */ | |
if (err.status !== 404) { | |
throw err; | |
} | |
}).then(function (lastSeqDoc) { | |
view.seq = lastSeqDoc ? lastSeqDoc.seq : 0; | |
if (!temporary) { | |
sourceDB._cachedViews = sourceDB._cachedViews || {}; | |
sourceDB._cachedViews[viewSignature] = view; | |
view.db.on('destroyed', function () { | |
delete sourceDB._cachedViews[viewSignature]; | |
}); | |
} | |
return view; | |
}); | |
}); | |
}); | |
}); | |
}; | |
},{"./upsert":151,"./utils":152}],148:[function(require,module,exports){ | |
'use strict'; | |
module.exports = function (func, emit, sum, log, isArray, toJSON) { | |
/*jshint evil:true,unused:false */ | |
return eval("'use strict'; (" + func.replace(/;\s*$/, "") + ");"); | |
}; | |
},{}],149:[function(require,module,exports){ | |
(function (process){ | |
'use strict'; | |
var pouchCollate = require('pouchdb-collate'); | |
var TaskQueue = require('./taskqueue'); | |
var collate = pouchCollate.collate; | |
var toIndexableString = pouchCollate.toIndexableString; | |
var normalizeKey = pouchCollate.normalizeKey; | |
var createView = require('./create-view'); | |
var evalFunc = require('./evalfunc'); | |
var log; | |
/* istanbul ignore else */ | |
if ((typeof console !== 'undefined') && (typeof console.log === 'function')) { | |
log = Function.prototype.bind.call(console.log, console); | |
} else { | |
log = function () {}; | |
} | |
var utils = require('./utils'); | |
var Promise = utils.Promise; | |
var persistentQueues = {}; | |
var tempViewQueue = new TaskQueue(); | |
var CHANGES_BATCH_SIZE = 50; | |
function parseViewName(name) { | |
// can be either 'ddocname/viewname' or just 'viewname' | |
// (where the ddoc name is the same) | |
return name.indexOf('/') === -1 ? [name, name] : name.split('/'); | |
} | |
function isGenOne(changes) { | |
// only return true if the current change is 1- | |
// and there are no other leafs | |
return changes.length === 1 && /^1-/.test(changes[0].rev); | |
} | |
function emitError(db, e) { | |
try { | |
db.emit('error', e); | |
} catch (err) { | |
console.error( | |
'The user\'s map/reduce function threw an uncaught error.\n' + | |
'You can debug this error by doing:\n' + | |
'myDatabase.on(\'error\', function (err) { debugger; });\n' + | |
'Please double-check your map/reduce function.'); | |
console.error(e); | |
} | |
} | |
function tryCode(db, fun, args) { | |
// emit an event if there was an error thrown by a map/reduce function. | |
// putting try/catches in a single function also avoids deoptimizations. | |
try { | |
return { | |
output : fun.apply(null, args) | |
}; | |
} catch (e) { | |
emitError(db, e); | |
return {error: e}; | |
} | |
} | |
function sortByKeyThenValue(x, y) { | |
var keyCompare = collate(x.key, y.key); | |
return keyCompare !== 0 ? keyCompare : collate(x.value, y.value); | |
} | |
function sliceResults(results, limit, skip) { | |
skip = skip || 0; | |
if (typeof limit === 'number') { | |
return results.slice(skip, limit + skip); | |
} else if (skip > 0) { | |
return results.slice(skip); | |
} | |
return results; | |
} | |
function rowToDocId(row) { | |
var val = row.value; | |
// Users can explicitly specify a joined doc _id, or it | |
// defaults to the doc _id that emitted the key/value. | |
var docId = (val && typeof val === 'object' && val._id) || row.id; | |
return docId; | |
} | |
function createBuiltInError(name) { | |
var message = 'builtin ' + name + | |
' function requires map values to be numbers' + | |
' or number arrays'; | |
return new BuiltInError(message); | |
} | |
function sum(values) { | |
var result = 0; | |
for (var i = 0, len = values.length; i < len; i++) { | |
var num = values[i]; | |
if (typeof num !== 'number') { | |
if (Array.isArray(num)) { | |
// lists of numbers are also allowed, sum them separately | |
result = typeof result === 'number' ? [result] : result; | |
for (var j = 0, jLen = num.length; j < jLen; j++) { | |
var jNum = num[j]; | |
if (typeof jNum !== 'number') { | |
throw createBuiltInError('_sum'); | |
} else if (typeof result[j] === 'undefined') { | |
result.push(jNum); | |
} else { | |
result[j] += jNum; | |
} | |
} | |
} else { // not array/number | |
throw createBuiltInError('_sum'); | |
} | |
} else if (typeof result === 'number') { | |
result += num; | |
} else { // add number to array | |
result[0] += num; | |
} | |
} | |
return result; | |
} | |
var builtInReduce = { | |
_sum: function (keys, values) { | |
return sum(values); | |
}, | |
_count: function (keys, values) { | |
return values.length; | |
}, | |
_stats: function (keys, values) { | |
// no need to implement rereduce=true, because Pouch | |
// will never call it | |
function sumsqr(values) { | |
var _sumsqr = 0; | |
for (var i = 0, len = values.length; i < len; i++) { | |
var num = values[i]; | |
_sumsqr += (num * num); | |
} | |
return _sumsqr; | |
} | |
return { | |
sum : sum(values), | |
min : Math.min.apply(null, values), | |
max : Math.max.apply(null, values), | |
count : values.length, | |
sumsqr : sumsqr(values) | |
}; | |
} | |
}; | |
function addHttpParam(paramName, opts, params, asJson) { | |
// add an http param from opts to params, optionally json-encoded | |
var val = opts[paramName]; | |
if (typeof val !== 'undefined') { | |
if (asJson) { | |
val = encodeURIComponent(JSON.stringify(val)); | |
} | |
params.push(paramName + '=' + val); | |
} | |
} | |
function checkQueryParseError(options, fun) { | |
var startkeyName = options.descending ? 'endkey' : 'startkey'; | |
var endkeyName = options.descending ? 'startkey' : 'endkey'; | |
if (typeof options[startkeyName] !== 'undefined' && | |
typeof options[endkeyName] !== 'undefined' && | |
collate(options[startkeyName], options[endkeyName]) > 0) { | |
throw new QueryParseError('No rows can match your key range, reverse your ' + | |
'start_key and end_key or set {descending : true}'); | |
} else if (fun.reduce && options.reduce !== false) { | |
if (options.include_docs) { | |
throw new QueryParseError('{include_docs:true} is invalid for reduce'); | |
} else if (options.keys && options.keys.length > 1 && | |
!options.group && !options.group_level) { | |
throw new QueryParseError('Multi-key fetches for reduce views must use {group: true}'); | |
} | |
} | |
if (options.group_level) { | |
if (typeof options.group_level !== 'number') { | |
throw new QueryParseError('Invalid value for integer: "' + options.group_level + '"'); | |
} | |
if (options.group_level < 0) { | |
throw new QueryParseError('Invalid value for positive integer: ' + | |
'"' + options.group_level + '"'); | |
} | |
} | |
} | |
function httpQuery(db, fun, opts) { | |
// List of parameters to add to the PUT request | |
var params = []; | |
var body; | |
var method = 'GET'; | |
// If opts.reduce exists and is defined, then add it to the list | |
// of parameters. | |
// If reduce=false then the results are that of only the map function | |
// not the final result of map and reduce. | |
addHttpParam('reduce', opts, params); | |
addHttpParam('include_docs', opts, params); | |
addHttpParam('attachments', opts, params); | |
addHttpParam('limit', opts, params); | |
addHttpParam('descending', opts, params); | |
addHttpParam('group', opts, params); | |
addHttpParam('group_level', opts, params); | |
addHttpParam('skip', opts, params); | |
addHttpParam('stale', opts, params); | |
addHttpParam('conflicts', opts, params); | |
addHttpParam('startkey', opts, params, true); | |
addHttpParam('endkey', opts, params, true); | |
addHttpParam('inclusive_end', opts, params); | |
addHttpParam('key', opts, params, true); | |
// Format the list of parameters into a valid URI query string | |
params = params.join('&'); | |
params = params === '' ? '' : '?' + params; | |
// If keys are supplied, issue a POST request to circumvent GET query string limits | |
// see http://wiki.apache.org/couchdb/HTTP_view_API#Querying_Options | |
if (typeof opts.keys !== 'undefined') { | |
var MAX_URL_LENGTH = 2000; | |
// according to http://stackoverflow.com/a/417184/680742, | |
// the de facto URL length limit is 2000 characters | |
var keysAsString = | |
'keys=' + encodeURIComponent(JSON.stringify(opts.keys)); | |
if (keysAsString.length + params.length + 1 <= MAX_URL_LENGTH) { | |
// If the keys are short enough, do a GET. we do this to work around | |
// Safari not understanding 304s on POSTs (see pouchdb/pouchdb#1239) | |
params += (params[0] === '?' ? '&' : '?') + keysAsString; | |
} else { | |
method = 'POST'; | |
if (typeof fun === 'string') { | |
body = JSON.stringify({keys: opts.keys}); | |
} else { // fun is {map : mapfun}, so append to this | |
fun.keys = opts.keys; | |
} | |
} | |
} | |
// We are referencing a query defined in the design doc | |
if (typeof fun === 'string') { | |
var parts = parseViewName(fun); | |
return db.request({ | |
method: method, | |
url: '_design/' + parts[0] + '/_view/' + parts[1] + params, | |
body: body | |
}); | |
} | |
// We are using a temporary view, terrible for performance but good for testing | |
body = body || {}; | |
Object.keys(fun).forEach(function (key) { | |
if (Array.isArray(fun[key])) { | |
body[key] = fun[key]; | |
} else { | |
body[key] = fun[key].toString(); | |
} | |
}); | |
return db.request({ | |
method: 'POST', | |
url: '_temp_view' + params, | |
body: body | |
}); | |
} | |
function defaultsTo(value) { | |
return function (reason) { | |
/* istanbul ignore else */ | |
if (reason.status === 404) { | |
return value; | |
} else { | |
throw reason; | |
} | |
}; | |
} | |
// returns a promise for a list of docs to update, based on the input docId. | |
// the order doesn't matter, because post-3.2.0, bulkDocs | |
// is an atomic operation in all three adapters. | |
function getDocsToPersist(docId, view, docIdsToChangesAndEmits) { | |
var metaDocId = '_local/doc_' + docId; | |
var defaultMetaDoc = {_id: metaDocId, keys: []}; | |
var docData = docIdsToChangesAndEmits[docId]; | |
var indexableKeysToKeyValues = docData.indexableKeysToKeyValues; | |
var changes = docData.changes; | |
function getMetaDoc() { | |
if (isGenOne(changes)) { | |
// generation 1, so we can safely assume initial state | |
// for performance reasons (avoids unnecessary GETs) | |
return Promise.resolve(defaultMetaDoc); | |
} | |
return view.db.get(metaDocId)["catch"](defaultsTo(defaultMetaDoc)); | |
} | |
function getKeyValueDocs(metaDoc) { | |
if (!metaDoc.keys.length) { | |
// no keys, no need for a lookup | |
return Promise.resolve({rows: []}); | |
} | |
return view.db.allDocs({ | |
keys: metaDoc.keys, | |
include_docs: true | |
}); | |
} | |
function processKvDocs(metaDoc, kvDocsRes) { | |
var kvDocs = []; | |
var oldKeysMap = {}; | |
for (var i = 0, len = kvDocsRes.rows.length; i < len; i++) { | |
var row = kvDocsRes.rows[i]; | |
var doc = row.doc; | |
if (!doc) { // deleted | |
continue; | |
} | |
kvDocs.push(doc); | |
oldKeysMap[doc._id] = true; | |
doc._deleted = !indexableKeysToKeyValues[doc._id]; | |
if (!doc._deleted) { | |
var keyValue = indexableKeysToKeyValues[doc._id]; | |
if ('value' in keyValue) { | |
doc.value = keyValue.value; | |
} | |
} | |
} | |
var newKeys = Object.keys(indexableKeysToKeyValues); | |
newKeys.forEach(function (key) { | |
if (!oldKeysMap[key]) { | |
// new doc | |
var kvDoc = { | |
_id: key | |
}; | |
var keyValue = indexableKeysToKeyValues[key]; | |
if ('value' in keyValue) { | |
kvDoc.value = keyValue.value; | |
} | |
kvDocs.push(kvDoc); | |
} | |
}); | |
metaDoc.keys = utils.uniq(newKeys.concat(metaDoc.keys)); | |
kvDocs.push(metaDoc); | |
return kvDocs; | |
} | |
return getMetaDoc().then(function (metaDoc) { | |
return getKeyValueDocs(metaDoc).then(function (kvDocsRes) { | |
return processKvDocs(metaDoc, kvDocsRes); | |
}); | |
}); | |
} | |
// updates all emitted key/value docs and metaDocs in the mrview database | |
// for the given batch of documents from the source database | |
function saveKeyValues(view, docIdsToChangesAndEmits, seq) { | |
var seqDocId = '_local/lastSeq'; | |
return view.db.get(seqDocId)[ | |
"catch"](defaultsTo({_id: seqDocId, seq: 0})) | |
.then(function (lastSeqDoc) { | |
var docIds = Object.keys(docIdsToChangesAndEmits); | |
return Promise.all(docIds.map(function (docId) { | |
return getDocsToPersist(docId, view, docIdsToChangesAndEmits); | |
})).then(function (listOfDocsToPersist) { | |
var docsToPersist = utils.flatten(listOfDocsToPersist); | |
lastSeqDoc.seq = seq; | |
docsToPersist.push(lastSeqDoc); | |
// write all docs in a single operation, update the seq once | |
return view.db.bulkDocs({docs : docsToPersist}); | |
}); | |
}); | |
} | |
function getQueue(view) { | |
var viewName = typeof view === 'string' ? view : view.name; | |
var queue = persistentQueues[viewName]; | |
if (!queue) { | |
queue = persistentQueues[viewName] = new TaskQueue(); | |
} | |
return queue; | |
} | |
function updateView(view) { | |
return utils.sequentialize(getQueue(view), function () { | |
return updateViewInQueue(view); | |
})(); | |
} | |
function updateViewInQueue(view) { | |
// bind the emit function once | |
var mapResults; | |
var doc; | |
function emit(key, value) { | |
var output = {id: doc._id, key: normalizeKey(key)}; | |
// Don't explicitly store the value unless it's defined and non-null. | |
// This saves on storage space, because often people don't use it. | |
if (typeof value !== 'undefined' && value !== null) { | |
output.value = normalizeKey(value); | |
} | |
mapResults.push(output); | |
} | |
var mapFun; | |
// for temp_views one can use emit(doc, emit), see #38 | |
if (typeof view.mapFun === "function" && view.mapFun.length === 2) { | |
var origMap = view.mapFun; | |
mapFun = function (doc) { | |
return origMap(doc, emit); | |
}; | |
} else { | |
mapFun = evalFunc(view.mapFun.toString(), emit, sum, log, Array.isArray, JSON.parse); | |
} | |
var currentSeq = view.seq || 0; | |
function processChange(docIdsToChangesAndEmits, seq) { | |
return function () { | |
return saveKeyValues(view, docIdsToChangesAndEmits, seq); | |
}; | |
} | |
var queue = new TaskQueue(); | |
// TODO(neojski): https://github.com/daleharvey/pouchdb/issues/1521 | |
return new Promise(function (resolve, reject) { | |
function complete() { | |
queue.finish().then(function () { | |
view.seq = currentSeq; | |
resolve(); | |
}); | |
} | |
function processNextBatch() { | |
view.sourceDB.changes({ | |
conflicts: true, | |
include_docs: true, | |
style: 'all_docs', | |
since: currentSeq, | |
limit: CHANGES_BATCH_SIZE | |
}).on('complete', function (response) { | |
var results = response.results; | |
if (!results.length) { | |
return complete(); | |
} | |
var docIdsToChangesAndEmits = {}; | |
for (var i = 0, l = results.length; i < l; i++) { | |
var change = results[i]; | |
if (change.doc._id[0] !== '_') { | |
mapResults = []; | |
doc = change.doc; | |
if (!doc._deleted) { | |
tryCode(view.sourceDB, mapFun, [doc]); | |
} | |
mapResults.sort(sortByKeyThenValue); | |
var indexableKeysToKeyValues = {}; | |
var lastKey; | |
for (var j = 0, jl = mapResults.length; j < jl; j++) { | |
var obj = mapResults[j]; | |
var complexKey = [obj.key, obj.id]; | |
if (collate(obj.key, lastKey) === 0) { | |
complexKey.push(j); // dup key+id, so make it unique | |
} | |
var indexableKey = toIndexableString(complexKey); | |
indexableKeysToKeyValues[indexableKey] = obj; | |
lastKey = obj.key; | |
} | |
docIdsToChangesAndEmits[change.doc._id] = { | |
indexableKeysToKeyValues: indexableKeysToKeyValues, | |
changes: change.changes | |
}; | |
} | |
currentSeq = change.seq; | |
} | |
queue.add(processChange(docIdsToChangesAndEmits, currentSeq)); | |
if (results.length < CHANGES_BATCH_SIZE) { | |
return complete(); | |
} | |
return processNextBatch(); | |
}).on('error', onError); | |
/* istanbul ignore next */ | |
function onError(err) { | |
reject(err); | |
} | |
} | |
processNextBatch(); | |
}); | |
} | |
function reduceView(view, results, options) { | |
if (options.group_level === 0) { | |
delete options.group_level; | |
} | |
var shouldGroup = options.group || options.group_level; | |
var reduceFun; | |
if (builtInReduce[view.reduceFun]) { | |
reduceFun = builtInReduce[view.reduceFun]; | |
} else { | |
reduceFun = evalFunc( | |
view.reduceFun.toString(), null, sum, log, Array.isArray, JSON.parse); | |
} | |
var groups = []; | |
var lvl = options.group_level; | |
results.forEach(function (e) { | |
var last = groups[groups.length - 1]; | |
var key = shouldGroup ? e.key : null; | |
// only set group_level for array keys | |
if (shouldGroup && Array.isArray(key) && typeof lvl === 'number') { | |
key = key.length > lvl ? key.slice(0, lvl) : key; | |
} | |
if (last && collate(last.key[0][0], key) === 0) { | |
last.key.push([key, e.id]); | |
last.value.push(e.value); | |
return; | |
} | |
groups.push({key: [ | |
[key, e.id] | |
], value: [e.value]}); | |
}); | |
for (var i = 0, len = groups.length; i < len; i++) { | |
var e = groups[i]; | |
var reduceTry = tryCode(view.sourceDB, reduceFun, [e.key, e.value, false]); | |
if (reduceTry.error && reduceTry.error instanceof BuiltInError) { | |
// CouchDB returns an error if a built-in errors out | |
throw reduceTry.error; | |
} | |
// CouchDB just sets the value to null if a non-built-in errors out | |
e.value = reduceTry.error ? null : reduceTry.output; | |
e.key = e.key[0][0]; | |
} | |
// no total_rows/offset when reducing | |
return {rows: sliceResults(groups, options.limit, options.skip)}; | |
} | |
function queryView(view, opts) { | |
return utils.sequentialize(getQueue(view), function () { | |
return queryViewInQueue(view, opts); | |
})(); | |
} | |
function queryViewInQueue(view, opts) { | |
var totalRows; | |
var shouldReduce = view.reduceFun && opts.reduce !== false; | |
var skip = opts.skip || 0; | |
if (typeof opts.keys !== 'undefined' && !opts.keys.length) { | |
// equivalent query | |
opts.limit = 0; | |
delete opts.keys; | |
} | |
function fetchFromView(viewOpts) { | |
viewOpts.include_docs = true; | |
return view.db.allDocs(viewOpts).then(function (res) { | |
totalRows = res.total_rows; | |
return res.rows.map(function (result) { | |
// implicit migration - in older versions of PouchDB, | |
// we explicitly stored the doc as {id: ..., key: ..., value: ...} | |
// this is tested in a migration test | |
/* istanbul ignore next */ | |
if ('value' in result.doc && typeof result.doc.value === 'object' && | |
result.doc.value !== null) { | |
var keys = Object.keys(result.doc.value).sort(); | |
// this detection method is not perfect, but it's unlikely the user | |
// emitted a value which was an object with these 3 exact keys | |
var expectedKeys = ['id', 'key', 'value']; | |
if (!(keys < expectedKeys || keys > expectedKeys)) { | |
return result.doc.value; | |
} | |
} | |
var parsedKeyAndDocId = pouchCollate.parseIndexableString(result.doc._id); | |
return { | |
key: parsedKeyAndDocId[0], | |
id: parsedKeyAndDocId[1], | |
value: ('value' in result.doc ? result.doc.value : null) | |
}; | |
}); | |
}); | |
} | |
function onMapResultsReady(rows) { | |
var finalResults; | |
if (shouldReduce) { | |
finalResults = reduceView(view, rows, opts); | |
} else { | |
finalResults = { | |
total_rows: totalRows, | |
offset: skip, | |
rows: rows | |
}; | |
} | |
if (opts.include_docs) { | |
var docIds = utils.uniq(rows.map(rowToDocId)); | |
return view.sourceDB.allDocs({ | |
keys: docIds, | |
include_docs: true, | |
conflicts: opts.conflicts, | |
attachments: opts.attachments | |
}).then(function (allDocsRes) { | |
var docIdsToDocs = {}; | |
allDocsRes.rows.forEach(function (row) { | |
if (row.doc) { | |
docIdsToDocs['$' + row.id] = row.doc; | |
} | |
}); | |
rows.forEach(function (row) { | |
var docId = rowToDocId(row); | |
var doc = docIdsToDocs['$' + docId]; | |
if (doc) { | |
row.doc = doc; | |
} | |
}); | |
return finalResults; | |
}); | |
} else { | |
return finalResults; | |
} | |
} | |
var flatten = function (array) { | |
return array.reduce(function (prev, cur) { | |
return prev.concat(cur); | |
}); | |
}; | |
if (typeof opts.keys !== 'undefined') { | |
var keys = opts.keys; | |
var fetchPromises = keys.map(function (key) { | |
var viewOpts = { | |
startkey : toIndexableString([key]), | |
endkey : toIndexableString([key, {}]) | |
}; | |
return fetchFromView(viewOpts); | |
}); | |
return Promise.all(fetchPromises).then(flatten).then(onMapResultsReady); | |
} else { // normal query, no 'keys' | |
var viewOpts = { | |
descending : opts.descending | |
}; | |
if (typeof opts.startkey !== 'undefined') { | |
viewOpts.startkey = opts.descending ? | |
toIndexableString([opts.startkey, {}]) : | |
toIndexableString([opts.startkey]); | |
} | |
if (typeof opts.endkey !== 'undefined') { | |
var inclusiveEnd = opts.inclusive_end !== false; | |
if (opts.descending) { | |
inclusiveEnd = !inclusiveEnd; | |
} | |
viewOpts.endkey = toIndexableString(inclusiveEnd ? [opts.endkey, {}] : [opts.endkey]); | |
} | |
if (typeof opts.key !== 'undefined') { | |
var keyStart = toIndexableString([opts.key]); | |
var keyEnd = toIndexableString([opts.key, {}]); | |
if (viewOpts.descending) { | |
viewOpts.endkey = keyStart; | |
viewOpts.startkey = keyEnd; | |
} else { | |
viewOpts.startkey = keyStart; | |
viewOpts.endkey = keyEnd; | |
} | |
} | |
if (!shouldReduce) { | |
if (typeof opts.limit === 'number') { | |
viewOpts.limit = opts.limit; | |
} | |
viewOpts.skip = skip; | |
} | |
return fetchFromView(viewOpts).then(onMapResultsReady); | |
} | |
} | |
function httpViewCleanup(db) { | |
return db.request({ | |
method: 'POST', | |
url: '_view_cleanup' | |
}); | |
} | |
function localViewCleanup(db) { | |
return db.get('_local/mrviews').then(function (metaDoc) { | |
var docsToViews = {}; | |
Object.keys(metaDoc.views).forEach(function (fullViewName) { | |
var parts = parseViewName(fullViewName); | |
var designDocName = '_design/' + parts[0]; | |
var viewName = parts[1]; | |
docsToViews[designDocName] = docsToViews[designDocName] || {}; | |
docsToViews[designDocName][viewName] = true; | |
}); | |
var opts = { | |
keys : Object.keys(docsToViews), | |
include_docs : true | |
}; | |
return db.allDocs(opts).then(function (res) { | |
var viewsToStatus = {}; | |
res.rows.forEach(function (row) { | |
var ddocName = row.key.substring(8); | |
Object.keys(docsToViews[row.key]).forEach(function (viewName) { | |
var fullViewName = ddocName + '/' + viewName; | |
/* istanbul ignore if */ | |
if (!metaDoc.views[fullViewName]) { | |
// new format, without slashes, to support PouchDB 2.2.0 | |
// migration test in pouchdb's browser.migration.js verifies this | |
fullViewName = viewName; | |
} | |
var viewDBNames = Object.keys(metaDoc.views[fullViewName]); | |
// design doc deleted, or view function nonexistent | |
var statusIsGood = row.doc && row.doc.views && row.doc.views[viewName]; | |
viewDBNames.forEach(function (viewDBName) { | |
viewsToStatus[viewDBName] = viewsToStatus[viewDBName] || statusIsGood; | |
}); | |
}); | |
}); | |
var dbsToDelete = Object.keys(viewsToStatus).filter(function (viewDBName) { | |
return !viewsToStatus[viewDBName]; | |
}); | |
var destroyPromises = dbsToDelete.map(function (viewDBName) { | |
return utils.sequentialize(getQueue(viewDBName), function () { | |
return new db.constructor(viewDBName, db.__opts).destroy(); | |
})(); | |
}); | |
return Promise.all(destroyPromises).then(function () { | |
return {ok: true}; | |
}); | |
}); | |
}, defaultsTo({ok: true})); | |
} | |
exports.viewCleanup = utils.callbackify(function () { | |
var db = this; | |
if (db.type() === 'http') { | |
return httpViewCleanup(db); | |
} | |
return localViewCleanup(db); | |
}); | |
function queryPromised(db, fun, opts) { | |
if (db.type() === 'http') { | |
return httpQuery(db, fun, opts); | |
} | |
if (typeof fun !== 'string') { | |
// temp_view | |
checkQueryParseError(opts, fun); | |
var createViewOpts = { | |
db : db, | |
viewName : 'temp_view/temp_view', | |
map : fun.map, | |
reduce : fun.reduce, | |
temporary : true | |
}; | |
tempViewQueue.add(function () { | |
return createView(createViewOpts).then(function (view) { | |
function cleanup() { | |
return view.db.destroy(); | |
} | |
return utils.fin(updateView(view).then(function () { | |
return queryView(view, opts); | |
}), cleanup); | |
}); | |
}); | |
return tempViewQueue.finish(); | |
} else { | |
// persistent view | |
var fullViewName = fun; | |
var parts = parseViewName(fullViewName); | |
var designDocName = parts[0]; | |
var viewName = parts[1]; | |
return db.get('_design/' + designDocName).then(function (doc) { | |
var fun = doc.views && doc.views[viewName]; | |
if (!fun || typeof fun.map !== 'string') { | |
throw new NotFoundError('ddoc ' + designDocName + ' has no view named ' + | |
viewName); | |
} | |
checkQueryParseError(opts, fun); | |
var createViewOpts = { | |
db : db, | |
viewName : fullViewName, | |
map : fun.map, | |
reduce : fun.reduce | |
}; | |
return createView(createViewOpts).then(function (view) { | |
if (opts.stale === 'ok' || opts.stale === 'update_after') { | |
if (opts.stale === 'update_after') { | |
process.nextTick(function () { | |
updateView(view); | |
}); | |
} | |
return queryView(view, opts); | |
} else { // stale not ok | |
return updateView(view).then(function () { | |
return queryView(view, opts); | |
}); | |
} | |
}); | |
}); | |
} | |
} | |
exports.query = function (fun, opts, callback) { | |
if (typeof opts === 'function') { | |
callback = opts; | |
opts = {}; | |
} | |
opts = utils.extend(true, {}, opts); | |
if (typeof fun === 'function') { | |
fun = {map : fun}; | |
} | |
var db = this; | |
var promise = Promise.resolve().then(function () { | |
return queryPromised(db, fun, opts); | |
}); | |
utils.promisedCallback(promise, callback); | |
return promise; | |
}; | |
function QueryParseError(message) { | |
this.status = 400; | |
this.name = 'query_parse_error'; | |
this.message = message; | |
this.error = true; | |
try { | |
Error.captureStackTrace(this, QueryParseError); | |
} catch (e) {} | |
} | |
utils.inherits(QueryParseError, Error); | |
function NotFoundError(message) { | |
this.status = 404; | |
this.name = 'not_found'; | |
this.message = message; | |
this.error = true; | |
try { | |
Error.captureStackTrace(this, NotFoundError); | |
} catch (e) {} | |
} | |
utils.inherits(NotFoundError, Error); | |
function BuiltInError(message) { | |
this.status = 500; | |
this.name = 'invalid_value'; | |
this.message = message; | |
this.error = true; | |
try { | |
Error.captureStackTrace(this, BuiltInError); | |
} catch (e) {} | |
} | |
utils.inherits(BuiltInError, Error); | |
}).call(this,require("/Users/nlawson/workspace-node/pouchdb-authentication/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js")) | |
},{"./create-view":147,"./evalfunc":148,"./taskqueue":150,"./utils":152,"/Users/nlawson/workspace-node/pouchdb-authentication/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":46,"pouchdb-collate":144}],150:[function(require,module,exports){ | |
'use strict'; | |
/* | |
* Simple task queue to sequentialize actions. Assumes callbacks will eventually fire (once). | |
*/ | |
var Promise = require('./utils').Promise; | |
function TaskQueue() { | |
this.promise = new Promise(function (fulfill) {fulfill(); }); | |
} | |
TaskQueue.prototype.add = function (promiseFactory) { | |
this.promise = this.promise["catch"](function () { | |
// just recover | |
}).then(function () { | |
return promiseFactory(); | |
}); | |
return this.promise; | |
}; | |
TaskQueue.prototype.finish = function () { | |
return this.promise; | |
}; | |
module.exports = TaskQueue; | |
},{"./utils":152}],151:[function(require,module,exports){ | |
'use strict'; | |
var upsert = require('pouchdb-upsert').upsert; | |
module.exports = function (db, doc, diffFun) { | |
return upsert.apply(db, [doc, diffFun]); | |
}; | |
},{"pouchdb-upsert":153}],152:[function(require,module,exports){ | |
(function (process,global){ | |
'use strict'; | |
/* istanbul ignore if */ | |
if (typeof global.Promise === 'function') { | |
exports.Promise = global.Promise; | |
} else { | |
exports.Promise = require('lie'); | |
} | |
exports.inherits = require('inherits'); | |
exports.extend = require('pouchdb-extend'); | |
var argsarray = require('argsarray'); | |
exports.promisedCallback = function (promise, callback) { | |
if (callback) { | |
promise.then(function (res) { | |
process.nextTick(function () { | |
callback(null, res); | |
}); | |
}, function (reason) { | |
process.nextTick(function () { | |
callback(reason); | |
}); | |
}); | |
} | |
return promise; | |
}; | |
exports.callbackify = function (fun) { | |
return argsarray(function (args) { | |
var cb = args.pop(); | |
var promise = fun.apply(this, args); | |
if (typeof cb === 'function') { | |
exports.promisedCallback(promise, cb); | |
} | |
return promise; | |
}); | |
}; | |
// Promise finally util similar to Q.finally | |
exports.fin = function (promise, cb) { | |
return promise.then(function (res) { | |
var promise2 = cb(); | |
if (typeof promise2.then === 'function') { | |
return promise2.then(function () { | |
return res; | |
}); | |
} | |
return res; | |
}, function (reason) { | |
var promise2 = cb(); | |
if (typeof promise2.then === 'function') { | |
return promise2.then(function () { | |
throw reason; | |
}); | |
} | |
throw reason; | |
}); | |
}; | |
exports.sequentialize = function (queue, promiseFactory) { | |
return function () { | |
var args = arguments; | |
var that = this; | |
return queue.add(function () { | |
return promiseFactory.apply(that, args); | |
}); | |
}; | |
}; | |
exports.flatten = function (arrs) { | |
var res = []; | |
for (var i = 0, len = arrs.length; i < len; i++) { | |
res = res.concat(arrs[i]); | |
} | |
return res; | |
}; | |
// uniq an array of strings, order not guaranteed | |
// similar to underscore/lodash _.uniq | |
exports.uniq = function (arr) { | |
var map = {}; | |
for (var i = 0, len = arr.length; i < len; i++) { | |
map['$' + arr[i]] = true; | |
} | |
var keys = Object.keys(map); | |
var output = new Array(keys.length); | |
for (i = 0, len = keys.length; i < len; i++) { | |
output[i] = keys[i].substring(1); | |
} | |
return output; | |
}; | |
var crypto = require('crypto'); | |
var Md5 = require('spark-md5'); | |
exports.MD5 = function (string) { | |
/* istanbul ignore else */ | |
if (!process.browser) { | |
return crypto.createHash('md5').update(string).digest('hex'); | |
} else { | |
return Md5.hash(string); | |
} | |
}; | |
}).call(this,require("/Users/nlawson/workspace-node/pouchdb-authentication/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
},{"/Users/nlawson/workspace-node/pouchdb-authentication/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":46,"argsarray":140,"crypto":41,"inherits":79,"lie":83,"pouchdb-extend":99,"spark-md5":154}],153:[function(require,module,exports){ | |
(function (global){ | |
'use strict'; | |
var PouchPromise; | |
/* istanbul ignore next */ | |
if (typeof window !== 'undefined' && window.PouchDB) { | |
PouchPromise = window.PouchDB.utils.Promise; | |
} else { | |
PouchPromise = typeof global.Promise === 'function' ? global.Promise : require('lie'); | |
} | |
// this is essentially the "update sugar" function from daleharvey/pouchdb#1388 | |
// the diffFun tells us what delta to apply to the doc. it either returns | |
// the doc, or false if it doesn't need to do an update after all | |
function upsertInner(db, docId, diffFun) { | |
return new PouchPromise(function (fulfill, reject) { | |
if (typeof docId !== 'string') { | |
return reject(new Error('doc id is required')); | |
} | |
db.get(docId, function (err, doc) { | |
if (err) { | |
/* istanbul ignore next */ | |
if (err.status !== 404) { | |
return reject(err); | |
} | |
doc = {}; | |
} | |
// the user might change the _rev, so save it for posterity | |
var docRev = doc._rev; | |
var newDoc = diffFun(doc); | |
if (!newDoc) { | |
// if the diffFun returns falsy, we short-circuit as | |
// an optimization | |
return fulfill({updated: false, rev: docRev}); | |
} | |
// users aren't allowed to modify these values, | |
// so reset them here | |
newDoc._id = docId; | |
newDoc._rev = docRev; | |
fulfill(tryAndPut(db, newDoc, diffFun)); | |
}); | |
}); | |
} | |
function tryAndPut(db, doc, diffFun) { | |
return db.put(doc).then(function (res) { | |
return { | |
updated: true, | |
rev: res.rev | |
}; | |
}, function (err) { | |
/* istanbul ignore next */ | |
if (err.status !== 409) { | |
throw err; | |
} | |
return upsertInner(db, doc._id, diffFun); | |
}); | |
} | |
exports.upsert = function upsert(docId, diffFun, cb) { | |
var db = this; | |
var promise = upsertInner(db, docId, diffFun); | |
if (typeof cb !== 'function') { | |
return promise; | |
} | |
promise.then(function (resp) { | |
cb(null, resp); | |
}, cb); | |
}; | |
exports.putIfNotExists = function putIfNotExists(docId, doc, cb) { | |
var db = this; | |
if (typeof docId !== 'string') { | |
cb = doc; | |
doc = docId; | |
docId = doc._id; | |
} | |
var diffFun = function (existingDoc) { | |
if (existingDoc._rev) { | |
return false; // do nothing | |
} | |
return doc; | |
}; | |
var promise = upsertInner(db, docId, diffFun); | |
if (typeof cb !== 'function') { | |
return promise; | |
} | |
promise.then(function (resp) { | |
cb(null, resp); | |
}, cb); | |
}; | |
/* istanbul ignore next */ | |
if (typeof window !== 'undefined' && window.PouchDB) { | |
window.PouchDB.plugin(exports); | |
} | |
}).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
},{"lie":83}],154:[function(require,module,exports){ | |
/*jshint bitwise:false*/ | |
/*global unescape*/ | |
(function (factory) { | |
if (typeof exports === 'object') { | |
// Node/CommonJS | |
module.exports = factory(); | |
} else if (typeof define === 'function' && define.amd) { | |
// AMD | |
define(factory); | |
} else { | |
// Browser globals (with support for web workers) | |
var glob; | |
try { | |
glob = window; | |
} catch (e) { | |
glob = self; | |
} | |
glob.SparkMD5 = factory(); | |
} | |
}(function (undefined) { | |
'use strict'; | |
//////////////////////////////////////////////////////////////////////////// | |
/* | |
* Fastest md5 implementation around (JKM md5) | |
* Credits: Joseph Myers | |
* | |
* @see http://www.myersdaily.org/joseph/javascript/md5-text.html | |
* @see http://jsperf.com/md5-shootout/7 | |
*/ | |
/* this function is much faster, | |
so if possible we use it. Some IEs | |
are the only ones I know of that | |
need the idiotic second function, | |
generated by an if clause. */ | |
var add32 = function (a, b) { | |
return (a + b) & 0xFFFFFFFF; | |
}, | |
cmn = function (q, a, b, x, s, t) { | |
a = add32(add32(a, q), add32(x, t)); | |
return add32((a << s) | (a >>> (32 - s)), b); | |
}, | |
ff = function (a, b, c, d, x, s, t) { | |
return cmn((b & c) | ((~b) & d), a, b, x, s, t); | |
}, | |
gg = function (a, b, c, d, x, s, t) { | |
return cmn((b & d) | (c & (~d)), a, b, x, s, t); | |
}, | |
hh = function (a, b, c, d, x, s, t) { | |
return cmn(b ^ c ^ d, a, b, x, s, t); | |
}, | |
ii = function (a, b, c, d, x, s, t) { | |
return cmn(c ^ (b | (~d)), a, b, x, s, t); | |
}, | |
md5cycle = function (x, k) { | |
var a = x[0], | |
b = x[1], | |
c = x[2], | |
d = x[3]; | |
a = ff(a, b, c, d, k[0], 7, -680876936); | |
d = ff(d, a, b, c, k[1], 12, -389564586); | |
c = ff(c, d, a, b, k[2], 17, 606105819); | |
b = ff(b, c, d, a, k[3], 22, -1044525330); | |
a = ff(a, b, c, d, k[4], 7, -176418897); | |
d = ff(d, a, b, c, k[5], 12, 1200080426); | |
c = ff(c, d, a, b, k[6], 17, -1473231341); | |
b = ff(b, c, d, a, k[7], 22, -45705983); | |
a = ff(a, b, c, d, k[8], 7, 1770035416); | |
d = ff(d, a, b, c, k[9], 12, -1958414417); | |
c = ff(c, d, a, b, k[10], 17, -42063); | |
b = ff(b, c, d, a, k[11], 22, -1990404162); | |
a = ff(a, b, c, d, k[12], 7, 1804603682); | |
d = ff(d, a, b, c, k[13], 12, -40341101); | |
c = ff(c, d, a, b, k[14], 17, -1502002290); | |
b = ff(b, c, d, a, k[15], 22, 1236535329); | |
a = gg(a, b, c, d, k[1], 5, -165796510); | |
d = gg(d, a, b, c, k[6], 9, -1069501632); | |
c = gg(c, d, a, b, k[11], 14, 643717713); | |
b = gg(b, c, d, a, k[0], 20, -373897302); | |
a = gg(a, b, c, d, k[5], 5, -701558691); | |
d = gg(d, a, b, c, k[10], 9, 38016083); | |
c = gg(c, d, a, b, k[15], 14, -660478335); | |
b = gg(b, c, d, a, k[4], 20, -405537848); | |
a = gg(a, b, c, d, k[9], 5, 568446438); | |
d = gg(d, a, b, c, k[14], 9, -1019803690); | |
c = gg(c, d, a, b, k[3], 14, -187363961); | |
b = gg(b, c, d, a, k[8], 20, 1163531501); | |
a = gg(a, b, c, d, k[13], 5, -1444681467); | |
d = gg(d, a, b, c, k[2], 9, -51403784); | |
c = gg(c, d, a, b, k[7], 14, 1735328473); | |
b = gg(b, c, d, a, k[12], 20, -1926607734); | |
a = hh(a, b, c, d, k[5], 4, -378558); | |
d = hh(d, a, b, c, k[8], 11, -2022574463); | |
c = hh(c, d, a, b, k[11], 16, 1839030562); | |
b = hh(b, c, d, a, k[14], 23, -35309556); | |
a = hh(a, b, c, d, k[1], 4, -1530992060); | |
d = hh(d, a, b, c, k[4], 11, 1272893353); | |
c = hh(c, d, a, b, k[7], 16, -155497632); | |
b = hh(b, c, d, a, k[10], 23, -1094730640); | |
a = hh(a, b, c, d, k[13], 4, 681279174); | |
d = hh(d, a, b, c, k[0], 11, -358537222); | |
c = hh(c, d, a, b, k[3], 16, -722521979); | |
b = hh(b, c, d, a, k[6], 23, 76029189); | |
a = hh(a, b, c, d, k[9], 4, -640364487); | |
d = hh(d, a, b, c, k[12], 11, -421815835); | |
c = hh(c, d, a, b, k[15], 16, 530742520); | |
b = hh(b, c, d, a, k[2], 23, -995338651); | |
a = ii(a, b, c, d, k[0], 6, -198630844); | |
d = ii(d, a, b, c, k[7], 10, 1126891415); | |
c = ii(c, d, a, b, k[14], 15, -1416354905); | |
b = ii(b, c, d, a, k[5], 21, -57434055); | |
a = ii(a, b, c, d, k[12], 6, 1700485571); | |
d = ii(d, a, b, c, k[3], 10, -1894986606); | |
c = ii(c, d, a, b, k[10], 15, -1051523); | |
b = ii(b, c, d, a, k[1], 21, -2054922799); | |
a = ii(a, b, c, d, k[8], 6, 1873313359); | |
d = ii(d, a, b, c, k[15], 10, -30611744); | |
c = ii(c, d, a, b, k[6], 15, -1560198380); | |
b = ii(b, c, d, a, k[13], 21, 1309151649); | |
a = ii(a, b, c, d, k[4], 6, -145523070); | |
d = ii(d, a, b, c, k[11], 10, -1120210379); | |
c = ii(c, d, a, b, k[2], 15, 718787259); | |
b = ii(b, c, d, a, k[9], 21, -343485551); | |
x[0] = add32(a, x[0]); | |
x[1] = add32(b, x[1]); | |
x[2] = add32(c, x[2]); | |
x[3] = add32(d, x[3]); | |
}, | |
/* there needs to be support for Unicode here, | |
* unless we pretend that we can redefine the MD-5 | |
* algorithm for multi-byte characters (perhaps | |
* by adding every four 16-bit characters and | |
* shortening the sum to 32 bits). Otherwise | |
* I suggest performing MD-5 as if every character | |
* was two bytes--e.g., 0040 0025 = @%--but then | |
* how will an ordinary MD-5 sum be matched? | |
* There is no way to standardize text to something | |
* like UTF-8 before transformation; speed cost is | |
* utterly prohibitive. The JavaScript standard | |
* itself needs to look at this: it should start | |
* providing access to strings as preformed UTF-8 | |
* 8-bit unsigned value arrays. | |
*/ | |
md5blk = function (s) { | |
var md5blks = [], | |
i; /* Andy King said do it this way. */ | |
for (i = 0; i < 64; i += 4) { | |
md5blks[i >> 2] = s.charCodeAt(i) + (s.charCodeAt(i + 1) << 8) + (s.charCodeAt(i + 2) << 16) + (s.charCodeAt(i + 3) << 24); | |
} | |
return md5blks; | |
}, | |
md5blk_array = function (a) { | |
var md5blks = [], | |
i; /* Andy King said do it this way. */ | |
for (i = 0; i < 64; i += 4) { | |
md5blks[i >> 2] = a[i] + (a[i + 1] << 8) + (a[i + 2] << 16) + (a[i + 3] << 24); | |
} | |
return md5blks; | |
}, | |
md51 = function (s) { | |
var n = s.length, | |
state = [1732584193, -271733879, -1732584194, 271733878], | |
i, | |
length, | |
tail, | |
tmp, | |
lo, | |
hi; | |
for (i = 64; i <= n; i += 64) { | |
md5cycle(state, md5blk(s.substring(i - 64, i))); | |
} | |
s = s.substring(i - 64); | |
length = s.length; | |
tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; | |
for (i = 0; i < length; i += 1) { | |
tail[i >> 2] |= s.charCodeAt(i) << ((i % 4) << 3); | |
} | |
tail[i >> 2] |= 0x80 << ((i % 4) << 3); | |
if (i > 55) { | |
md5cycle(state, tail); | |
for (i = 0; i < 16; i += 1) { | |
tail[i] = 0; | |
} | |
} | |
// Beware that the final length might not fit in 32 bits so we take care of that | |
tmp = n * 8; | |
tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/); | |
lo = parseInt(tmp[2], 16); | |
hi = parseInt(tmp[1], 16) || 0; | |
tail[14] = lo; | |
tail[15] = hi; | |
md5cycle(state, tail); | |
return state; | |
}, | |
md51_array = function (a) { | |
var n = a.length, | |
state = [1732584193, -271733879, -1732584194, 271733878], | |
i, | |
length, | |
tail, | |
tmp, | |
lo, | |
hi; | |
for (i = 64; i <= n; i += 64) { | |
md5cycle(state, md5blk_array(a.subarray(i - 64, i))); | |
} | |
// Not sure if it is a bug, however IE10 will always produce a sub array of length 1 | |
// containing the last element of the parent array if the sub array specified starts | |
// beyond the length of the parent array - weird. | |
// https://connect.microsoft.com/IE/feedback/details/771452/typed-array-subarray-issue | |
a = (i - 64) < n ? a.subarray(i - 64) : new Uint8Array(0); | |
length = a.length; | |
tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; | |
for (i = 0; i < length; i += 1) { | |
tail[i >> 2] |= a[i] << ((i % 4) << 3); | |
} | |
tail[i >> 2] |= 0x80 << ((i % 4) << 3); | |
if (i > 55) { | |
md5cycle(state, tail); | |
for (i = 0; i < 16; i += 1) { | |
tail[i] = 0; | |
} | |
} | |
// Beware that the final length might not fit in 32 bits so we take care of that | |
tmp = n * 8; | |
tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/); | |
lo = parseInt(tmp[2], 16); | |
hi = parseInt(tmp[1], 16) || 0; | |
tail[14] = lo; | |
tail[15] = hi; | |
md5cycle(state, tail); | |
return state; | |
}, | |
hex_chr = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'], | |
rhex = function (n) { | |
var s = '', | |
j; | |
for (j = 0; j < 4; j += 1) { | |
s += hex_chr[(n >> (j * 8 + 4)) & 0x0F] + hex_chr[(n >> (j * 8)) & 0x0F]; | |
} | |
return s; | |
}, | |
hex = function (x) { | |
var i; | |
for (i = 0; i < x.length; i += 1) { | |
x[i] = rhex(x[i]); | |
} | |
return x.join(''); | |
}, | |
md5 = function (s) { | |
return hex(md51(s)); | |
}, | |
//////////////////////////////////////////////////////////////////////////// | |
/** | |
* SparkMD5 OOP implementation. | |
* | |
* Use this class to perform an incremental md5, otherwise use the | |
* static methods instead. | |
*/ | |
SparkMD5 = function () { | |
// call reset to init the instance | |
this.reset(); | |
}; | |
// In some cases the fast add32 function cannot be used.. | |
if (md5('hello') !== '5d41402abc4b2a76b9719d911017c592') { | |
add32 = function (x, y) { | |
var lsw = (x & 0xFFFF) + (y & 0xFFFF), | |
msw = (x >> 16) + (y >> 16) + (lsw >> 16); | |
return (msw << 16) | (lsw & 0xFFFF); | |
}; | |
} | |
/** | |
* Appends a string. | |
* A conversion will be applied if an utf8 string is detected. | |
* | |
* @param {String} str The string to be appended | |
* | |
* @return {SparkMD5} The instance itself | |
*/ | |
SparkMD5.prototype.append = function (str) { | |
// converts the string to utf8 bytes if necessary | |
if (/[\u0080-\uFFFF]/.test(str)) { | |
str = unescape(encodeURIComponent(str)); | |
} | |
// then append as binary | |
this.appendBinary(str); | |
return this; | |
}; | |
/** | |
* Appends a binary string. | |
* | |
* @param {String} contents The binary string to be appended | |
* | |
* @return {SparkMD5} The instance itself | |
*/ | |
SparkMD5.prototype.appendBinary = function (contents) { | |
this._buff += contents; | |
this._length += contents.length; | |
var length = this._buff.length, | |
i; | |
for (i = 64; i <= length; i += 64) { | |
md5cycle(this._state, md5blk(this._buff.substring(i - 64, i))); | |
} | |
this._buff = this._buff.substr(i - 64); | |
return this; | |
}; | |
/** | |
* Finishes the incremental computation, reseting the internal state and | |
* returning the result. | |
* Use the raw parameter to obtain the raw result instead of the hex one. | |
* | |
* @param {Boolean} raw True to get the raw result, false to get the hex result | |
* | |
* @return {String|Array} The result | |
*/ | |
SparkMD5.prototype.end = function (raw) { | |
var buff = this._buff, | |
length = buff.length, | |
i, | |
tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], | |
ret; | |
for (i = 0; i < length; i += 1) { | |
tail[i >> 2] |= buff.charCodeAt(i) << ((i % 4) << 3); | |
} | |
this._finish(tail, length); | |
ret = !!raw ? this._state : hex(this._state); | |
this.reset(); | |
return ret; | |
}; | |
/** | |
* Finish the final calculation based on the tail. | |
* | |
* @param {Array} tail The tail (will be modified) | |
* @param {Number} length The length of the remaining buffer | |
*/ | |
SparkMD5.prototype._finish = function (tail, length) { | |
var i = length, | |
tmp, | |
lo, | |
hi; | |
tail[i >> 2] |= 0x80 << ((i % 4) << 3); | |
if (i > 55) { | |
md5cycle(this._state, tail); | |
for (i = 0; i < 16; i += 1) { | |
tail[i] = 0; | |
} | |
} | |
// Do the final computation based on the tail and length | |
// Beware that the final length may not fit in 32 bits so we take care of that | |
tmp = this._length * 8; | |
tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/); | |
lo = parseInt(tmp[2], 16); | |
hi = parseInt(tmp[1], 16) || 0; | |
tail[14] = lo; | |
tail[15] = hi; | |
md5cycle(this._state, tail); | |
}; | |
/** | |
* Resets the internal state of the computation. | |
* | |
* @return {SparkMD5} The instance itself | |
*/ | |
SparkMD5.prototype.reset = function () { | |
this._buff = ""; | |
this._length = 0; | |
this._state = [1732584193, -271733879, -1732584194, 271733878]; | |
return this; | |
}; | |
/** | |
* Releases memory used by the incremental buffer and other aditional | |
* resources. If you plan to use the instance again, use reset instead. | |
*/ | |
SparkMD5.prototype.destroy = function () { | |
delete this._state; | |
delete this._buff; | |
delete this._length; | |
}; | |
/** | |
* Performs the md5 hash on a string. | |
* A conversion will be applied if utf8 string is detected. | |
* | |
* @param {String} str The string | |
* @param {Boolean} raw True to get the raw result, false to get the hex result | |
* | |
* @return {String|Array} The result | |
*/ | |
SparkMD5.hash = function (str, raw) { | |
// converts the string to utf8 bytes if necessary | |
if (/[\u0080-\uFFFF]/.test(str)) { | |
str = unescape(encodeURIComponent(str)); | |
} | |
var hash = md51(str); | |
return !!raw ? hash : hex(hash); | |
}; | |
/** | |
* Performs the md5 hash on a binary string. | |
* | |
* @param {String} content The binary string | |
* @param {Boolean} raw True to get the raw result, false to get the hex result | |
* | |
* @return {String|Array} The result | |
*/ | |
SparkMD5.hashBinary = function (content, raw) { | |
var hash = md51(content); | |
return !!raw ? hash : hex(hash); | |
}; | |
/** | |
* SparkMD5 OOP implementation for array buffers. | |
* | |
* Use this class to perform an incremental md5 ONLY for array buffers. | |
*/ | |
SparkMD5.ArrayBuffer = function () { | |
// call reset to init the instance | |
this.reset(); | |
}; | |
//////////////////////////////////////////////////////////////////////////// | |
/** | |
* Appends an array buffer. | |
* | |
* @param {ArrayBuffer} arr The array to be appended | |
* | |
* @return {SparkMD5.ArrayBuffer} The instance itself | |
*/ | |
SparkMD5.ArrayBuffer.prototype.append = function (arr) { | |
// TODO: we could avoid the concatenation here but the algorithm would be more complex | |
// if you find yourself needing extra performance, please make a PR. | |
var buff = this._concatArrayBuffer(this._buff, arr), | |
length = buff.length, | |
i; | |
this._length += arr.byteLength; | |
for (i = 64; i <= length; i += 64) { | |
md5cycle(this._state, md5blk_array(buff.subarray(i - 64, i))); | |
} | |
// Avoids IE10 weirdness (documented above) | |
this._buff = (i - 64) < length ? buff.subarray(i - 64) : new Uint8Array(0); | |
return this; | |
}; | |
/** | |
* Finishes the incremental computation, reseting the internal state and | |
* returning the result. | |
* Use the raw parameter to obtain the raw result instead of the hex one. | |
* | |
* @param {Boolean} raw True to get the raw result, false to get the hex result | |
* | |
* @return {String|Array} The result | |
*/ | |
SparkMD5.ArrayBuffer.prototype.end = function (raw) { | |
var buff = this._buff, | |
length = buff.length, | |
tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], | |
i, | |
ret; | |
for (i = 0; i < length; i += 1) { | |
tail[i >> 2] |= buff[i] << ((i % 4) << 3); | |
} | |
this._finish(tail, length); | |
ret = !!raw ? this._state : hex(this._state); | |
this.reset(); | |
return ret; | |
}; | |
SparkMD5.ArrayBuffer.prototype._finish = SparkMD5.prototype._finish; | |
/** | |
* Resets the internal state of the computation. | |
* | |
* @return {SparkMD5.ArrayBuffer} The instance itself | |
*/ | |
SparkMD5.ArrayBuffer.prototype.reset = function () { | |
this._buff = new Uint8Array(0); | |
this._length = 0; | |
this._state = [1732584193, -271733879, -1732584194, 271733878]; | |
return this; | |
}; | |
/** | |
* Releases memory used by the incremental buffer and other aditional | |
* resources. If you plan to use the instance again, use reset instead. | |
*/ | |
SparkMD5.ArrayBuffer.prototype.destroy = SparkMD5.prototype.destroy; | |
/** | |
* Concats two array buffers, returning a new one. | |
* | |
* @param {ArrayBuffer} first The first array buffer | |
* @param {ArrayBuffer} second The second array buffer | |
* | |
* @return {ArrayBuffer} The new array buffer | |
*/ | |
SparkMD5.ArrayBuffer.prototype._concatArrayBuffer = function (first, second) { | |
var firstLength = first.length, | |
result = new Uint8Array(firstLength + second.byteLength); | |
result.set(first); | |
result.set(new Uint8Array(second), firstLength); | |
return result; | |
}; | |
/** | |
* Performs the md5 hash on an array buffer. | |
* | |
* @param {ArrayBuffer} arr The array buffer | |
* @param {Boolean} raw True to get the raw result, false to get the hex result | |
* | |
* @return {String|Array} The result | |
*/ | |
SparkMD5.ArrayBuffer.hash = function (arr, raw) { | |
var hash = md51_array(new Uint8Array(arr)); | |
return !!raw ? hash : hex(hash); | |
}; | |
return SparkMD5; | |
})); | |
},{}],155:[function(require,module,exports){ | |
'use strict'; | |
/** | |
* Stringify/parse functions that don't operate | |
* recursively, so they avoid call stack exceeded | |
* errors. | |
*/ | |
exports.stringify = function stringify(input) { | |
var queue = []; | |
queue.push({obj: input}); | |
var res = ''; | |
var next, obj, prefix, val, i, arrayPrefix, keys, k, key, value, objPrefix; | |
while ((next = queue.pop())) { | |
obj = next.obj; | |
prefix = next.prefix || ''; | |
val = next.val || ''; | |
res += prefix; | |
if (val) { | |
res += val; | |
} else if (typeof obj !== 'object') { | |
res += typeof obj === 'undefined' ? null : JSON.stringify(obj); | |
} else if (obj === null) { | |
res += 'null'; | |
} else if (Array.isArray(obj)) { | |
queue.push({val: ']'}); | |
for (i = obj.length - 1; i >= 0; i--) { | |
arrayPrefix = i === 0 ? '' : ','; | |
queue.push({obj: obj[i], prefix: arrayPrefix}); | |
} | |
queue.push({val: '['}); | |
} else { // object | |
keys = []; | |
for (k in obj) { | |
if (obj.hasOwnProperty(k)) { | |
keys.push(k); | |
} | |
} | |
queue.push({val: '}'}); | |
for (i = keys.length - 1; i >= 0; i--) { | |
key = keys[i]; | |
value = obj[key]; | |
objPrefix = (i > 0 ? ',' : ''); | |
objPrefix += JSON.stringify(key) + ':'; | |
queue.push({obj: value, prefix: objPrefix}); | |
} | |
queue.push({val: '{'}); | |
} | |
} | |
return res; | |
}; | |
// Convenience function for the parse function. | |
// This pop function is basically copied from | |
// pouchCollate.parseIndexableString | |
function pop(obj, stack, metaStack) { | |
var lastMetaElement = metaStack[metaStack.length - 1]; | |
if (obj === lastMetaElement.element) { | |
// popping a meta-element, e.g. an object whose value is another object | |
metaStack.pop(); | |
lastMetaElement = metaStack[metaStack.length - 1]; | |
} | |
var element = lastMetaElement.element; | |
var lastElementIndex = lastMetaElement.index; | |
if (Array.isArray(element)) { | |
element.push(obj); | |
} else if (lastElementIndex === stack.length - 2) { // obj with key+value | |
var key = stack.pop(); | |
element[key] = obj; | |
} else { | |
stack.push(obj); // obj with key only | |
} | |
} | |
exports.parse = function (str) { | |
var stack = []; | |
var metaStack = []; // stack for arrays and objects | |
var i = 0; | |
var collationIndex,parsedNum,numChar; | |
var parsedString,lastCh,numConsecutiveSlashes,ch; | |
var arrayElement, objElement; | |
while (true) { | |
collationIndex = str[i++]; | |
if (collationIndex === '}' || | |
collationIndex === ']' || | |
typeof collationIndex === 'undefined') { | |
if (stack.length === 1) { | |
return stack.pop(); | |
} else { | |
pop(stack.pop(), stack, metaStack); | |
continue; | |
} | |
} | |
switch (collationIndex) { | |
case ' ': | |
case '\t': | |
case '\n': | |
case ':': | |
case ',': | |
break; | |
case 'n': | |
i += 3; // 'ull' | |
pop(null, stack, metaStack); | |
break; | |
case 't': | |
i += 3; // 'rue' | |
pop(true, stack, metaStack); | |
break; | |
case 'f': | |
i += 4; // 'alse' | |
pop(false, stack, metaStack); | |
break; | |
case '0': | |
case '1': | |
case '2': | |
case '3': | |
case '4': | |
case '5': | |
case '6': | |
case '7': | |
case '8': | |
case '9': | |
case '-': | |
parsedNum = ''; | |
i--; | |
while (true) { | |
numChar = str[i++]; | |
if (/[\d\.\-e\+]/.test(numChar)) { | |
parsedNum += numChar; | |
} else { | |
i--; | |
break; | |
} | |
} | |
pop(parseFloat(parsedNum), stack, metaStack); | |
break; | |
case '"': | |
parsedString = ''; | |
lastCh = void 0; | |
numConsecutiveSlashes = 0; | |
while (true) { | |
ch = str[i++]; | |
if (ch !== '"' || (lastCh === '\\' && | |
numConsecutiveSlashes % 2 === 1)) { | |
parsedString += ch; | |
lastCh = ch; | |
if (lastCh === '\\') { | |
numConsecutiveSlashes++; | |
} else { | |
numConsecutiveSlashes = 0; | |
} | |
} else { | |
break; | |
} | |
} | |
pop(JSON.parse('"' + parsedString + '"'), stack, metaStack); | |
break; | |
case '[': | |
arrayElement = { element: [], index: stack.length }; | |
stack.push(arrayElement.element); | |
metaStack.push(arrayElement); | |
break; | |
case '{': | |
objElement = { element: {}, index: stack.length }; | |
stack.push(objElement.element); | |
metaStack.push(objElement); | |
break; | |
default: | |
throw new Error( | |
'unexpectedly reached end of input: ' + collationIndex); | |
} | |
} | |
}; | |
},{}],156:[function(require,module,exports){ | |
(function (process){ | |
/*jshint expr:true */ | |
/* global sum */ | |
'use strict'; | |
var PouchDB = require('pouchdb'); | |
var Authentication = require('../lib'); | |
PouchDB.plugin(Authentication); | |
var utils = require('../lib/utils'); | |
var chai = require('chai'); | |
var should = chai.should(); | |
require('mocha-as-promised')(); | |
chai.use(require('chai-as-promised')); | |
var Promise = require('bluebird'); | |
var all = Promise.all; | |
if (process.browser) { | |
process.env.TEST_DB = 'http://localhost:2022/testdb'; | |
} | |
var dbs = process.env.TEST_DB; | |
if (!dbs) { | |
console.error('No db name specified'); | |
process.exit(1); | |
} | |
dbs.split(',').forEach(function (db) { | |
var dbType = /^http/.test(db) ? 'http' : 'local'; | |
describe(dbType, function () { | |
tests(db); | |
}); | |
}); | |
var users = ['batman', 'superman', 'green_lantern', 'robin', 'aquaman', 'spiderman']; | |
function tests(dbName) { | |
describe('authentication', function () { | |
var db; | |
beforeEach(function () { | |
db = new PouchDB(dbName); | |
return db; | |
}); | |
afterEach(function () { | |
return db.logout().then(function () { | |
return PouchDB.destroy(dbName).then(function () { | |
var usersUrl = utils.getUsersUrl(db); | |
return new PouchDB(usersUrl).then(function (usersDb) { | |
// remove the fake users, hopefully we're in the admin party | |
return usersDb.allDocs({ | |
include_docs : true, | |
keys : users.map(function (user) { | |
return 'org.couchdb.user:' + user; | |
}) | |
}).then(function (res) { | |
var rows = res.rows.filter(function (row) { | |
return row.doc; | |
}); | |
var docs = rows.map(function (row) { | |
row.doc._deleted = true; | |
return row.doc; | |
}); | |
return usersDb.bulkDocs({docs : docs}); | |
}); | |
}); | |
}); | |
}); | |
}); | |
it('Test signup', function () { | |
return db.signup('batman', 'brucewayne').then(function (res) { | |
console.log(res); | |
res.ok.should.equal(true); | |
res.id.should.equal('org.couchdb.user:batman'); | |
}); | |
}); | |
it('Test signup conflict', function () { | |
return db.signup('superman', 'clarkkent').then(function (res) { | |
res.ok.should.equal(true); | |
return db.signup('superman', 'notclarkkent').then(function (res) { | |
should.not.exist(res); | |
}).catch(function (err) { | |
err.name.should.equal('conflict'); | |
}); | |
}); | |
}); | |
it('Test bad signup args', function () { | |
return db.signup().catch(function (err) { | |
should.exist(err); | |
}); | |
}); | |
it('Test bad signup args 2', function () { | |
return db.signup('green_lantern').catch(function (err) { | |
should.exist(err); | |
}); | |
}); | |
it('Test login/logout', function () { | |
return db.signup('aquaman', 'sleeps_with_fishes').then(function (res) { | |
return db.getSession(); | |
}).then(function (res) { | |
should.equal(res.userCtx.name, null); | |
return db.login('aquaman', 'sleeps_with_fishes'); | |
}).then(function (res) { | |
res.ok.should.equal(true); | |
return db.getSession(); | |
}).then(function (res) { | |
res.userCtx.name.should.equal('aquaman'); | |
return db.logout(); | |
}).then(function () { | |
return db.getSession(); | |
}).then(function (res) { | |
should.equal(res.userCtx.name, null); | |
}); | |
}); | |
it('Test metadata', function () { | |
var metadata = {alias : 'boywonder', profession : 'acrobat'}; | |
var opts = {metadata : metadata}; | |
return db.signup('robin', 'dickgrayson', opts).then(function (res) { | |
res.ok.should.equal(true); | |
return db.login('robin', 'dickgrayson'); | |
}).then(function () { | |
return db.getUser('robin'); | |
}).then(function (user) { | |
console.log(user); | |
user.name.should.equal('robin'); | |
user.alias.should.equal('boywonder'); | |
user.profession.should.equal('acrobat'); | |
}); | |
}); | |
it('Test wrong user for getUser', function () { | |
return db.signup('robin', 'dickgrayson').then(function (res) { | |
return db.signup('aquaman', 'sleeps_with_fishes'); | |
}).then(function () { | |
return db.login('robin', 'dickgrayson'); | |
}).then(function () { | |
return db.getUser('robin'); | |
}).then(function (res) { | |
res.name.should.equal('robin'); | |
return db.getUser('aquaman').then(function (res) { | |
should.not.exist(res); | |
}).catch(function (err) { | |
err.name.should.equal('not_found'); | |
return db.login('aquaman', 'sleeps_with_fishes').then(function () { | |
return db.getUser('aquaman').then(function (res) { | |
res.name.should.equal('aquaman'); | |
return db.getSession(); | |
}).then(function (res) { | |
res.userCtx.name.should.equal('aquaman'); | |
return db.getUser('robin').then(function (res) { | |
should.not.exist(res); | |
}).catch(function (err) { | |
err.name.should.equal('not_found'); | |
}); | |
}); | |
}); | |
}); | |
}); | |
}); | |
it('Test change password', function () { | |
return db.signup('spiderman', 'will-forget').then(function (res) { | |
return db.changePassword('spiderman', 'will-remember').then(function (res) { | |
res.ok.should.equal(true); | |
}).then(function () { | |
return db.login('spiderman', 'will-remember'); | |
}).then(function (res) { | |
res.ok.should.equal(true); | |
}); | |
}); | |
}); | |
}); | |
} | |
}).call(this,require("/Users/nlawson/workspace-node/pouchdb-authentication/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js")) | |
},{"../lib":1,"../lib/utils":2,"/Users/nlawson/workspace-node/pouchdb-authentication/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":46,"bluebird":6,"chai":49,"chai-as-promised":48,"mocha-as-promised":98,"pouchdb":132}]},{},[156]) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*jshint expr:true */ | |
/* global sum */ | |
'use strict'; | |
var PouchDB = require('pouchdb'); | |
var Authentication = require('../lib'); | |
PouchDB.plugin(Authentication); | |
var utils = require('../lib/utils'); | |
var chai = require('chai'); | |
var should = chai.should(); | |
require('mocha-as-promised')(); | |
chai.use(require('chai-as-promised')); | |
var Promise = require('bluebird'); | |
var all = Promise.all; | |
if (process.browser) { | |
process.env.TEST_DB = 'http://localhost:2022/testdb'; | |
} | |
var dbs = process.env.TEST_DB; | |
if (!dbs) { | |
console.error('No db name specified'); | |
process.exit(1); | |
} | |
dbs.split(',').forEach(function (db) { | |
var dbType = /^http/.test(db) ? 'http' : 'local'; | |
describe(dbType, function () { | |
tests(db); | |
}); | |
}); | |
var users = ['batman', 'superman', 'green_lantern', 'robin', 'aquaman', 'spiderman']; | |
function tests(dbName) { | |
describe('authentication', function () { | |
var db; | |
beforeEach(function () { | |
db = new PouchDB(dbName); | |
return db; | |
}); | |
afterEach(function () { | |
return db.logout().then(function () { | |
return PouchDB.destroy(dbName).then(function () { | |
var usersUrl = utils.getUsersUrl(db); | |
return new PouchDB(usersUrl).then(function (usersDb) { | |
// remove the fake users, hopefully we're in the admin party | |
return usersDb.allDocs({ | |
include_docs : true, | |
keys : users.map(function (user) { | |
return 'org.couchdb.user:' + user; | |
}) | |
}).then(function (res) { | |
var rows = res.rows.filter(function (row) { | |
return row.doc; | |
}); | |
var docs = rows.map(function (row) { | |
row.doc._deleted = true; | |
return row.doc; | |
}); | |
return usersDb.bulkDocs({docs : docs}); | |
}); | |
}); | |
}); | |
}); | |
}); | |
it('Test signup', function () { | |
return db.signup('batman', 'brucewayne').then(function (res) { | |
console.log(res); | |
res.ok.should.equal(true); | |
res.id.should.equal('org.couchdb.user:batman'); | |
}); | |
}); | |
it('Test signup conflict', function () { | |
return db.signup('superman', 'clarkkent').then(function (res) { | |
res.ok.should.equal(true); | |
return db.signup('superman', 'notclarkkent').then(function (res) { | |
should.not.exist(res); | |
}).catch(function (err) { | |
err.name.should.equal('conflict'); | |
}); | |
}); | |
}); | |
it('Test bad signup args', function () { | |
return db.signup().catch(function (err) { | |
should.exist(err); | |
}); | |
}); | |
it('Test bad signup args 2', function () { | |
return db.signup('green_lantern').catch(function (err) { | |
should.exist(err); | |
}); | |
}); | |
it('Test login/logout', function () { | |
return db.signup('aquaman', 'sleeps_with_fishes').then(function (res) { | |
return db.getSession(); | |
}).then(function (res) { | |
should.equal(res.userCtx.name, null); | |
return db.login('aquaman', 'sleeps_with_fishes'); | |
}).then(function (res) { | |
res.ok.should.equal(true); | |
return db.getSession(); | |
}).then(function (res) { | |
res.userCtx.name.should.equal('aquaman'); | |
return db.logout(); | |
}).then(function () { | |
return db.getSession(); | |
}).then(function (res) { | |
should.equal(res.userCtx.name, null); | |
}); | |
}); | |
it('Test metadata', function () { | |
var metadata = {alias : 'boywonder', profession : 'acrobat'}; | |
var opts = {metadata : metadata}; | |
return db.signup('robin', 'dickgrayson', opts).then(function (res) { | |
res.ok.should.equal(true); | |
return db.login('robin', 'dickgrayson'); | |
}).then(function () { | |
return db.getUser('robin'); | |
}).then(function (user) { | |
console.log(user); | |
user.name.should.equal('robin'); | |
user.alias.should.equal('boywonder'); | |
user.profession.should.equal('acrobat'); | |
}); | |
}); | |
it('Test wrong user for getUser', function () { | |
return db.signup('robin', 'dickgrayson').then(function (res) { | |
return db.signup('aquaman', 'sleeps_with_fishes'); | |
}).then(function () { | |
return db.login('robin', 'dickgrayson'); | |
}).then(function () { | |
return db.getUser('robin'); | |
}).then(function (res) { | |
res.name.should.equal('robin'); | |
return db.getUser('aquaman').then(function (res) { | |
should.not.exist(res); | |
}).catch(function (err) { | |
err.name.should.equal('not_found'); | |
return db.login('aquaman', 'sleeps_with_fishes').then(function () { | |
return db.getUser('aquaman').then(function (res) { | |
res.name.should.equal('aquaman'); | |
return db.getSession(); | |
}).then(function (res) { | |
res.userCtx.name.should.equal('aquaman'); | |
return db.getUser('robin').then(function (res) { | |
should.not.exist(res); | |
}).catch(function (err) { | |
err.name.should.equal('not_found'); | |
}); | |
}); | |
}); | |
}); | |
}); | |
}); | |
it('Test change password', function () { | |
return db.signup('spiderman', 'will-forget').then(function (res) { | |
return db.changePassword('spiderman', 'will-remember').then(function (res) { | |
res.ok.should.equal(true); | |
}).then(function () { | |
return db.login('spiderman', 'will-remember'); | |
}).then(function (res) { | |
res.ok.should.equal(true); | |
}); | |
}); | |
}); | |
}); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment