Initial commit

This commit is contained in:
Spencer Pincott
2024-07-15 22:20:13 -04:00
commit 97737ca1ae
16618 changed files with 934131 additions and 0 deletions

1228
themes/keepit/node_modules/algoliasearch/CHANGELOG.md generated vendored Normal file

File diff suppressed because it is too large Load Diff

22
themes/keepit/node_modules/algoliasearch/LICENSE.txt generated vendored Normal file
View File

@@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2013 Algolia
http://www.algolia.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

81
themes/keepit/node_modules/algoliasearch/README.md generated vendored Normal file
View File

@@ -0,0 +1,81 @@
<p align="center">
<a href="https://www.algolia.com">
<img alt="Algolia for JavaScript" src="https://raw.githubusercontent.com/algolia/algoliasearch-client-common/master/banners/javascript.png" >
</a>
<h4 align="center">The perfect starting point to integrate <a href="https://algolia.com" target="_blank">Algolia</a> within your JavaScript project</h4>
<p align="center">
<a href="https://travis-ci.org/algolia/algoliasearch-client-javascript"><img src="https://img.shields.io/travis/algolia/algoliasearch-client-javascript/master.svg" alt="Build Status"></img></a>
<a href="https://npmjs.org/package/algoliasearch"><img src="https://img.shields.io/npm/v/algoliasearch.svg?style=flat-square" alt="NPM version"></img></a>
<a href="http://npm-stat.com/charts.html?package=algoliasearch"><img src="https://img.shields.io/npm/dm/algoliasearch.svg?style=flat-square" alt="NPM downloads"></a>
<a href="https://www.jsdelivr.com/package/npm/algoliasearch"><img src="https://data.jsdelivr.com/v1/package/npm/algoliasearch/badge" alt="jsDelivr Downloads"></img></a>
<a href="LICENSE.txt"><img src="https://img.shields.io/badge/license-MIT-green.svg?style=flat-square" alt="License"></a>
</p>
</p>
<p align="center">
<a href="https://www.algolia.com/doc/api-client/getting-started/install/javascript/" target="_blank">Documentation</a> •
<a href="https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/js/" target="_blank">InstantSearch</a> •
<a href="https://discourse.algolia.com" target="_blank">Community Forum</a> •
<a href="http://stackoverflow.com/questions/tagged/algolia" target="_blank">Stack Overflow</a> •
<a href="https://github.com/algolia/algoliasearch-client-javascript/issues" target="_blank">Report a bug</a> •
<a href="https://www.algolia.com/support" target="_blank">Support</a>
</p>
## ✨ Features
- Thin & **minimal low-level HTTP client** to interact with Algolia's API
- Works both on the **browser** and **node.js**
- **UMD compatible**, you can use it with any module loader
- Contains type definitions: **[@types/algoliasearch](https://www.npmjs.com/package/@types/algoliasearch)**
## 💡 Getting Started
First, install Algolia JavaScript API Client via the [npm](https://www.npmjs.com/get-npm) package manager:
```bash
npm install --save algoliasearch
```
Then, create objects on your index:
```js
const algoliasearch = require('algoliasearch');
const client = algoliasearch('YourApplicationID', 'YourAdminAPIKey');
const index = client.initIndex('your_index_name');
const objects = [{
objectID: 1,
name: 'Foo'
}];
index
.saveObjects(objects)
.then(({ objectIDs }) => {
console.log(objectIDs);
})
.catch(err => {
console.log(err);
});
```
Finally, let's actually search using the `search` method:
```js
index
.search('Fo')
.then(({ hits }) => {
console.log(hits);
})
.catch(err => {
console.log(err);
});
```
For full documentation, visit the **[online documentation](https://www.algolia.com/doc/api-client/getting-started/install/javascript/)**.
## 📄 License
Algolia JavaScript API Client is an open-sourced software licensed under the [MIT license](LICENSE.txt).

24
themes/keepit/node_modules/algoliasearch/bower.json generated vendored Normal file
View File

@@ -0,0 +1,24 @@
{
"name": "algoliasearch",
"version": "3.35.1",
"homepage": "https://github.com/algolia/algoliasearch-client-js",
"authors": [
"Algolia Team <support@algolia.com>"
],
"description": "Algolia Search is a search API that provides hosted full-text, numerical and faceted search.",
"main": "dist/algoliasearch.js",
"keywords": [
"js",
"algolia",
"search",
"api",
"rest"
],
"license": "MIT",
"ignore": [
"examples",
"node_modules",
"bower_components",
"test"
]
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

6
themes/keepit/node_modules/algoliasearch/index.js generated vendored Normal file
View File

@@ -0,0 +1,6 @@
'use strict';
// default entrypoint is the node module
// this is overridden by the `browser` field in package.json
// https://github.com/substack/node-browserify#browser-field
module.exports = require('./src/server/builds/node');

7
themes/keepit/node_modules/algoliasearch/lite.js generated vendored Normal file
View File

@@ -0,0 +1,7 @@
'use strict';
// For Node.js users, you will get the normal build (no lite)
// For browser users, this will be mapped to src/browser/builds/algoliasearchLite.js
// when used through browserify or webpack (see package.json browser field)
// This is done to ease universal applications (https://github.com/algolia/algoliasearch-client-js/issues/283)
module.exports = require('./index.js');

View File

@@ -0,0 +1 @@
../semver/bin/semver

View File

@@ -0,0 +1 @@
repo_token: SIAeZjKYlHK74rbcFvNHMUzjRiMpflxve

View File

@@ -0,0 +1,11 @@
{
"env": {
"browser": true,
"node": true
},
"rules": {
"no-console": 0,
"no-empty": [1, { "allowEmptyCatch": true }]
},
"extends": "eslint:recommended"
}

View File

@@ -0,0 +1,9 @@
support
test
examples
example
*.sock
dist
yarn.lock
coverage
bower.json

View File

@@ -0,0 +1,14 @@
language: node_js
node_js:
- "6"
- "5"
- "4"
install:
- make node_modules
script:
- make lint
- make test
- make coveralls

View File

@@ -0,0 +1,362 @@
2.6.9 / 2017-09-22
==================
* remove ReDoS regexp in %o formatter (#504)
2.6.8 / 2017-05-18
==================
* Fix: Check for undefined on browser globals (#462, @marbemac)
2.6.7 / 2017-05-16
==================
* Fix: Update ms to 2.0.0 to fix regular expression denial of service vulnerability (#458, @hubdotcom)
* Fix: Inline extend function in node implementation (#452, @dougwilson)
* Docs: Fix typo (#455, @msasad)
2.6.5 / 2017-04-27
==================
* Fix: null reference check on window.documentElement.style.WebkitAppearance (#447, @thebigredgeek)
* Misc: clean up browser reference checks (#447, @thebigredgeek)
* Misc: add npm-debug.log to .gitignore (@thebigredgeek)
2.6.4 / 2017-04-20
==================
* Fix: bug that would occure if process.env.DEBUG is a non-string value. (#444, @LucianBuzzo)
* Chore: ignore bower.json in npm installations. (#437, @joaovieira)
* Misc: update "ms" to v0.7.3 (@tootallnate)
2.6.3 / 2017-03-13
==================
* Fix: Electron reference to `process.env.DEBUG` (#431, @paulcbetts)
* Docs: Changelog fix (@thebigredgeek)
2.6.2 / 2017-03-10
==================
* Fix: DEBUG_MAX_ARRAY_LENGTH (#420, @slavaGanzin)
* Docs: Add backers and sponsors from Open Collective (#422, @piamancini)
* Docs: Add Slackin invite badge (@tootallnate)
2.6.1 / 2017-02-10
==================
* Fix: Module's `export default` syntax fix for IE8 `Expected identifier` error
* Fix: Whitelist DEBUG_FD for values 1 and 2 only (#415, @pi0)
* Fix: IE8 "Expected identifier" error (#414, @vgoma)
* Fix: Namespaces would not disable once enabled (#409, @musikov)
2.6.0 / 2016-12-28
==================
* Fix: added better null pointer checks for browser useColors (@thebigredgeek)
* Improvement: removed explicit `window.debug` export (#404, @tootallnate)
* Improvement: deprecated `DEBUG_FD` environment variable (#405, @tootallnate)
2.5.2 / 2016-12-25
==================
* Fix: reference error on window within webworkers (#393, @KlausTrainer)
* Docs: fixed README typo (#391, @lurch)
* Docs: added notice about v3 api discussion (@thebigredgeek)
2.5.1 / 2016-12-20
==================
* Fix: babel-core compatibility
2.5.0 / 2016-12-20
==================
* Fix: wrong reference in bower file (@thebigredgeek)
* Fix: webworker compatibility (@thebigredgeek)
* Fix: output formatting issue (#388, @kribblo)
* Fix: babel-loader compatibility (#383, @escwald)
* Misc: removed built asset from repo and publications (@thebigredgeek)
* Misc: moved source files to /src (#378, @yamikuronue)
* Test: added karma integration and replaced babel with browserify for browser tests (#378, @yamikuronue)
* Test: coveralls integration (#378, @yamikuronue)
* Docs: simplified language in the opening paragraph (#373, @yamikuronue)
2.4.5 / 2016-12-17
==================
* Fix: `navigator` undefined in Rhino (#376, @jochenberger)
* Fix: custom log function (#379, @hsiliev)
* Improvement: bit of cleanup + linting fixes (@thebigredgeek)
* Improvement: rm non-maintainted `dist/` dir (#375, @freewil)
* Docs: simplified language in the opening paragraph. (#373, @yamikuronue)
2.4.4 / 2016-12-14
==================
* Fix: work around debug being loaded in preload scripts for electron (#368, @paulcbetts)
2.4.3 / 2016-12-14
==================
* Fix: navigation.userAgent error for react native (#364, @escwald)
2.4.2 / 2016-12-14
==================
* Fix: browser colors (#367, @tootallnate)
* Misc: travis ci integration (@thebigredgeek)
* Misc: added linting and testing boilerplate with sanity check (@thebigredgeek)
2.4.1 / 2016-12-13
==================
* Fix: typo that broke the package (#356)
2.4.0 / 2016-12-13
==================
* Fix: bower.json references unbuilt src entry point (#342, @justmatt)
* Fix: revert "handle regex special characters" (@tootallnate)
* Feature: configurable util.inspect()`options for NodeJS (#327, @tootallnate)
* Feature: %O`(big O) pretty-prints objects (#322, @tootallnate)
* Improvement: allow colors in workers (#335, @botverse)
* Improvement: use same color for same namespace. (#338, @lchenay)
2.3.3 / 2016-11-09
==================
* Fix: Catch `JSON.stringify()` errors (#195, Jovan Alleyne)
* Fix: Returning `localStorage` saved values (#331, Levi Thomason)
* Improvement: Don't create an empty object when no `process` (Nathan Rajlich)
2.3.2 / 2016-11-09
==================
* Fix: be super-safe in index.js as well (@TooTallNate)
* Fix: should check whether process exists (Tom Newby)
2.3.1 / 2016-11-09
==================
* Fix: Added electron compatibility (#324, @paulcbetts)
* Improvement: Added performance optimizations (@tootallnate)
* Readme: Corrected PowerShell environment variable example (#252, @gimre)
* Misc: Removed yarn lock file from source control (#321, @fengmk2)
2.3.0 / 2016-11-07
==================
* Fix: Consistent placement of ms diff at end of output (#215, @gorangajic)
* Fix: Escaping of regex special characters in namespace strings (#250, @zacronos)
* Fix: Fixed bug causing crash on react-native (#282, @vkarpov15)
* Feature: Enabled ES6+ compatible import via default export (#212 @bucaran)
* Feature: Added %O formatter to reflect Chrome's console.log capability (#279, @oncletom)
* Package: Update "ms" to 0.7.2 (#315, @DevSide)
* Package: removed superfluous version property from bower.json (#207 @kkirsche)
* Readme: fix USE_COLORS to DEBUG_COLORS
* Readme: Doc fixes for format string sugar (#269, @mlucool)
* Readme: Updated docs for DEBUG_FD and DEBUG_COLORS environment variables (#232, @mattlyons0)
* Readme: doc fixes for PowerShell (#271 #243, @exoticknight @unreadable)
* Readme: better docs for browser support (#224, @matthewmueller)
* Tooling: Added yarn integration for development (#317, @thebigredgeek)
* Misc: Renamed History.md to CHANGELOG.md (@thebigredgeek)
* Misc: Added license file (#226 #274, @CantemoInternal @sdaitzman)
* Misc: Updated contributors (@thebigredgeek)
2.2.0 / 2015-05-09
==================
* package: update "ms" to v0.7.1 (#202, @dougwilson)
* README: add logging to file example (#193, @DanielOchoa)
* README: fixed a typo (#191, @amir-s)
* browser: expose `storage` (#190, @stephenmathieson)
* Makefile: add a `distclean` target (#189, @stephenmathieson)
2.1.3 / 2015-03-13
==================
* Updated stdout/stderr example (#186)
* Updated example/stdout.js to match debug current behaviour
* Renamed example/stderr.js to stdout.js
* Update Readme.md (#184)
* replace high intensity foreground color for bold (#182, #183)
2.1.2 / 2015-03-01
==================
* dist: recompile
* update "ms" to v0.7.0
* package: update "browserify" to v9.0.3
* component: fix "ms.js" repo location
* changed bower package name
* updated documentation about using debug in a browser
* fix: security error on safari (#167, #168, @yields)
2.1.1 / 2014-12-29
==================
* browser: use `typeof` to check for `console` existence
* browser: check for `console.log` truthiness (fix IE 8/9)
* browser: add support for Chrome apps
* Readme: added Windows usage remarks
* Add `bower.json` to properly support bower install
2.1.0 / 2014-10-15
==================
* node: implement `DEBUG_FD` env variable support
* package: update "browserify" to v6.1.0
* package: add "license" field to package.json (#135, @panuhorsmalahti)
2.0.0 / 2014-09-01
==================
* package: update "browserify" to v5.11.0
* node: use stderr rather than stdout for logging (#29, @stephenmathieson)
1.0.4 / 2014-07-15
==================
* dist: recompile
* example: remove `console.info()` log usage
* example: add "Content-Type" UTF-8 header to browser example
* browser: place %c marker after the space character
* browser: reset the "content" color via `color: inherit`
* browser: add colors support for Firefox >= v31
* debug: prefer an instance `log()` function over the global one (#119)
* Readme: update documentation about styled console logs for FF v31 (#116, @wryk)
1.0.3 / 2014-07-09
==================
* Add support for multiple wildcards in namespaces (#122, @seegno)
* browser: fix lint
1.0.2 / 2014-06-10
==================
* browser: update color palette (#113, @gscottolson)
* common: make console logging function configurable (#108, @timoxley)
* node: fix %o colors on old node <= 0.8.x
* Makefile: find node path using shell/which (#109, @timoxley)
1.0.1 / 2014-06-06
==================
* browser: use `removeItem()` to clear localStorage
* browser, node: don't set DEBUG if namespaces is undefined (#107, @leedm777)
* package: add "contributors" section
* node: fix comment typo
* README: list authors
1.0.0 / 2014-06-04
==================
* make ms diff be global, not be scope
* debug: ignore empty strings in enable()
* node: make DEBUG_COLORS able to disable coloring
* *: export the `colors` array
* npmignore: don't publish the `dist` dir
* Makefile: refactor to use browserify
* package: add "browserify" as a dev dependency
* Readme: add Web Inspector Colors section
* node: reset terminal color for the debug content
* node: map "%o" to `util.inspect()`
* browser: map "%j" to `JSON.stringify()`
* debug: add custom "formatters"
* debug: use "ms" module for humanizing the diff
* Readme: add "bash" syntax highlighting
* browser: add Firebug color support
* browser: add colors for WebKit browsers
* node: apply log to `console`
* rewrite: abstract common logic for Node & browsers
* add .jshintrc file
0.8.1 / 2014-04-14
==================
* package: re-add the "component" section
0.8.0 / 2014-03-30
==================
* add `enable()` method for nodejs. Closes #27
* change from stderr to stdout
* remove unnecessary index.js file
0.7.4 / 2013-11-13
==================
* remove "browserify" key from package.json (fixes something in browserify)
0.7.3 / 2013-10-30
==================
* fix: catch localStorage security error when cookies are blocked (Chrome)
* add debug(err) support. Closes #46
* add .browser prop to package.json. Closes #42
0.7.2 / 2013-02-06
==================
* fix package.json
* fix: Mobile Safari (private mode) is broken with debug
* fix: Use unicode to send escape character to shell instead of octal to work with strict mode javascript
0.7.1 / 2013-02-05
==================
* add repository URL to package.json
* add DEBUG_COLORED to force colored output
* add browserify support
* fix component. Closes #24
0.7.0 / 2012-05-04
==================
* Added .component to package.json
* Added debug.component.js build
0.6.0 / 2012-03-16
==================
* Added support for "-" prefix in DEBUG [Vinay Pulim]
* Added `.enabled` flag to the node version [TooTallNate]
0.5.0 / 2012-02-02
==================
* Added: humanize diffs. Closes #8
* Added `debug.disable()` to the CS variant
* Removed padding. Closes #10
* Fixed: persist client-side variant again. Closes #9
0.4.0 / 2012-02-01
==================
* Added browser variant support for older browsers [TooTallNate]
* Added `debug.enable('project:*')` to browser variant [TooTallNate]
* Added padding to diff (moved it to the right)
0.3.0 / 2012-01-26
==================
* Added millisecond diff when isatty, otherwise UTC string
0.2.0 / 2012-01-22
==================
* Added wildcard support
0.1.0 / 2011-12-02
==================
* Added: remove colors unless stderr isatty [TooTallNate]
0.0.1 / 2010-01-03
==================
* Initial release

View File

@@ -0,0 +1,19 @@
(The MIT License)
Copyright (c) 2014 TJ Holowaychuk <tj@vision-media.ca>
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.

View File

@@ -0,0 +1,50 @@
# get Makefile directory name: http://stackoverflow.com/a/5982798/376773
THIS_MAKEFILE_PATH:=$(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST))
THIS_DIR:=$(shell cd $(dir $(THIS_MAKEFILE_PATH));pwd)
# BIN directory
BIN := $(THIS_DIR)/node_modules/.bin
# Path
PATH := node_modules/.bin:$(PATH)
SHELL := /bin/bash
# applications
NODE ?= $(shell which node)
YARN ?= $(shell which yarn)
PKG ?= $(if $(YARN),$(YARN),$(NODE) $(shell which npm))
BROWSERIFY ?= $(NODE) $(BIN)/browserify
.FORCE:
install: node_modules
node_modules: package.json
@NODE_ENV= $(PKG) install
@touch node_modules
lint: .FORCE
eslint browser.js debug.js index.js node.js
test-node: .FORCE
istanbul cover node_modules/mocha/bin/_mocha -- test/**.js
test-browser: .FORCE
mkdir -p dist
@$(BROWSERIFY) \
--standalone debug \
. > dist/debug.js
karma start --single-run
rimraf dist
test: .FORCE
concurrently \
"make test-node" \
"make test-browser"
coveralls:
cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js
.PHONY: all install clean distclean

View File

@@ -0,0 +1,312 @@
# debug
[![Build Status](https://travis-ci.org/visionmedia/debug.svg?branch=master)](https://travis-ci.org/visionmedia/debug) [![Coverage Status](https://coveralls.io/repos/github/visionmedia/debug/badge.svg?branch=master)](https://coveralls.io/github/visionmedia/debug?branch=master) [![Slack](https://visionmedia-community-slackin.now.sh/badge.svg)](https://visionmedia-community-slackin.now.sh/) [![OpenCollective](https://opencollective.com/debug/backers/badge.svg)](#backers)
[![OpenCollective](https://opencollective.com/debug/sponsors/badge.svg)](#sponsors)
A tiny node.js debugging utility modelled after node core's debugging technique.
**Discussion around the V3 API is under way [here](https://github.com/visionmedia/debug/issues/370)**
## Installation
```bash
$ npm install debug
```
## Usage
`debug` exposes a function; simply pass this function the name of your module, and it will return a decorated version of `console.error` for you to pass debug statements to. This will allow you to toggle the debug output for different parts of your module as well as the module as a whole.
Example _app.js_:
```js
var debug = require('debug')('http')
, http = require('http')
, name = 'My App';
// fake app
debug('booting %s', name);
http.createServer(function(req, res){
debug(req.method + ' ' + req.url);
res.end('hello\n');
}).listen(3000, function(){
debug('listening');
});
// fake worker of some kind
require('./worker');
```
Example _worker.js_:
```js
var debug = require('debug')('worker');
setInterval(function(){
debug('doing some work');
}, 1000);
```
The __DEBUG__ environment variable is then used to enable these based on space or comma-delimited names. Here are some examples:
![debug http and worker](http://f.cl.ly/items/18471z1H402O24072r1J/Screenshot.png)
![debug worker](http://f.cl.ly/items/1X413v1a3M0d3C2c1E0i/Screenshot.png)
#### Windows note
On Windows the environment variable is set using the `set` command.
```cmd
set DEBUG=*,-not_this
```
Note that PowerShell uses different syntax to set environment variables.
```cmd
$env:DEBUG = "*,-not_this"
```
Then, run the program to be debugged as usual.
## Millisecond diff
When actively developing an application it can be useful to see when the time spent between one `debug()` call and the next. Suppose for example you invoke `debug()` before requesting a resource, and after as well, the "+NNNms" will show you how much time was spent between calls.
![](http://f.cl.ly/items/2i3h1d3t121M2Z1A3Q0N/Screenshot.png)
When stdout is not a TTY, `Date#toUTCString()` is used, making it more useful for logging the debug information as shown below:
![](http://f.cl.ly/items/112H3i0e0o0P0a2Q2r11/Screenshot.png)
## Conventions
If you're using this in one or more of your libraries, you _should_ use the name of your library so that developers may toggle debugging as desired without guessing names. If you have more than one debuggers you _should_ prefix them with your library name and use ":" to separate features. For example "bodyParser" from Connect would then be "connect:bodyParser".
## Wildcards
The `*` character may be used as a wildcard. Suppose for example your library has debuggers named "connect:bodyParser", "connect:compress", "connect:session", instead of listing all three with `DEBUG=connect:bodyParser,connect:compress,connect:session`, you may simply do `DEBUG=connect:*`, or to run everything using this module simply use `DEBUG=*`.
You can also exclude specific debuggers by prefixing them with a "-" character. For example, `DEBUG=*,-connect:*` would include all debuggers except those starting with "connect:".
## Environment Variables
When running through Node.js, you can set a few environment variables that will
change the behavior of the debug logging:
| Name | Purpose |
|-----------|-------------------------------------------------|
| `DEBUG` | Enables/disables specific debugging namespaces. |
| `DEBUG_COLORS`| Whether or not to use colors in the debug output. |
| `DEBUG_DEPTH` | Object inspection depth. |
| `DEBUG_SHOW_HIDDEN` | Shows hidden properties on inspected objects. |
__Note:__ The environment variables beginning with `DEBUG_` end up being
converted into an Options object that gets used with `%o`/`%O` formatters.
See the Node.js documentation for
[`util.inspect()`](https://nodejs.org/api/util.html#util_util_inspect_object_options)
for the complete list.
## Formatters
Debug uses [printf-style](https://wikipedia.org/wiki/Printf_format_string) formatting. Below are the officially supported formatters:
| Formatter | Representation |
|-----------|----------------|
| `%O` | Pretty-print an Object on multiple lines. |
| `%o` | Pretty-print an Object all on a single line. |
| `%s` | String. |
| `%d` | Number (both integer and float). |
| `%j` | JSON. Replaced with the string '[Circular]' if the argument contains circular references. |
| `%%` | Single percent sign ('%'). This does not consume an argument. |
### Custom formatters
You can add custom formatters by extending the `debug.formatters` object. For example, if you wanted to add support for rendering a Buffer as hex with `%h`, you could do something like:
```js
const createDebug = require('debug')
createDebug.formatters.h = (v) => {
return v.toString('hex')
}
// …elsewhere
const debug = createDebug('foo')
debug('this is hex: %h', new Buffer('hello world'))
// foo this is hex: 68656c6c6f20776f726c6421 +0ms
```
## Browser support
You can build a browser-ready script using [browserify](https://github.com/substack/node-browserify),
or just use the [browserify-as-a-service](https://wzrd.in/) [build](https://wzrd.in/standalone/debug@latest),
if you don't want to build it yourself.
Debug's enable state is currently persisted by `localStorage`.
Consider the situation shown below where you have `worker:a` and `worker:b`,
and wish to debug both. You can enable this using `localStorage.debug`:
```js
localStorage.debug = 'worker:*'
```
And then refresh the page.
```js
a = debug('worker:a');
b = debug('worker:b');
setInterval(function(){
a('doing some work');
}, 1000);
setInterval(function(){
b('doing some work');
}, 1200);
```
#### Web Inspector Colors
Colors are also enabled on "Web Inspectors" that understand the `%c` formatting
option. These are WebKit web inspectors, Firefox ([since version
31](https://hacks.mozilla.org/2014/05/editable-box-model-multiple-selection-sublime-text-keys-much-more-firefox-developer-tools-episode-31/))
and the Firebug plugin for Firefox (any version).
Colored output looks something like:
![](https://cloud.githubusercontent.com/assets/71256/3139768/b98c5fd8-e8ef-11e3-862a-f7253b6f47c6.png)
## Output streams
By default `debug` will log to stderr, however this can be configured per-namespace by overriding the `log` method:
Example _stdout.js_:
```js
var debug = require('debug');
var error = debug('app:error');
// by default stderr is used
error('goes to stderr!');
var log = debug('app:log');
// set this namespace to log via console.log
log.log = console.log.bind(console); // don't forget to bind to console!
log('goes to stdout');
error('still goes to stderr!');
// set all output to go via console.info
// overrides all per-namespace log settings
debug.log = console.info.bind(console);
error('now goes to stdout via console.info');
log('still goes to stdout, but via console.info now');
```
## Authors
- TJ Holowaychuk
- Nathan Rajlich
- Andrew Rhyne
## Backers
Support us with a monthly donation and help us continue our activities. [[Become a backer](https://opencollective.com/debug#backer)]
<a href="https://opencollective.com/debug/backer/0/website" target="_blank"><img src="https://opencollective.com/debug/backer/0/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/1/website" target="_blank"><img src="https://opencollective.com/debug/backer/1/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/2/website" target="_blank"><img src="https://opencollective.com/debug/backer/2/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/3/website" target="_blank"><img src="https://opencollective.com/debug/backer/3/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/4/website" target="_blank"><img src="https://opencollective.com/debug/backer/4/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/5/website" target="_blank"><img src="https://opencollective.com/debug/backer/5/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/6/website" target="_blank"><img src="https://opencollective.com/debug/backer/6/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/7/website" target="_blank"><img src="https://opencollective.com/debug/backer/7/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/8/website" target="_blank"><img src="https://opencollective.com/debug/backer/8/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/9/website" target="_blank"><img src="https://opencollective.com/debug/backer/9/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/10/website" target="_blank"><img src="https://opencollective.com/debug/backer/10/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/11/website" target="_blank"><img src="https://opencollective.com/debug/backer/11/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/12/website" target="_blank"><img src="https://opencollective.com/debug/backer/12/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/13/website" target="_blank"><img src="https://opencollective.com/debug/backer/13/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/14/website" target="_blank"><img src="https://opencollective.com/debug/backer/14/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/15/website" target="_blank"><img src="https://opencollective.com/debug/backer/15/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/16/website" target="_blank"><img src="https://opencollective.com/debug/backer/16/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/17/website" target="_blank"><img src="https://opencollective.com/debug/backer/17/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/18/website" target="_blank"><img src="https://opencollective.com/debug/backer/18/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/19/website" target="_blank"><img src="https://opencollective.com/debug/backer/19/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/20/website" target="_blank"><img src="https://opencollective.com/debug/backer/20/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/21/website" target="_blank"><img src="https://opencollective.com/debug/backer/21/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/22/website" target="_blank"><img src="https://opencollective.com/debug/backer/22/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/23/website" target="_blank"><img src="https://opencollective.com/debug/backer/23/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/24/website" target="_blank"><img src="https://opencollective.com/debug/backer/24/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/25/website" target="_blank"><img src="https://opencollective.com/debug/backer/25/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/26/website" target="_blank"><img src="https://opencollective.com/debug/backer/26/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/27/website" target="_blank"><img src="https://opencollective.com/debug/backer/27/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/28/website" target="_blank"><img src="https://opencollective.com/debug/backer/28/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/29/website" target="_blank"><img src="https://opencollective.com/debug/backer/29/avatar.svg"></a>
## Sponsors
Become a sponsor and get your logo on our README on Github with a link to your site. [[Become a sponsor](https://opencollective.com/debug#sponsor)]
<a href="https://opencollective.com/debug/sponsor/0/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/0/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/1/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/1/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/2/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/2/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/3/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/3/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/4/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/4/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/5/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/5/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/6/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/6/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/7/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/7/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/8/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/8/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/9/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/9/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/10/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/10/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/11/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/11/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/12/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/12/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/13/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/13/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/14/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/14/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/15/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/15/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/16/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/16/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/17/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/17/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/18/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/18/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/19/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/19/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/20/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/20/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/21/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/21/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/22/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/22/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/23/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/23/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/24/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/24/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/25/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/25/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/26/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/26/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/27/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/27/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/28/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/28/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/29/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/29/avatar.svg"></a>
## License
(The MIT License)
Copyright (c) 2014-2016 TJ Holowaychuk &lt;tj@vision-media.ca&gt;
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.

View File

@@ -0,0 +1,19 @@
{
"name": "debug",
"repo": "visionmedia/debug",
"description": "small debugging utility",
"version": "2.6.9",
"keywords": [
"debug",
"log",
"debugger"
],
"main": "src/browser.js",
"scripts": [
"src/browser.js",
"src/debug.js"
],
"dependencies": {
"rauchg/ms.js": "0.7.1"
}
}

View File

@@ -0,0 +1,70 @@
// Karma configuration
// Generated on Fri Dec 16 2016 13:09:51 GMT+0000 (UTC)
module.exports = function(config) {
config.set({
// base path that will be used to resolve all patterns (eg. files, exclude)
basePath: '',
// frameworks to use
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
frameworks: ['mocha', 'chai', 'sinon'],
// list of files / patterns to load in the browser
files: [
'dist/debug.js',
'test/*spec.js'
],
// list of files to exclude
exclude: [
'src/node.js'
],
// preprocess matching files before serving them to the browser
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
preprocessors: {
},
// test results reporter to use
// possible values: 'dots', 'progress'
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
reporters: ['progress'],
// web server port
port: 9876,
// enable / disable colors in the output (reporters and logs)
colors: true,
// level of logging
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
logLevel: config.LOG_INFO,
// enable / disable watching file and executing tests whenever any file changes
autoWatch: true,
// start these browsers
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
browsers: ['PhantomJS'],
// Continuous Integration mode
// if true, Karma captures browsers, runs the tests and exits
singleRun: false,
// Concurrency level
// how many browser should be started simultaneous
concurrency: Infinity
})
}

View File

@@ -0,0 +1 @@
module.exports = require('./src/node');

View File

@@ -0,0 +1,49 @@
{
"name": "debug",
"version": "2.6.9",
"repository": {
"type": "git",
"url": "git://github.com/visionmedia/debug.git"
},
"description": "small debugging utility",
"keywords": [
"debug",
"log",
"debugger"
],
"author": "TJ Holowaychuk <tj@vision-media.ca>",
"contributors": [
"Nathan Rajlich <nathan@tootallnate.net> (http://n8.io)",
"Andrew Rhyne <rhyneandrew@gmail.com>"
],
"license": "MIT",
"dependencies": {
"ms": "2.0.0"
},
"devDependencies": {
"browserify": "9.0.3",
"chai": "^3.5.0",
"concurrently": "^3.1.0",
"coveralls": "^2.11.15",
"eslint": "^3.12.1",
"istanbul": "^0.4.5",
"karma": "^1.3.0",
"karma-chai": "^0.1.0",
"karma-mocha": "^1.3.0",
"karma-phantomjs-launcher": "^1.0.2",
"karma-sinon": "^1.0.5",
"mocha": "^3.2.0",
"mocha-lcov-reporter": "^1.2.0",
"rimraf": "^2.5.4",
"sinon": "^1.17.6",
"sinon-chai": "^2.8.0"
},
"main": "./src/index.js",
"browser": "./src/browser.js",
"component": {
"scripts": {
"debug/index.js": "browser.js",
"debug/debug.js": "debug.js"
}
}
}

View File

@@ -0,0 +1,185 @@
/**
* 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() {
// NB: In an Electron preload script, document will be defined but not fully
// initialized. Since we know we're in Chrome, we'll just detect this case
// explicitly
if (typeof window !== 'undefined' && window.process && window.process.type === 'renderer') {
return true;
}
// is webkit? http://stackoverflow.com/a/16459606/376773
// document is undefined in react-native: https://github.com/facebook/react-native/pull/1632
return (typeof document !== 'undefined' && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance) ||
// is firebug? http://stackoverflow.com/a/398120/376773
(typeof window !== 'undefined' && window.console && (window.console.firebug || (window.console.exception && window.console.table))) ||
// is firefox >= v31?
// https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages
(typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31) ||
// double check webkit in userAgent just in case we are in a worker
(typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/));
}
/**
* Map %j to `JSON.stringify()`, since no Web Inspectors do that by default.
*/
exports.formatters.j = function(v) {
try {
return JSON.stringify(v);
} catch (err) {
return '[UnexpectedJSONParseError]: ' + err.message;
}
};
/**
* Colorize log arguments if enabled.
*
* @api public
*/
function formatArgs(args) {
var useColors = this.useColors;
args[0] = (useColors ? '%c' : '')
+ this.namespace
+ (useColors ? ' %c' : ' ')
+ args[0]
+ (useColors ? '%c ' : ' ')
+ '+' + exports.humanize(this.diff);
if (!useColors) return;
var c = 'color: ' + this.color;
args.splice(1, 0, c, 'color: inherit')
// 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-zA-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);
}
/**
* 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) {}
// If debug isn't set in LS, and we're in Electron, try to load $DEBUG
if (!r && typeof process !== 'undefined' && 'env' in process) {
r = process.env.DEBUG;
}
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) {}
}

View File

@@ -0,0 +1,202 @@
/**
* This is the common logic for both the Node.js and web browser
* implementations of `debug()`.
*
* Expose `debug()` as the module.
*/
exports = module.exports = createDebug.debug = createDebug['default'] = createDebug;
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, lower or upper-case letter, i.e. "n" and "N".
*/
exports.formatters = {};
/**
* Previous log timestamp.
*/
var prevTime;
/**
* Select a color.
* @param {String} namespace
* @return {Number}
* @api private
*/
function selectColor(namespace) {
var hash = 0, i;
for (i in namespace) {
hash = ((hash << 5) - hash) + namespace.charCodeAt(i);
hash |= 0; // Convert to 32bit integer
}
return exports.colors[Math.abs(hash) % exports.colors.length];
}
/**
* Create a debugger with the given `namespace`.
*
* @param {String} namespace
* @return {Function}
* @api public
*/
function createDebug(namespace) {
function debug() {
// disabled?
if (!debug.enabled) return;
var self = debug;
// set `diff` timestamp
var curr = +new Date();
var ms = curr - (prevTime || curr);
self.diff = ms;
self.prev = prevTime;
self.curr = curr;
prevTime = curr;
// turn the `arguments` into a proper Array
var args = new Array(arguments.length);
for (var i = 0; i < args.length; i++) {
args[i] = arguments[i];
}
args[0] = exports.coerce(args[0]);
if ('string' !== typeof args[0]) {
// anything else let's inspect with %O
args.unshift('%O');
}
// apply any `formatters` transformations
var index = 0;
args[0] = args[0].replace(/%([a-zA-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;
});
// apply env-specific formatting (colors, etc.)
exports.formatArgs.call(self, args);
var logFn = debug.log || exports.log || console.log.bind(console);
logFn.apply(self, args);
}
debug.namespace = namespace;
debug.enabled = exports.enabled(namespace);
debug.useColors = exports.useColors();
debug.color = selectColor(namespace);
// env-specific initialization logic for debug instances
if ('function' === typeof exports.init) {
exports.init(debug);
}
return debug;
}
/**
* 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);
exports.names = [];
exports.skips = [];
var split = (typeof namespaces === 'string' ? 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;
}

View File

@@ -0,0 +1,10 @@
/**
* Detect Electron renderer process, which is node, but we should
* treat as a browser.
*/
if (typeof process !== 'undefined' && process.type === 'renderer') {
module.exports = require('./browser.js');
} else {
module.exports = require('./node.js');
}

View File

@@ -0,0 +1,15 @@
module.exports = inspectorLog;
// black hole
const nullStream = new (require('stream').Writable)();
nullStream._write = () => {};
/**
* Outputs a `console.log()` to the Node.js Inspector console *only*.
*/
function inspectorLog() {
const stdout = console._stdout;
console._stdout = nullStream;
console.log.apply(console, arguments);
console._stdout = stdout;
}

View File

@@ -0,0 +1,248 @@
/**
* Module dependencies.
*/
var tty = require('tty');
var util = require('util');
/**
* This is the Node.js implementation of `debug()`.
*
* Expose `debug()` as the module.
*/
exports = module.exports = require('./debug');
exports.init = init;
exports.log = log;
exports.formatArgs = formatArgs;
exports.save = save;
exports.load = load;
exports.useColors = useColors;
/**
* Colors.
*/
exports.colors = [6, 2, 3, 4, 5, 1];
/**
* Build up the default `inspectOpts` object from the environment variables.
*
* $ DEBUG_COLORS=no DEBUG_DEPTH=10 DEBUG_SHOW_HIDDEN=enabled node script.js
*/
exports.inspectOpts = Object.keys(process.env).filter(function (key) {
return /^debug_/i.test(key);
}).reduce(function (obj, key) {
// camel-case
var prop = key
.substring(6)
.toLowerCase()
.replace(/_([a-z])/g, function (_, k) { return k.toUpperCase() });
// coerce string value into JS value
var val = process.env[key];
if (/^(yes|on|true|enabled)$/i.test(val)) val = true;
else if (/^(no|off|false|disabled)$/i.test(val)) val = false;
else if (val === 'null') val = null;
else val = Number(val);
obj[prop] = val;
return obj;
}, {});
/**
* The file descriptor to write the `debug()` calls to.
* Set the `DEBUG_FD` env variable to override with another value. i.e.:
*
* $ DEBUG_FD=3 node script.js 3>debug.log
*/
var fd = parseInt(process.env.DEBUG_FD, 10) || 2;
if (1 !== fd && 2 !== fd) {
util.deprecate(function(){}, 'except for stderr(2) and stdout(1), any other usage of DEBUG_FD is deprecated. Override debug.log if you want to use a different log function (https://git.io/debug_fd)')()
}
var stream = 1 === fd ? process.stdout :
2 === fd ? process.stderr :
createWritableStdioStream(fd);
/**
* Is stdout a TTY? Colored output is enabled when `true`.
*/
function useColors() {
return 'colors' in exports.inspectOpts
? Boolean(exports.inspectOpts.colors)
: tty.isatty(fd);
}
/**
* Map %o to `util.inspect()`, all on a single line.
*/
exports.formatters.o = function(v) {
this.inspectOpts.colors = this.useColors;
return util.inspect(v, this.inspectOpts)
.split('\n').map(function(str) {
return str.trim()
}).join(' ');
};
/**
* Map %o to `util.inspect()`, allowing multiple lines if needed.
*/
exports.formatters.O = function(v) {
this.inspectOpts.colors = this.useColors;
return util.inspect(v, this.inspectOpts);
};
/**
* Adds ANSI color escape codes if enabled.
*
* @api public
*/
function formatArgs(args) {
var name = this.namespace;
var useColors = this.useColors;
if (useColors) {
var c = this.color;
var prefix = ' \u001b[3' + c + ';1m' + name + ' ' + '\u001b[0m';
args[0] = prefix + args[0].split('\n').join('\n' + prefix);
args.push('\u001b[3' + c + 'm+' + exports.humanize(this.diff) + '\u001b[0m');
} else {
args[0] = new Date().toUTCString()
+ ' ' + name + ' ' + args[0];
}
}
/**
* Invokes `util.format()` with the specified arguments and writes to `stream`.
*/
function log() {
return stream.write(util.format.apply(util, arguments) + '\n');
}
/**
* Save `namespaces`.
*
* @param {String} namespaces
* @api private
*/
function save(namespaces) {
if (null == namespaces) {
// If you set a process.env field to null or undefined, it gets cast to the
// string 'null' or 'undefined'. Just delete instead.
delete process.env.DEBUG;
} else {
process.env.DEBUG = namespaces;
}
}
/**
* Load `namespaces`.
*
* @return {String} returns the previously persisted debug modes
* @api private
*/
function load() {
return process.env.DEBUG;
}
/**
* Copied from `node/src/node.js`.
*
* XXX: It's lame that node doesn't expose this API out-of-the-box. It also
* relies on the undocumented `tty_wrap.guessHandleType()` which is also lame.
*/
function createWritableStdioStream (fd) {
var stream;
var tty_wrap = process.binding('tty_wrap');
// Note stream._type is used for test-module-load-list.js
switch (tty_wrap.guessHandleType(fd)) {
case 'TTY':
stream = new tty.WriteStream(fd);
stream._type = 'tty';
// Hack to have stream not keep the event loop alive.
// See https://github.com/joyent/node/issues/1726
if (stream._handle && stream._handle.unref) {
stream._handle.unref();
}
break;
case 'FILE':
var fs = require('fs');
stream = new fs.SyncWriteStream(fd, { autoClose: false });
stream._type = 'fs';
break;
case 'PIPE':
case 'TCP':
var net = require('net');
stream = new net.Socket({
fd: fd,
readable: false,
writable: true
});
// FIXME Should probably have an option in net.Socket to create a
// stream from an existing fd which is writable only. But for now
// we'll just add this hack and set the `readable` member to false.
// Test: ./node test/fixtures/echo.js < /etc/passwd
stream.readable = false;
stream.read = null;
stream._type = 'pipe';
// FIXME Hack to have stream not keep the event loop alive.
// See https://github.com/joyent/node/issues/1726
if (stream._handle && stream._handle.unref) {
stream._handle.unref();
}
break;
default:
// Probably an error on in uv_guess_handle()
throw new Error('Implement me. Unknown stream file type!');
}
// For supporting legacy API we put the FD here.
stream.fd = fd;
stream._isStdio = true;
return stream;
}
/**
* Init logic for `debug` instances.
*
* Create a new `inspectOpts` object in case `useColors` is set
* differently for a particular `debug` instance.
*/
function init (debug) {
debug.inspectOpts = {};
var keys = Object.keys(exports.inspectOpts);
for (var i = 0; i < keys.length; i++) {
debug.inspectOpts[keys[i]] = exports.inspectOpts[keys[i]];
}
}
/**
* Enable namespaces listed in `process.env.DEBUG` initially.
*/
exports.enable(load());

View File

@@ -0,0 +1 @@
node_modules

View File

@@ -0,0 +1,8 @@
sudo: false
language: node_js
node_js:
- '0.10'
env:
global:
- secure: XcBiD8yReflut9q7leKsigDZ0mI3qTKH+QrNVY8DaqlomJOZw8aOrVuX9Jz12l86ZJ41nbxmKnRNkFzcVr9mbP9YaeTb3DpeOBWmvaoSfud9Wnc16VfXtc1FCcwDhSVcSiM3UtnrmFU5cH+Dw1LPh5PbfylYOS/nJxUvG0FFLqI=
- secure: jNWtEbqhUdQ0xXDHvCYfUbKYeJCi6a7B4LsrcxYCyWWn4NIgncE5x2YbB+FSUUFVYfz0dsn5RKP1oHB99f0laUEo18HBNkrAS/rtyOdVzcpJjbQ6kgSILGjnJD/Ty1B57Rcz3iyev5Y7bLZ6Y1FbDnk/i9/l0faOGz8vTC3Vdkc=

View File

@@ -0,0 +1,12 @@
ui: mocha-qunit
browsers:
- name: chrome
version: latest
- name: firefox
version: latest
- name: safari
version: 5..latest
- name: iphone
version: latest
- name: ie
version: 8..latest

View File

@@ -0,0 +1,42 @@
# 1.1.1 (2016-06-22)
- add more context to errors if they are not instanceof Error
# 1.1.0 (2015-09-29)
- add Emitter#listerCount (to match node v4 api)
# 1.0.2 (2014-08-28)
- remove un-reachable code
- update devDeps
## 1.0.1 / 2014-05-11
- check for console.trace before using it
## 1.0.0 / 2013-12-10
- Update to latest events code from node.js 0.10
- copy tests from node.js
## 0.4.0 / 2011-07-03 ##
- Switching to graphquire@0.8.0
## 0.3.0 / 2011-07-03 ##
- Switching to URL based module require.
## 0.2.0 / 2011-06-10 ##
- Simplified package structure.
- Graphquire for dependency management.
## 0.1.1 / 2011-05-16 ##
- Unhandled errors are logged via console.error
## 0.1.0 / 2011-04-22 ##
- Initial release

View File

@@ -0,0 +1,22 @@
MIT
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.

View File

@@ -0,0 +1,19 @@
# events [![Build Status](https://travis-ci.org/Gozala/events.png?branch=master)](https://travis-ci.org/Gozala/events)
Node's event emitter for all engines.
## Install ##
```
npm install events
```
## Require ##
```javascript
var EventEmitter = require('events').EventEmitter
```
## Usage ##
See the [node.js event emitter docs](http://nodejs.org/api/events.html)

View File

@@ -0,0 +1,302 @@
// 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
} else {
// At least give some kind of context to the user
var err = new Error('Uncaught, unspecified "error" event. (' + er + ')');
err.context = er;
throw err;
}
}
}
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:
args = Array.prototype.slice.call(arguments, 1);
handler.apply(this, args);
}
} else if (isObject(handler)) {
args = Array.prototype.slice.call(arguments, 1);
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) {
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 if (listeners) {
// 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.prototype.listenerCount = function(type) {
if (this._events) {
var evlistener = this._events[type];
if (isFunction(evlistener))
return 1;
else if (evlistener)
return evlistener.length;
}
return 0;
};
EventEmitter.listenerCount = function(emitter, type) {
return emitter.listenerCount(type);
};
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;
}

View File

@@ -0,0 +1,33 @@
{
"name": "events",
"id": "events",
"version": "1.1.1",
"description": "Node's event emitter for all engines.",
"keywords": [
"events",
"eventEmitter",
"eventDispatcher",
"listeners"
],
"author": "Irakli Gozalishvili <rfobic@gmail.com> (http://jeditoolkit.com)",
"repository": {
"type": "git",
"url": "git://github.com/Gozala/events.git",
"web": "https://github.com/Gozala/events"
},
"bugs": {
"url": "http://github.com/Gozala/events/issues/"
},
"main": "./events.js",
"engines": {
"node": ">=0.4.x"
},
"devDependencies": {
"mocha": "~1.21.4",
"zuul": "~1.10.2"
},
"scripts": {
"test": "mocha --ui qunit -- tests/index.js && zuul -- tests/index.js"
},
"license": "MIT"
}

View File

@@ -0,0 +1,63 @@
// 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.
var assert = require('assert');
var events = require('../');
var e = new events.EventEmitter();
var events_new_listener_emited = [];
var listeners_new_listener_emited = [];
var times_hello_emited = 0;
// sanity check
assert.equal(e.addListener, e.on);
e.on('newListener', function(event, listener) {
console.log('newListener: ' + event);
events_new_listener_emited.push(event);
listeners_new_listener_emited.push(listener);
});
function hello(a, b) {
console.log('hello');
times_hello_emited += 1;
assert.equal('a', a);
assert.equal('b', b);
}
e.on('hello', hello);
var foo = function() {};
e.once('foo', foo);
console.log('start');
e.emit('hello', 'a', 'b');
// just make sure that this doesn't throw:
var f = new events.EventEmitter();
f.setMaxListeners(0);
assert.deepEqual(['hello', 'foo'], events_new_listener_emited);
assert.deepEqual([hello, foo], listeners_new_listener_emited);
assert.equal(1, times_hello_emited);

View File

@@ -0,0 +1,86 @@
// 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.
var assert = require('assert');
var events = require('../');
var e = new events.EventEmitter();
// default
for (var i = 0; i < 10; i++) {
e.on('default', function() {});
}
assert.ok(!e._events['default'].hasOwnProperty('warned'));
e.on('default', function() {});
assert.ok(e._events['default'].warned);
// specific
e.setMaxListeners(5);
for (var i = 0; i < 5; i++) {
e.on('specific', function() {});
}
assert.ok(!e._events['specific'].hasOwnProperty('warned'));
e.on('specific', function() {});
assert.ok(e._events['specific'].warned);
// only one
e.setMaxListeners(1);
e.on('only one', function() {});
assert.ok(!e._events['only one'].hasOwnProperty('warned'));
e.on('only one', function() {});
assert.ok(e._events['only one'].hasOwnProperty('warned'));
// unlimited
e.setMaxListeners(0);
for (var i = 0; i < 1000; i++) {
e.on('unlimited', function() {});
}
assert.ok(!e._events['unlimited'].hasOwnProperty('warned'));
// process-wide
events.EventEmitter.defaultMaxListeners = 42;
e = new events.EventEmitter();
for (var i = 0; i < 42; ++i) {
e.on('fortytwo', function() {});
}
assert.ok(!e._events['fortytwo'].hasOwnProperty('warned'));
e.on('fortytwo', function() {});
assert.ok(e._events['fortytwo'].hasOwnProperty('warned'));
delete e._events['fortytwo'].warned;
events.EventEmitter.defaultMaxListeners = 44;
e.on('fortytwo', function() {});
assert.ok(!e._events['fortytwo'].hasOwnProperty('warned'));
e.on('fortytwo', function() {});
assert.ok(e._events['fortytwo'].hasOwnProperty('warned'));
// but _maxListeners still has precedence over defaultMaxListeners
events.EventEmitter.defaultMaxListeners = 42;
e = new events.EventEmitter();
e.setMaxListeners(1);
e.on('uno', function() {});
assert.ok(!e._events['uno'].hasOwnProperty('warned'));
e.on('uno', function() {});
assert.ok(e._events['uno'].hasOwnProperty('warned'));
// chainable
assert.strictEqual(e, e.setMaxListeners(1));

View File

@@ -0,0 +1,42 @@
var assert = require('assert');
var mustCallChecks = [];
function runCallChecks() {
var failed_count = 0;
for (var i=0 ; i< mustCallChecks.length; ++i) {
var context = mustCallChecks[i];
if (context.actual === context.expected) {
continue;
}
failed_count++;
console.log('Mismatched %s function calls. Expected %d, actual %d.',
context.name,
context.expected,
context.actual);
console.log(context.stack.split('\n').slice(2).join('\n'));
}
assert(failed_count === 0);
}
after(runCallChecks);
exports.mustCall = function(fn, expected) {
if (typeof expected !== 'number') expected = 1;
var context = {
expected: expected,
actual: 0,
stack: (new Error).stack,
name: fn.name || '<anonymous>'
};
mustCallChecks.push(context);
return function() {
context.actual++;
return fn.apply(this, arguments);
};
};

View File

@@ -0,0 +1,25 @@
require('./legacy-compat');
// we do this to easily wrap each file in a mocha test
// and also have browserify be able to statically analyze this file
var orig_require = require;
var require = function(file) {
test(file, function() {
orig_require(file);
});
}
require('./add-listeners.js');
require('./check-listener-leaks.js');
require('./listener-count.js');
require('./listeners-side-effects.js');
require('./listeners.js');
require('./max-listeners.js');
require('./modify-in-emit.js');
require('./num-args.js');
require('./once.js');
require('./set-max-listeners-side-effects.js');
require('./subclass.js');
require('./remove-all-listeners.js');
require('./remove-listeners.js');

View File

@@ -0,0 +1,18 @@
// sigh... life is hard
if (!global.console) {
console = {}
}
var fns = ['log', 'error', 'trace'];
for (var i=0 ; i<fns.length ; ++i) {
var fn = fns[i];
if (!console[fn]) {
console[fn] = function() {};
}
}
if (!Array.isArray) {
Array.isArray = function(arr) {
return Object.prototype.toString.call(arr) === '[object Array]';
}
}

View File

@@ -0,0 +1,36 @@
// 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.
var assert = require('assert');
var EventEmitter = require('../');
var emitter = new EventEmitter();
emitter.on('foo', function() {});
emitter.on('foo', function() {});
emitter.on('baz', function() {});
// Allow any type
emitter.on(123, function() {});
assert.strictEqual(EventEmitter.listenerCount(emitter, 'foo'), 2);
assert.strictEqual(emitter.listenerCount('foo'), 2);
assert.strictEqual(emitter.listenerCount('bar'), 0);
assert.strictEqual(emitter.listenerCount('baz'), 1);
assert.strictEqual(emitter.listenerCount(123), 1);

View File

@@ -0,0 +1,55 @@
// 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.
var assert = require('assert');
var EventEmitter = require('../').EventEmitter;
var e = new EventEmitter;
var fl; // foo listeners
fl = e.listeners('foo');
assert(Array.isArray(fl));
assert(fl.length === 0);
assert.deepEqual(e._events, {});
e.on('foo', assert.fail);
fl = e.listeners('foo');
assert(e._events.foo === assert.fail);
assert(Array.isArray(fl));
assert(fl.length === 1);
assert(fl[0] === assert.fail);
e.listeners('bar');
assert(!e._events.hasOwnProperty('bar'));
e.on('foo', assert.ok);
fl = e.listeners('foo');
assert(Array.isArray(e._events.foo));
assert(e._events.foo.length === 2);
assert(e._events.foo[0] === assert.fail);
assert(e._events.foo[1] === assert.ok);
assert(Array.isArray(fl));
assert(fl.length === 2);
assert(fl[0] === assert.fail);
assert(fl[1] === assert.ok);

View File

@@ -0,0 +1,51 @@
// 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.
var assert = require('assert');
var events = require('../');
function listener() {}
function listener2() {}
var e1 = new events.EventEmitter();
e1.on('foo', listener);
var fooListeners = e1.listeners('foo');
assert.deepEqual(e1.listeners('foo'), [listener]);
e1.removeAllListeners('foo');
assert.deepEqual(e1.listeners('foo'), []);
assert.deepEqual(fooListeners, [listener]);
var e2 = new events.EventEmitter();
e2.on('foo', listener);
var e2ListenersCopy = e2.listeners('foo');
assert.deepEqual(e2ListenersCopy, [listener]);
assert.deepEqual(e2.listeners('foo'), [listener]);
e2ListenersCopy.push(listener2);
assert.deepEqual(e2.listeners('foo'), [listener]);
assert.deepEqual(e2ListenersCopy, [listener, listener2]);
var e3 = new events.EventEmitter();
e3.on('foo', listener);
var e3ListenersCopy = e3.listeners('foo');
e3.on('foo', listener2);
assert.deepEqual(e3.listeners('foo'), [listener, listener2]);
assert.deepEqual(e3ListenersCopy, [listener]);

View File

@@ -0,0 +1,50 @@
// 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.
var assert = require('assert');
var events = require('../');
var gotEvent = false;
var e = new events.EventEmitter();
e.on('maxListeners', function() {
gotEvent = true;
});
// Should not corrupt the 'maxListeners' queue.
e.setMaxListeners(42);
assert.throws(function() {
e.setMaxListeners(NaN);
});
assert.throws(function() {
e.setMaxListeners(-1);
});
assert.throws(function() {
e.setMaxListeners("and even this");
});
e.emit('maxListeners');
assert(gotEvent);

View File

@@ -0,0 +1,76 @@
// 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.
var assert = require('assert');
var events = require('../');
var callbacks_called = [];
var e = new events.EventEmitter();
function callback1() {
callbacks_called.push('callback1');
e.on('foo', callback2);
e.on('foo', callback3);
e.removeListener('foo', callback1);
}
function callback2() {
callbacks_called.push('callback2');
e.removeListener('foo', callback2);
}
function callback3() {
callbacks_called.push('callback3');
e.removeListener('foo', callback3);
}
e.on('foo', callback1);
assert.equal(1, e.listeners('foo').length);
e.emit('foo');
assert.equal(2, e.listeners('foo').length);
assert.deepEqual(['callback1'], callbacks_called);
e.emit('foo');
assert.equal(0, e.listeners('foo').length);
assert.deepEqual(['callback1', 'callback2', 'callback3'], callbacks_called);
e.emit('foo');
assert.equal(0, e.listeners('foo').length);
assert.deepEqual(['callback1', 'callback2', 'callback3'], callbacks_called);
e.on('foo', callback1);
e.on('foo', callback2);
assert.equal(2, e.listeners('foo').length);
e.removeAllListeners('foo');
assert.equal(0, e.listeners('foo').length);
// Verify that removing callbacks while in emit allows emits to propagate to
// all listeners
callbacks_called = [];
e.on('foo', callback2);
e.on('foo', callback3);
assert.equal(2, e.listeners('foo').length);
e.emit('foo');
assert.deepEqual(['callback2', 'callback3'], callbacks_called);
assert.equal(0, e.listeners('foo').length);

View File

@@ -0,0 +1,44 @@
// 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.
var assert = require('assert');
var events = require('../');
var e = new events.EventEmitter(),
num_args_emited = [];
e.on('numArgs', function() {
var numArgs = arguments.length;
console.log('numArgs: ' + numArgs);
num_args_emited.push(numArgs);
});
console.log('start');
e.emit('numArgs');
e.emit('numArgs', null);
e.emit('numArgs', null, null);
e.emit('numArgs', null, null, null);
e.emit('numArgs', null, null, null, null);
e.emit('numArgs', null, null, null, null, null);
assert.deepEqual([0, 1, 2, 3, 4, 5], num_args_emited);

View File

@@ -0,0 +1,59 @@
// 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.
var assert = require('assert');
var events = require('../');
var e = new events.EventEmitter();
var times_hello_emited = 0;
e.once('hello', function(a, b) {
times_hello_emited++;
});
e.emit('hello', 'a', 'b');
e.emit('hello', 'a', 'b');
e.emit('hello', 'a', 'b');
e.emit('hello', 'a', 'b');
var remove = function() {
assert.fail(1, 0, 'once->foo should not be emitted', '!');
};
e.once('foo', remove);
e.removeListener('foo', remove);
e.emit('foo');
var times_recurse_emitted = 0;
e.once('e', function() {
e.emit('e');
times_recurse_emitted++;
});
e.once('e', function() {
times_recurse_emitted++;
});
e.emit('e');
assert.equal(1, times_hello_emited);
assert.equal(2, times_recurse_emitted);

View File

@@ -0,0 +1,80 @@
// 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.
var common = require('./common');
var assert = require('assert');
var events = require('../');
var after_checks = [];
after(function() {
for (var i=0 ; i<after_checks.length ; ++i) {
after_checks[i]();
}
});
function expect(expected) {
var actual = [];
after_checks.push(function() {
assert.deepEqual(actual.sort(), expected.sort());
});
function listener(name) {
actual.push(name)
}
return common.mustCall(listener, expected.length);
}
function listener() {}
var e1 = new events.EventEmitter();
e1.on('foo', listener);
e1.on('bar', listener);
e1.on('baz', listener);
e1.on('baz', listener);
var fooListeners = e1.listeners('foo');
var barListeners = e1.listeners('bar');
var bazListeners = e1.listeners('baz');
e1.on('removeListener', expect(['bar', 'baz', 'baz']));
e1.removeAllListeners('bar');
e1.removeAllListeners('baz');
assert.deepEqual(e1.listeners('foo'), [listener]);
assert.deepEqual(e1.listeners('bar'), []);
assert.deepEqual(e1.listeners('baz'), []);
// after calling removeAllListeners,
// the old listeners array should stay unchanged
assert.deepEqual(fooListeners, [listener]);
assert.deepEqual(barListeners, [listener]);
assert.deepEqual(bazListeners, [listener, listener]);
// after calling removeAllListeners,
// new listeners arrays are different from the old
assert.notEqual(e1.listeners('bar'), barListeners);
assert.notEqual(e1.listeners('baz'), bazListeners);
var e2 = new events.EventEmitter();
e2.on('foo', listener);
e2.on('bar', listener);
// expect LIFO order
e2.on('removeListener', expect(['foo', 'bar', 'removeListener']));
e2.on('removeListener', expect(['foo', 'bar']));
e2.removeAllListeners();
console.error(e2);
assert.deepEqual([], e2.listeners('foo'));
assert.deepEqual([], e2.listeners('bar'));

View File

@@ -0,0 +1,84 @@
// 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.
var common = require('./common');
var assert = require('assert');
var events = require('../');
var count = 0;
function listener1() {
console.log('listener1');
count++;
}
function listener2() {
console.log('listener2');
count++;
}
function listener3() {
console.log('listener3');
count++;
}
function remove1() {
assert(0);
}
function remove2() {
assert(0);
}
var e1 = new events.EventEmitter();
e1.on('hello', listener1);
e1.on('removeListener', common.mustCall(function(name, cb) {
assert.equal(name, 'hello');
assert.equal(cb, listener1);
}));
e1.removeListener('hello', listener1);
assert.deepEqual([], e1.listeners('hello'));
var e2 = new events.EventEmitter();
e2.on('hello', listener1);
e2.on('removeListener', assert.fail);
e2.removeListener('hello', listener2);
assert.deepEqual([listener1], e2.listeners('hello'));
var e3 = new events.EventEmitter();
e3.on('hello', listener1);
e3.on('hello', listener2);
e3.on('removeListener', common.mustCall(function(name, cb) {
assert.equal(name, 'hello');
assert.equal(cb, listener1);
}));
e3.removeListener('hello', listener1);
assert.deepEqual([listener2], e3.listeners('hello'));
var e4 = new events.EventEmitter();
e4.on('removeListener', common.mustCall(function(name, cb) {
if (cb !== remove1) return;
this.removeListener('quux', remove2);
this.emit('quux');
}, 2));
e4.on('quux', remove1);
e4.on('quux', remove2);
e4.removeListener('quux', remove1);

View File

@@ -0,0 +1,29 @@
// 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.
var assert = require('assert');
var events = require('../');
var e = new events.EventEmitter;
assert.deepEqual(e._events, {});
e.setMaxListeners(5);
assert.deepEqual(e._events, {});

View File

@@ -0,0 +1,51 @@
// 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.
var assert = require('assert');
var EventEmitter = require('../').EventEmitter;
var util = require('util');
util.inherits(MyEE, EventEmitter);
function MyEE(cb) {
this.once(1, cb);
this.emit(1);
this.removeAllListeners();
EventEmitter.call(this);
}
var called = false;
var myee = new MyEE(function() {
called = true;
});
util.inherits(ErrorEE, EventEmitter);
function ErrorEE() {
this.emit('error', new Error('blerg'));
}
assert.throws(function() {
new ErrorEE();
}, /blerg/);
assert(called);
assert.deepEqual(myee._events, {});

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2013 Julian Gruber <julian@juliangruber.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,38 @@
# isarray
`Array#isArray` for older browsers and deprecated Node.js versions.
[![build status](https://secure.travis-ci.org/juliangruber/isarray.svg)](http://travis-ci.org/juliangruber/isarray)
[![downloads](https://img.shields.io/npm/dm/isarray.svg)](https://www.npmjs.org/package/isarray)
[![browser support](https://ci.testling.com/juliangruber/isarray.png)
](https://ci.testling.com/juliangruber/isarray)
__Just use Array.isArray directly__, unless you need to support those older versions.
## Usage
```js
var isArray = require('isarray');
console.log(isArray([])); // => true
console.log(isArray({})); // => false
```
## Installation
With [npm](https://npmjs.org) do
```bash
$ npm install isarray
```
Then bundle for the browser with
[browserify](https://github.com/substack/node-browserify).
## Sponsors
This module is proudly supported by my [Sponsors](https://github.com/juliangruber/sponsors)!
Do you want to support modules like this to improve their quality, stability and weigh in on new features? Then please consider donating to my [Patreon](https://www.patreon.com/juliangruber). Not sure how much of my modules you're using? Try [feross/thanks](https://github.com/feross/thanks)!

View File

@@ -0,0 +1,5 @@
var toString = {}.toString;
module.exports = Array.isArray || function (arr) {
return toString.call(arr) == '[object Array]';
};

View File

@@ -0,0 +1,48 @@
{
"name": "isarray",
"description": "Array#isArray for older browsers",
"version": "2.0.5",
"repository": {
"type": "git",
"url": "git://github.com/juliangruber/isarray.git"
},
"homepage": "https://github.com/juliangruber/isarray",
"main": "index.js",
"files": [
"index.js"
],
"dependencies": {},
"devDependencies": {
"tape": "~2.13.4"
},
"keywords": [
"browser",
"isarray",
"array"
],
"author": {
"name": "Julian Gruber",
"email": "mail@juliangruber.com",
"url": "http://juliangruber.com"
},
"license": "MIT",
"testling": {
"files": "test.js",
"browsers": [
"ie/8..latest",
"firefox/17..latest",
"firefox/nightly",
"chrome/22..latest",
"chrome/canary",
"opera/12..latest",
"opera/next",
"safari/5.1..latest",
"ipad/6.0..latest",
"iphone/6.0..latest",
"android-browser/4.2..latest"
]
},
"scripts": {
"test": "tape test.js"
}
}

View File

@@ -0,0 +1,152 @@
/**
* 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]
* @throws {Error} throw an error if val is not a non-empty string or a number
* @return {String|Number}
* @api public
*/
module.exports = function(val, options) {
options = options || {};
var type = typeof val;
if (type === 'string' && val.length > 0) {
return parse(val);
} else if (type === 'number' && isNaN(val) === false) {
return options.long ? fmtLong(val) : fmtShort(val);
}
throw new Error(
'val is not a non-empty string or a valid number. val=' +
JSON.stringify(val)
);
};
/**
* Parse the given `str` and return milliseconds.
*
* @param {String} str
* @return {Number}
* @api private
*/
function parse(str) {
str = String(str);
if (str.length > 100) {
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;
default:
return undefined;
}
}
/**
* Short format for `ms`.
*
* @param {Number} ms
* @return {String}
* @api private
*/
function fmtShort(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 fmtLong(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';
}

View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2016 Zeit, Inc.
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.

View File

@@ -0,0 +1,37 @@
{
"name": "ms",
"version": "2.0.0",
"description": "Tiny milisecond conversion utility",
"repository": "zeit/ms",
"main": "./index",
"files": [
"index.js"
],
"scripts": {
"precommit": "lint-staged",
"lint": "eslint lib/* bin/*",
"test": "mocha tests.js"
},
"eslintConfig": {
"extends": "eslint:recommended",
"env": {
"node": true,
"es6": true
}
},
"lint-staged": {
"*.js": [
"npm run lint",
"prettier --single-quote --write",
"git add"
]
},
"license": "MIT",
"devDependencies": {
"eslint": "3.19.0",
"expect.js": "0.3.1",
"husky": "0.13.3",
"lint-staged": "3.4.1",
"mocha": "3.4.1"
}
}

View File

@@ -0,0 +1,51 @@
# ms
[![Build Status](https://travis-ci.org/zeit/ms.svg?branch=master)](https://travis-ci.org/zeit/ms)
[![Slack Channel](http://zeit-slackin.now.sh/badge.svg)](https://zeit.chat/)
Use this package to easily convert various time formats to milliseconds.
## Examples
```js
ms('2 days') // 172800000
ms('1d') // 86400000
ms('10h') // 36000000
ms('2.5 hrs') // 9000000
ms('2h') // 7200000
ms('1m') // 60000
ms('5s') // 5000
ms('1y') // 31557600000
ms('100') // 100
```
### Convert from milliseconds
```js
ms(60000) // "1m"
ms(2 * 60000) // "2m"
ms(ms('10 hours')) // "10h"
```
### Time format written-out
```js
ms(60000, { long: true }) // "1 minute"
ms(2 * 60000, { long: true }) // "2 minutes"
ms(ms('10 hours'), { long: true }) // "10 hours"
```
## Features
- Works both in [node](https://nodejs.org) and in the browser.
- If a number is supplied to `ms`, a string with a unit is returned.
- If a string that contains the number is supplied, it returns it as a number (e.g.: it returns `100` for `'100'`).
- If you pass a string with a number and a valid unit, the number of equivalent ms is returned.
## Caught a bug?
1. [Fork](https://help.github.com/articles/fork-a-repo/) this repository to your own GitHub account and then [clone](https://help.github.com/articles/cloning-a-repository/) it to your local device
2. Link the package to the global module directory: `npm link`
3. Within the module you want to test your local development instance of ms, just link it to the dependencies: `npm link ms`. Instead of the default one from npm, node will now use your clone of ms!
As always, you can run the tests using: `npm test`

View File

@@ -0,0 +1,39 @@
# changes log
## 5.7
* Add `minVersion` method
## 5.6
* Move boolean `loose` param to an options object, with
backwards-compatibility protection.
* Add ability to opt out of special prerelease version handling with
the `includePrerelease` option flag.
## 5.5
* Add version coercion capabilities
## 5.4
* Add intersection checking
## 5.3
* Add `minSatisfying` method
## 5.2
* Add `prerelease(v)` that returns prerelease components
## 5.1
* Add Backus-Naur for ranges
* Remove excessively cute inspection methods
## 5.0
* Remove AMD/Browserified build artifacts
* Fix ltr and gtr when using the `*` range
* Fix for range `*` with a prerelease identifier

View File

@@ -0,0 +1,15 @@
The ISC License
Copyright (c) Isaac Z. Schlueter and Contributors
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

View File

@@ -0,0 +1,412 @@
semver(1) -- The semantic versioner for npm
===========================================
## Install
```bash
npm install --save semver
````
## Usage
As a node module:
```js
const semver = require('semver')
semver.valid('1.2.3') // '1.2.3'
semver.valid('a.b.c') // null
semver.clean(' =v1.2.3 ') // '1.2.3'
semver.satisfies('1.2.3', '1.x || >=2.5.0 || 5.0.0 - 7.2.3') // true
semver.gt('1.2.3', '9.8.7') // false
semver.lt('1.2.3', '9.8.7') // true
semver.minVersion('>=1.0.0') // '1.0.0'
semver.valid(semver.coerce('v2')) // '2.0.0'
semver.valid(semver.coerce('42.6.7.9.3-alpha')) // '42.6.7'
```
As a command-line utility:
```
$ semver -h
A JavaScript implementation of the https://semver.org/ specification
Copyright Isaac Z. Schlueter
Usage: semver [options] <version> [<version> [...]]
Prints valid versions sorted by SemVer precedence
Options:
-r --range <range>
Print versions that match the specified range.
-i --increment [<level>]
Increment a version by the specified level. Level can
be one of: major, minor, patch, premajor, preminor,
prepatch, or prerelease. Default level is 'patch'.
Only one version may be specified.
--preid <identifier>
Identifier to be used to prefix premajor, preminor,
prepatch or prerelease version increments.
-l --loose
Interpret versions and ranges loosely
-p --include-prerelease
Always include prerelease versions in range matching
-c --coerce
Coerce a string into SemVer if possible
(does not imply --loose)
Program exits successfully if any valid version satisfies
all supplied ranges, and prints all satisfying versions.
If no satisfying versions are found, then exits failure.
Versions are printed in ascending order, so supplying
multiple versions to the utility will just sort them.
```
## Versions
A "version" is described by the `v2.0.0` specification found at
<https://semver.org/>.
A leading `"="` or `"v"` character is stripped off and ignored.
## Ranges
A `version range` is a set of `comparators` which specify versions
that satisfy the range.
A `comparator` is composed of an `operator` and a `version`. The set
of primitive `operators` is:
* `<` Less than
* `<=` Less than or equal to
* `>` Greater than
* `>=` Greater than or equal to
* `=` Equal. If no operator is specified, then equality is assumed,
so this operator is optional, but MAY be included.
For example, the comparator `>=1.2.7` would match the versions
`1.2.7`, `1.2.8`, `2.5.3`, and `1.3.9`, but not the versions `1.2.6`
or `1.1.0`.
Comparators can be joined by whitespace to form a `comparator set`,
which is satisfied by the **intersection** of all of the comparators
it includes.
A range is composed of one or more comparator sets, joined by `||`. A
version matches a range if and only if every comparator in at least
one of the `||`-separated comparator sets is satisfied by the version.
For example, the range `>=1.2.7 <1.3.0` would match the versions
`1.2.7`, `1.2.8`, and `1.2.99`, but not the versions `1.2.6`, `1.3.0`,
or `1.1.0`.
The range `1.2.7 || >=1.2.9 <2.0.0` would match the versions `1.2.7`,
`1.2.9`, and `1.4.6`, but not the versions `1.2.8` or `2.0.0`.
### Prerelease Tags
If a version has a prerelease tag (for example, `1.2.3-alpha.3`) then
it will only be allowed to satisfy comparator sets if at least one
comparator with the same `[major, minor, patch]` tuple also has a
prerelease tag.
For example, the range `>1.2.3-alpha.3` would be allowed to match the
version `1.2.3-alpha.7`, but it would *not* be satisfied by
`3.4.5-alpha.9`, even though `3.4.5-alpha.9` is technically "greater
than" `1.2.3-alpha.3` according to the SemVer sort rules. The version
range only accepts prerelease tags on the `1.2.3` version. The
version `3.4.5` *would* satisfy the range, because it does not have a
prerelease flag, and `3.4.5` is greater than `1.2.3-alpha.7`.
The purpose for this behavior is twofold. First, prerelease versions
frequently are updated very quickly, and contain many breaking changes
that are (by the author's design) not yet fit for public consumption.
Therefore, by default, they are excluded from range matching
semantics.
Second, a user who has opted into using a prerelease version has
clearly indicated the intent to use *that specific* set of
alpha/beta/rc versions. By including a prerelease tag in the range,
the user is indicating that they are aware of the risk. However, it
is still not appropriate to assume that they have opted into taking a
similar risk on the *next* set of prerelease versions.
Note that this behavior can be suppressed (treating all prerelease
versions as if they were normal versions, for the purpose of range
matching) by setting the `includePrerelease` flag on the options
object to any
[functions](https://github.com/npm/node-semver#functions) that do
range matching.
#### Prerelease Identifiers
The method `.inc` takes an additional `identifier` string argument that
will append the value of the string as a prerelease identifier:
```javascript
semver.inc('1.2.3', 'prerelease', 'beta')
// '1.2.4-beta.0'
```
command-line example:
```bash
$ semver 1.2.3 -i prerelease --preid beta
1.2.4-beta.0
```
Which then can be used to increment further:
```bash
$ semver 1.2.4-beta.0 -i prerelease
1.2.4-beta.1
```
### Advanced Range Syntax
Advanced range syntax desugars to primitive comparators in
deterministic ways.
Advanced ranges may be combined in the same way as primitive
comparators using white space or `||`.
#### Hyphen Ranges `X.Y.Z - A.B.C`
Specifies an inclusive set.
* `1.2.3 - 2.3.4` := `>=1.2.3 <=2.3.4`
If a partial version is provided as the first version in the inclusive
range, then the missing pieces are replaced with zeroes.
* `1.2 - 2.3.4` := `>=1.2.0 <=2.3.4`
If a partial version is provided as the second version in the
inclusive range, then all versions that start with the supplied parts
of the tuple are accepted, but nothing that would be greater than the
provided tuple parts.
* `1.2.3 - 2.3` := `>=1.2.3 <2.4.0`
* `1.2.3 - 2` := `>=1.2.3 <3.0.0`
#### X-Ranges `1.2.x` `1.X` `1.2.*` `*`
Any of `X`, `x`, or `*` may be used to "stand in" for one of the
numeric values in the `[major, minor, patch]` tuple.
* `*` := `>=0.0.0` (Any version satisfies)
* `1.x` := `>=1.0.0 <2.0.0` (Matching major version)
* `1.2.x` := `>=1.2.0 <1.3.0` (Matching major and minor versions)
A partial version range is treated as an X-Range, so the special
character is in fact optional.
* `""` (empty string) := `*` := `>=0.0.0`
* `1` := `1.x.x` := `>=1.0.0 <2.0.0`
* `1.2` := `1.2.x` := `>=1.2.0 <1.3.0`
#### Tilde Ranges `~1.2.3` `~1.2` `~1`
Allows patch-level changes if a minor version is specified on the
comparator. Allows minor-level changes if not.
* `~1.2.3` := `>=1.2.3 <1.(2+1).0` := `>=1.2.3 <1.3.0`
* `~1.2` := `>=1.2.0 <1.(2+1).0` := `>=1.2.0 <1.3.0` (Same as `1.2.x`)
* `~1` := `>=1.0.0 <(1+1).0.0` := `>=1.0.0 <2.0.0` (Same as `1.x`)
* `~0.2.3` := `>=0.2.3 <0.(2+1).0` := `>=0.2.3 <0.3.0`
* `~0.2` := `>=0.2.0 <0.(2+1).0` := `>=0.2.0 <0.3.0` (Same as `0.2.x`)
* `~0` := `>=0.0.0 <(0+1).0.0` := `>=0.0.0 <1.0.0` (Same as `0.x`)
* `~1.2.3-beta.2` := `>=1.2.3-beta.2 <1.3.0` Note that prereleases in
the `1.2.3` version will be allowed, if they are greater than or
equal to `beta.2`. So, `1.2.3-beta.4` would be allowed, but
`1.2.4-beta.2` would not, because it is a prerelease of a
different `[major, minor, patch]` tuple.
#### Caret Ranges `^1.2.3` `^0.2.5` `^0.0.4`
Allows changes that do not modify the left-most non-zero digit in the
`[major, minor, patch]` tuple. In other words, this allows patch and
minor updates for versions `1.0.0` and above, patch updates for
versions `0.X >=0.1.0`, and *no* updates for versions `0.0.X`.
Many authors treat a `0.x` version as if the `x` were the major
"breaking-change" indicator.
Caret ranges are ideal when an author may make breaking changes
between `0.2.4` and `0.3.0` releases, which is a common practice.
However, it presumes that there will *not* be breaking changes between
`0.2.4` and `0.2.5`. It allows for changes that are presumed to be
additive (but non-breaking), according to commonly observed practices.
* `^1.2.3` := `>=1.2.3 <2.0.0`
* `^0.2.3` := `>=0.2.3 <0.3.0`
* `^0.0.3` := `>=0.0.3 <0.0.4`
* `^1.2.3-beta.2` := `>=1.2.3-beta.2 <2.0.0` Note that prereleases in
the `1.2.3` version will be allowed, if they are greater than or
equal to `beta.2`. So, `1.2.3-beta.4` would be allowed, but
`1.2.4-beta.2` would not, because it is a prerelease of a
different `[major, minor, patch]` tuple.
* `^0.0.3-beta` := `>=0.0.3-beta <0.0.4` Note that prereleases in the
`0.0.3` version *only* will be allowed, if they are greater than or
equal to `beta`. So, `0.0.3-pr.2` would be allowed.
When parsing caret ranges, a missing `patch` value desugars to the
number `0`, but will allow flexibility within that value, even if the
major and minor versions are both `0`.
* `^1.2.x` := `>=1.2.0 <2.0.0`
* `^0.0.x` := `>=0.0.0 <0.1.0`
* `^0.0` := `>=0.0.0 <0.1.0`
A missing `minor` and `patch` values will desugar to zero, but also
allow flexibility within those values, even if the major version is
zero.
* `^1.x` := `>=1.0.0 <2.0.0`
* `^0.x` := `>=0.0.0 <1.0.0`
### Range Grammar
Putting all this together, here is a Backus-Naur grammar for ranges,
for the benefit of parser authors:
```bnf
range-set ::= range ( logical-or range ) *
logical-or ::= ( ' ' ) * '||' ( ' ' ) *
range ::= hyphen | simple ( ' ' simple ) * | ''
hyphen ::= partial ' - ' partial
simple ::= primitive | partial | tilde | caret
primitive ::= ( '<' | '>' | '>=' | '<=' | '=' ) partial
partial ::= xr ( '.' xr ( '.' xr qualifier ? )? )?
xr ::= 'x' | 'X' | '*' | nr
nr ::= '0' | ['1'-'9'] ( ['0'-'9'] ) *
tilde ::= '~' partial
caret ::= '^' partial
qualifier ::= ( '-' pre )? ( '+' build )?
pre ::= parts
build ::= parts
parts ::= part ( '.' part ) *
part ::= nr | [-0-9A-Za-z]+
```
## Functions
All methods and classes take a final `options` object argument. All
options in this object are `false` by default. The options supported
are:
- `loose` Be more forgiving about not-quite-valid semver strings.
(Any resulting output will always be 100% strict compliant, of
course.) For backwards compatibility reasons, if the `options`
argument is a boolean value instead of an object, it is interpreted
to be the `loose` param.
- `includePrerelease` Set to suppress the [default
behavior](https://github.com/npm/node-semver#prerelease-tags) of
excluding prerelease tagged versions from ranges unless they are
explicitly opted into.
Strict-mode Comparators and Ranges will be strict about the SemVer
strings that they parse.
* `valid(v)`: Return the parsed version, or null if it's not valid.
* `inc(v, release)`: Return the version incremented by the release
type (`major`, `premajor`, `minor`, `preminor`, `patch`,
`prepatch`, or `prerelease`), or null if it's not valid
* `premajor` in one call will bump the version up to the next major
version and down to a prerelease of that major version.
`preminor`, and `prepatch` work the same way.
* If called from a non-prerelease version, the `prerelease` will work the
same as `prepatch`. It increments the patch version, then makes a
prerelease. If the input version is already a prerelease it simply
increments it.
* `prerelease(v)`: Returns an array of prerelease components, or null
if none exist. Example: `prerelease('1.2.3-alpha.1') -> ['alpha', 1]`
* `major(v)`: Return the major version number.
* `minor(v)`: Return the minor version number.
* `patch(v)`: Return the patch version number.
* `intersects(r1, r2, loose)`: Return true if the two supplied ranges
or comparators intersect.
* `parse(v)`: Attempt to parse a string as a semantic version, returning either
a `SemVer` object or `null`.
### Comparison
* `gt(v1, v2)`: `v1 > v2`
* `gte(v1, v2)`: `v1 >= v2`
* `lt(v1, v2)`: `v1 < v2`
* `lte(v1, v2)`: `v1 <= v2`
* `eq(v1, v2)`: `v1 == v2` This is true if they're logically equivalent,
even if they're not the exact same string. You already know how to
compare strings.
* `neq(v1, v2)`: `v1 != v2` The opposite of `eq`.
* `cmp(v1, comparator, v2)`: Pass in a comparison string, and it'll call
the corresponding function above. `"==="` and `"!=="` do simple
string comparison, but are included for completeness. Throws if an
invalid comparison string is provided.
* `compare(v1, v2)`: Return `0` if `v1 == v2`, or `1` if `v1` is greater, or `-1` if
`v2` is greater. Sorts in ascending order if passed to `Array.sort()`.
* `rcompare(v1, v2)`: The reverse of compare. Sorts an array of versions
in descending order when passed to `Array.sort()`.
* `diff(v1, v2)`: Returns difference between two versions by the release type
(`major`, `premajor`, `minor`, `preminor`, `patch`, `prepatch`, or `prerelease`),
or null if the versions are the same.
### Comparators
* `intersects(comparator)`: Return true if the comparators intersect
### Ranges
* `validRange(range)`: Return the valid range or null if it's not valid
* `satisfies(version, range)`: Return true if the version satisfies the
range.
* `maxSatisfying(versions, range)`: Return the highest version in the list
that satisfies the range, or `null` if none of them do.
* `minSatisfying(versions, range)`: Return the lowest version in the list
that satisfies the range, or `null` if none of them do.
* `minVersion(range)`: Return the lowest version that can possibly match
the given range.
* `gtr(version, range)`: Return `true` if version is greater than all the
versions possible in the range.
* `ltr(version, range)`: Return `true` if version is less than all the
versions possible in the range.
* `outside(version, range, hilo)`: Return true if the version is outside
the bounds of the range in either the high or low direction. The
`hilo` argument must be either the string `'>'` or `'<'`. (This is
the function called by `gtr` and `ltr`.)
* `intersects(range)`: Return true if any of the ranges comparators intersect
Note that, since ranges may be non-contiguous, a version might not be
greater than a range, less than a range, *or* satisfy a range! For
example, the range `1.2 <1.2.9 || >2.0.0` would have a hole from `1.2.9`
until `2.0.0`, so the version `1.2.10` would not be greater than the
range (because `2.0.1` satisfies, which is higher), nor less than the
range (since `1.2.8` satisfies, which is lower), and it also does not
satisfy the range.
If you want to know if a version satisfies or does not satisfy a
range, use the `satisfies(version, range)` function.
### Coercion
* `coerce(version)`: Coerces a string to semver if possible
This aims to provide a very forgiving translation of a non-semver string to
semver. It looks for the first digit in a string, and consumes all
remaining characters which satisfy at least a partial semver (e.g., `1`,
`1.2`, `1.2.3`) up to the max permitted length (256 characters). Longer
versions are simply truncated (`4.6.3.9.2-alpha2` becomes `4.6.3`). All
surrounding text is simply ignored (`v3.4 replaces v3.3.1` becomes
`3.4.0`). Only text which lacks digits will fail coercion (`version one`
is not valid). The maximum length for any semver component considered for
coercion is 16 characters; longer components will be ignored
(`10000000000000000.4.7.4` becomes `4.7.4`). The maximum value for any
semver component is `Number.MAX_SAFE_INTEGER || (2**53 - 1)`; higher value
components are invalid (`9999999999999999.4.7.4` is likely invalid).

View File

@@ -0,0 +1,160 @@
#!/usr/bin/env node
// Standalone semver comparison program.
// Exits successfully and prints matching version(s) if
// any supplied version is valid and passes all tests.
var argv = process.argv.slice(2)
var versions = []
var range = []
var inc = null
var version = require('../package.json').version
var loose = false
var includePrerelease = false
var coerce = false
var identifier
var semver = require('../semver')
var reverse = false
var options = {}
main()
function main () {
if (!argv.length) return help()
while (argv.length) {
var a = argv.shift()
var indexOfEqualSign = a.indexOf('=')
if (indexOfEqualSign !== -1) {
a = a.slice(0, indexOfEqualSign)
argv.unshift(a.slice(indexOfEqualSign + 1))
}
switch (a) {
case '-rv': case '-rev': case '--rev': case '--reverse':
reverse = true
break
case '-l': case '--loose':
loose = true
break
case '-p': case '--include-prerelease':
includePrerelease = true
break
case '-v': case '--version':
versions.push(argv.shift())
break
case '-i': case '--inc': case '--increment':
switch (argv[0]) {
case 'major': case 'minor': case 'patch': case 'prerelease':
case 'premajor': case 'preminor': case 'prepatch':
inc = argv.shift()
break
default:
inc = 'patch'
break
}
break
case '--preid':
identifier = argv.shift()
break
case '-r': case '--range':
range.push(argv.shift())
break
case '-c': case '--coerce':
coerce = true
break
case '-h': case '--help': case '-?':
return help()
default:
versions.push(a)
break
}
}
var options = { loose: loose, includePrerelease: includePrerelease }
versions = versions.map(function (v) {
return coerce ? (semver.coerce(v) || { version: v }).version : v
}).filter(function (v) {
return semver.valid(v)
})
if (!versions.length) return fail()
if (inc && (versions.length !== 1 || range.length)) { return failInc() }
for (var i = 0, l = range.length; i < l; i++) {
versions = versions.filter(function (v) {
return semver.satisfies(v, range[i], options)
})
if (!versions.length) return fail()
}
return success(versions)
}
function failInc () {
console.error('--inc can only be used on a single version with no range')
fail()
}
function fail () { process.exit(1) }
function success () {
var compare = reverse ? 'rcompare' : 'compare'
versions.sort(function (a, b) {
return semver[compare](a, b, options)
}).map(function (v) {
return semver.clean(v, options)
}).map(function (v) {
return inc ? semver.inc(v, inc, options, identifier) : v
}).forEach(function (v, i, _) { console.log(v) })
}
function help () {
console.log(['SemVer ' + version,
'',
'A JavaScript implementation of the https://semver.org/ specification',
'Copyright Isaac Z. Schlueter',
'',
'Usage: semver [options] <version> [<version> [...]]',
'Prints valid versions sorted by SemVer precedence',
'',
'Options:',
'-r --range <range>',
' Print versions that match the specified range.',
'',
'-i --increment [<level>]',
' Increment a version by the specified level. Level can',
' be one of: major, minor, patch, premajor, preminor,',
" prepatch, or prerelease. Default level is 'patch'.",
' Only one version may be specified.',
'',
'--preid <identifier>',
' Identifier to be used to prefix premajor, preminor,',
' prepatch or prerelease version increments.',
'',
'-l --loose',
' Interpret versions and ranges loosely',
'',
'-p --include-prerelease',
' Always include prerelease versions in range matching',
'',
'-c --coerce',
' Coerce a string into SemVer if possible',
' (does not imply --loose)',
'',
'Program exits successfully if any valid version satisfies',
'all supplied ranges, and prints all satisfying versions.',
'',
'If no satisfying versions are found, then exits failure.',
'',
'Versions are printed in ascending order, so supplying',
'multiple versions to the utility will just sort them.'
].join('\n'))
}

View File

@@ -0,0 +1,28 @@
{
"name": "semver",
"version": "5.7.1",
"description": "The semantic version parser used by npm.",
"main": "semver.js",
"scripts": {
"test": "tap",
"preversion": "npm test",
"postversion": "npm publish",
"postpublish": "git push origin --all; git push origin --tags"
},
"devDependencies": {
"tap": "^13.0.0-rc.18"
},
"license": "ISC",
"repository": "https://github.com/npm/node-semver",
"bin": {
"semver": "./bin/semver"
},
"files": [
"bin",
"range.bnf",
"semver.js"
],
"tap": {
"check-coverage": true
}
}

View File

@@ -0,0 +1,16 @@
range-set ::= range ( logical-or range ) *
logical-or ::= ( ' ' ) * '||' ( ' ' ) *
range ::= hyphen | simple ( ' ' simple ) * | ''
hyphen ::= partial ' - ' partial
simple ::= primitive | partial | tilde | caret
primitive ::= ( '<' | '>' | '>=' | '<=' | '=' ) partial
partial ::= xr ( '.' xr ( '.' xr qualifier ? )? )?
xr ::= 'x' | 'X' | '*' | nr
nr ::= '0' | [1-9] ( [0-9] ) *
tilde ::= '~' partial
caret ::= '^' partial
qualifier ::= ( '-' pre )? ( '+' build )?
pre ::= parts
build ::= parts
parts ::= part ( '.' part ) *
part ::= nr | [-0-9A-Za-z]+

File diff suppressed because it is too large Load Diff

140
themes/keepit/node_modules/algoliasearch/package.json generated vendored Normal file
View File

@@ -0,0 +1,140 @@
{
"name": "algoliasearch",
"version": "3.35.1",
"description": "AlgoliaSearch API JavaScript client",
"main": "index.js",
"jsdelivr": "./dist/algoliasearch.min.js",
"browser": {
"./index.js": "./src/browser/builds/algoliasearch.js",
"./lite.js": "./src/browser/builds/algoliasearchLite.js"
},
"scripts": {
"build": "./scripts/build",
"dev": "APP_ENV=development DEBUG=zuul* zuul --no-coverage --local 8080 -- test/run-browser.js",
"dev-integration": "APP_ENV=development DEBUG=zuul* zuul --no-coverage --local 8080 -- test/run-integration.js",
"examples": "http-server . -a 0.0.0.0",
"watch": "watchify index.js -d -v -s algoliasearch -o dist/algoliasearch.js",
"release": "git clean dist/ -f && git checkout dist/ && yarn && ./scripts/release && APP_ENV=production npm run build",
"test": "./scripts/test",
"test-ci:lint": "./scripts/lint",
"test-ci:bundlesize": "./scripts/bundlesize",
"test-ci:unit-node": "./scripts/test-node-unit",
"test-ci:integration-node": "./scripts/test-node-integration",
"test-ci:unit-browsers": "./scripts/test-browser unit",
"test-ci:integration-browsers": "./scripts/test-browser integration",
"lint": "./scripts/lint"
},
"browserify": {
"transform": [
"envify"
]
},
"bundlesize": [
{
"path": "./dist/algoliasearch.?(jquery|angular).min.js",
"maxSize": "20 kB"
},
{
"path": "./dist/algoliasearchLite.min.js",
"maxSize": "13 kB"
}
],
"repository": {
"type": "git",
"url": "git://github.com/algolia/algoliasearch-client-js.git"
},
"keywords": [
"algolia",
"search",
"search api",
"instant search",
"realtime",
"autocomplete"
],
"homepage": "https://www.algolia.com/doc/api-client/javascript/",
"bugs": "https://github.com/algolia/algoliasearch-client-js/issues",
"author": {
"name": "Algolia SAS",
"url": "https://www.algolia.com"
},
"contributors": [
{
"name": "Algolia Team <support@algolia.com>",
"url": "http://www.algolia.com"
}
],
"files": [
"src",
"dist",
"plugins",
"index.js",
"lite.js",
"reactnative.js",
"bower.json"
],
"dependencies": {
"agentkeepalive": "^2.2.0",
"debug": "^2.6.9",
"envify": "^4.0.0",
"es6-promise": "^4.1.0",
"events": "^1.1.0",
"foreach": "^2.0.5",
"global": "^4.3.2",
"inherits": "^2.0.1",
"isarray": "^2.0.1",
"load-script": "^1.0.0",
"object-keys": "^1.0.11",
"querystring-es3": "^0.2.1",
"reduce": "^1.0.1",
"semver": "^5.1.0",
"tunnel-agent": "^0.6.0"
},
"devDependencies": {
"angular": "^1.6.4",
"array.from": "^1.0.3",
"async": "^1.5.2",
"babel-eslint": "^4.1.6",
"bowser": "1.9.3",
"browserify": "^14.5.0",
"bulk-require": "^1.0.0",
"bulkify": "^1.2.0",
"bundle-collapser": "^1.2.1",
"bundlesize": "^0.15.3",
"chance": "^1.0.9",
"compression": "^1.6.2",
"deumdify": "^1.2.4",
"domready": "0.3.0",
"eslint": "^1.7.3",
"eslint-config-airbnb": "^0.1.0",
"eslint-config-algolia": "4.2.0",
"eslint-plugin-react": "^3.6.3",
"express": "^4.15.3",
"faux-jax": "^5.0.6",
"http-proxy": "^1.13.3",
"http-server": "^0.10.0",
"jquery": "^3.2.1",
"jquery-ajax-transport-xdomainrequest": "^1.0.4",
"lodash-compat": "^3.10.2",
"morgan": "^1.8.2",
"mversion": "^1.10.1",
"pretty-bytes": "^2.0.1",
"proxy": "^0.2.4",
"proxy-agent": "^2.0.0",
"self-signed": "^1.3.1",
"server-destroy-vvo": "^1.0.1",
"sinon": "^1.17.7",
"tap-bail": "^1.0.0",
"tap-dot": "^1.0.5",
"tape": "^4.5.1",
"uglify-js": "2.6.4",
"url-parse": "^1.1.9",
"watchify": "^3.9.0",
"webpack": "^1.13.1",
"xhr": "^2.4.0",
"zuul": "^3.12.0"
},
"engines": {
"node": ">=0.8"
},
"license": "MIT"
}

View File

@@ -0,0 +1,6 @@
'use strict';
// this file serves as a way to require the angularjs module
// in a commonJS way easily:
// require('algoliasearch/plugins/angular');
module.exports = require('../src/browser/builds/algoliasearch.angular.js');

View File

@@ -0,0 +1,6 @@
'use strict';
// this file serves as a way to easily require the jQuery module
// in a commonJS way:
// require('algoliasearch/plugins/jquery');
module.exports = require('../src/browser/builds/algoliasearch.jquery.js');

View File

@@ -0,0 +1,4 @@
'use strict';
// Alias for the react native build of the client.
module.exports = require('./src/reactnative/builds/algoliasearch.reactnative.js');

View File

@@ -0,0 +1,767 @@
module.exports = AlgoliaSearch;
var Index = require('./Index.js');
var deprecate = require('./deprecate.js');
var deprecatedMessage = require('./deprecatedMessage.js');
var AlgoliaSearchCore = require('./AlgoliaSearchCore.js');
var inherits = require('inherits');
var errors = require('./errors');
function AlgoliaSearch() {
AlgoliaSearchCore.apply(this, arguments);
}
inherits(AlgoliaSearch, AlgoliaSearchCore);
/*
* Delete an index
*
* @param indexName the name of index to delete
* @param callback the result callback called with two arguments
* error: null or Error('message')
* content: the server answer that contains the task ID
*/
AlgoliaSearch.prototype.deleteIndex = function(indexName, callback) {
return this._jsonRequest({
method: 'DELETE',
url: '/1/indexes/' + encodeURIComponent(indexName),
hostType: 'write',
callback: callback
});
};
/**
* Move an existing index.
* @param srcIndexName the name of index to copy.
* @param dstIndexName the new index name that will contains a copy of
* srcIndexName (destination will be overriten if it already exist).
* @param callback the result callback called with two arguments
* error: null or Error('message')
* content: the server answer that contains the task ID
*/
AlgoliaSearch.prototype.moveIndex = function(srcIndexName, dstIndexName, callback) {
var postObj = {
operation: 'move', destination: dstIndexName
};
return this._jsonRequest({
method: 'POST',
url: '/1/indexes/' + encodeURIComponent(srcIndexName) + '/operation',
body: postObj,
hostType: 'write',
callback: callback
});
};
/**
* Copy an existing index.
* @param srcIndexName the name of index to copy.
* @param dstIndexName the new index name that will contains a copy
* of srcIndexName (destination will be overriten if it already exist).
* @param scope an array of scopes to copy: ['settings', 'synonyms', 'rules']
* @param callback the result callback called with two arguments
* error: null or Error('message')
* content: the server answer that contains the task ID
*/
AlgoliaSearch.prototype.copyIndex = function(srcIndexName, dstIndexName, scopeOrCallback, _callback) {
var postObj = {
operation: 'copy',
destination: dstIndexName
};
var callback = _callback;
if (typeof scopeOrCallback === 'function') {
// oops, old behaviour of third argument being a function
callback = scopeOrCallback;
} else if (Array.isArray(scopeOrCallback) && scopeOrCallback.length > 0) {
postObj.scope = scopeOrCallback;
} else if (typeof scopeOrCallback !== 'undefined') {
throw new Error('the scope given to `copyIndex` was not an array with settings, synonyms or rules');
}
return this._jsonRequest({
method: 'POST',
url: '/1/indexes/' + encodeURIComponent(srcIndexName) + '/operation',
body: postObj,
hostType: 'write',
callback: callback
});
};
/**
* Return last log entries.
* @param offset Specify the first entry to retrieve (0-based, 0 is the most recent log entry).
* @param length Specify the maximum number of entries to retrieve starting
* at offset. Maximum allowed value: 1000.
* @param type Specify the maximum number of entries to retrieve starting
* at offset. Maximum allowed value: 1000.
* @param callback the result callback called with two arguments
* error: null or Error('message')
* content: the server answer that contains the task ID
*/
AlgoliaSearch.prototype.getLogs = function(offset, length, callback) {
var clone = require('./clone.js');
var params = {};
if (typeof offset === 'object') {
// getLogs(params)
params = clone(offset);
callback = length;
} else if (arguments.length === 0 || typeof offset === 'function') {
// getLogs([cb])
callback = offset;
} else if (arguments.length === 1 || typeof length === 'function') {
// getLogs(1, [cb)]
callback = length;
params.offset = offset;
} else {
// getLogs(1, 2, [cb])
params.offset = offset;
params.length = length;
}
if (params.offset === undefined) params.offset = 0;
if (params.length === undefined) params.length = 10;
return this._jsonRequest({
method: 'GET',
url: '/1/logs?' + this._getSearchParams(params, ''),
hostType: 'read',
callback: callback
});
};
/*
* List all existing indexes (paginated)
*
* @param page The page to retrieve, starting at 0.
* @param callback the result callback called with two arguments
* error: null or Error('message')
* content: the server answer with index list
*/
AlgoliaSearch.prototype.listIndexes = function(page, callback) {
var params = '';
if (page === undefined || typeof page === 'function') {
callback = page;
} else {
params = '?page=' + page;
}
return this._jsonRequest({
method: 'GET',
url: '/1/indexes' + params,
hostType: 'read',
callback: callback
});
};
/*
* Get the index object initialized
*
* @param indexName the name of index
* @param callback the result callback with one argument (the Index instance)
*/
AlgoliaSearch.prototype.initIndex = function(indexName) {
return new Index(this, indexName);
};
AlgoliaSearch.prototype.initAnalytics = function(opts) {
// the actual require must be inside the function, when put outside then you have a cyclic dependency
// not well resolved that ends up making the main "./index.js" (main module, the agloliasearch function)
// export an object instead of a function
// Other workarounds:
// - rewrite the lib in ES6, cyclic dependencies may be better supported
// - move initAnalytics to a property on the main module (algoliasearch.initAnalytics),
// same as places.
// The current API was made mostly to mimic the one made in PHP
var createAnalyticsClient = require('./createAnalyticsClient.js');
return createAnalyticsClient(this.applicationID, this.apiKey, opts);
};
/*
* @deprecated use client.listApiKeys
*/
AlgoliaSearch.prototype.listUserKeys = deprecate(function(callback) {
return this.listApiKeys(callback);
}, deprecatedMessage('client.listUserKeys()', 'client.listApiKeys()'));
/*
* List all existing api keys with their associated ACLs
*
* @param callback the result callback called with two arguments
* error: null or Error('message')
* content: the server answer with api keys list
*/
AlgoliaSearch.prototype.listApiKeys = function(callback) {
return this._jsonRequest({
method: 'GET',
url: '/1/keys',
hostType: 'read',
callback: callback
});
};
/*
* @deprecated see client.getApiKey
*/
AlgoliaSearch.prototype.getUserKeyACL = deprecate(function(key, callback) {
return this.getApiKey(key, callback);
}, deprecatedMessage('client.getUserKeyACL()', 'client.getApiKey()'));
/*
* Get an API key
*
* @param key
* @param callback the result callback called with two arguments
* error: null or Error('message')
* content: the server answer with the right API key
*/
AlgoliaSearch.prototype.getApiKey = function(key, callback) {
return this._jsonRequest({
method: 'GET',
url: '/1/keys/' + key,
hostType: 'read',
callback: callback
});
};
/*
* @deprecated see client.deleteApiKey
*/
AlgoliaSearch.prototype.deleteUserKey = deprecate(function(key, callback) {
return this.deleteApiKey(key, callback);
}, deprecatedMessage('client.deleteUserKey()', 'client.deleteApiKey()'));
/*
* Delete an existing API key
* @param key
* @param callback the result callback called with two arguments
* error: null or Error('message')
* content: the server answer with the date of deletion
*/
AlgoliaSearch.prototype.deleteApiKey = function(key, callback) {
return this._jsonRequest({
method: 'DELETE',
url: '/1/keys/' + key,
hostType: 'write',
callback: callback
});
};
/**
* Restore a deleted API key
*
* @param {String} key - The key to restore
* @param {Function} callback - The result callback called with two arguments
* error: null or Error('message')
* content: the server answer with the restored API key
* @return {Promise|undefined} Returns a promise if no callback given
* @example
* client.restoreApiKey('APIKEY')
* @see {@link https://www.algolia.com/doc/rest-api/search/#restore-api-key|Algolia REST API Documentation}
*/
AlgoliaSearch.prototype.restoreApiKey = function(key, callback) {
return this._jsonRequest({
method: 'POST',
url: '/1/keys/' + key + '/restore',
hostType: 'write',
callback: callback
});
};
/*
@deprecated see client.addApiKey
*/
AlgoliaSearch.prototype.addUserKey = deprecate(function(acls, params, callback) {
return this.addApiKey(acls, params, callback);
}, deprecatedMessage('client.addUserKey()', 'client.addApiKey()'));
/*
* Add a new global API key
*
* @param {string[]} acls - The list of ACL for this key. Defined by an array of strings that
* can contains the following values:
* - search: allow to search (https and http)
* - addObject: allows to add/update an object in the index (https only)
* - deleteObject : allows to delete an existing object (https only)
* - deleteIndex : allows to delete index content (https only)
* - settings : allows to get index settings (https only)
* - editSettings : allows to change index settings (https only)
* @param {Object} [params] - Optionnal parameters to set for the key
* @param {number} params.validity - Number of seconds after which the key will be automatically removed (0 means no time limit for this key)
* @param {number} params.maxQueriesPerIPPerHour - Number of API calls allowed from an IP address per hour
* @param {number} params.maxHitsPerQuery - Number of hits this API key can retrieve in one call
* @param {string[]} params.indexes - Allowed targeted indexes for this key
* @param {string} params.description - A description for your key
* @param {string[]} params.referers - A list of authorized referers
* @param {Object} params.queryParameters - Force the key to use specific query parameters
* @param {Function} callback - The result callback called with two arguments
* error: null or Error('message')
* content: the server answer with the added API key
* @return {Promise|undefined} Returns a promise if no callback given
* @example
* client.addApiKey(['search'], {
* validity: 300,
* maxQueriesPerIPPerHour: 2000,
* maxHitsPerQuery: 3,
* indexes: ['fruits'],
* description: 'Eat three fruits',
* referers: ['*.algolia.com'],
* queryParameters: {
* tagFilters: ['public'],
* }
* })
* @see {@link https://www.algolia.com/doc/rest_api#AddKey|Algolia REST API Documentation}
*/
AlgoliaSearch.prototype.addApiKey = function(acls, params, callback) {
var isArray = require('isarray');
var usage = 'Usage: client.addApiKey(arrayOfAcls[, params, callback])';
if (!isArray(acls)) {
throw new Error(usage);
}
if (arguments.length === 1 || typeof params === 'function') {
callback = params;
params = null;
}
var postObj = {
acl: acls
};
if (params) {
postObj.validity = params.validity;
postObj.maxQueriesPerIPPerHour = params.maxQueriesPerIPPerHour;
postObj.maxHitsPerQuery = params.maxHitsPerQuery;
postObj.indexes = params.indexes;
postObj.description = params.description;
if (params.queryParameters) {
postObj.queryParameters = this._getSearchParams(params.queryParameters, '');
}
postObj.referers = params.referers;
}
return this._jsonRequest({
method: 'POST',
url: '/1/keys',
body: postObj,
hostType: 'write',
callback: callback
});
};
/**
* @deprecated Please use client.addApiKey()
*/
AlgoliaSearch.prototype.addUserKeyWithValidity = deprecate(function(acls, params, callback) {
return this.addApiKey(acls, params, callback);
}, deprecatedMessage('client.addUserKeyWithValidity()', 'client.addApiKey()'));
/**
* @deprecated Please use client.updateApiKey()
*/
AlgoliaSearch.prototype.updateUserKey = deprecate(function(key, acls, params, callback) {
return this.updateApiKey(key, acls, params, callback);
}, deprecatedMessage('client.updateUserKey()', 'client.updateApiKey()'));
/**
* Update an existing API key
* @param {string} key - The key to update
* @param {string[]} acls - The list of ACL for this key. Defined by an array of strings that
* can contains the following values:
* - search: allow to search (https and http)
* - addObject: allows to add/update an object in the index (https only)
* - deleteObject : allows to delete an existing object (https only)
* - deleteIndex : allows to delete index content (https only)
* - settings : allows to get index settings (https only)
* - editSettings : allows to change index settings (https only)
* @param {Object} [params] - Optionnal parameters to set for the key
* @param {number} params.validity - Number of seconds after which the key will be automatically removed (0 means no time limit for this key)
* @param {number} params.maxQueriesPerIPPerHour - Number of API calls allowed from an IP address per hour
* @param {number} params.maxHitsPerQuery - Number of hits this API key can retrieve in one call
* @param {string[]} params.indexes - Allowed targeted indexes for this key
* @param {string} params.description - A description for your key
* @param {string[]} params.referers - A list of authorized referers
* @param {Object} params.queryParameters - Force the key to use specific query parameters
* @param {Function} callback - The result callback called with two arguments
* error: null or Error('message')
* content: the server answer with the modified API key
* @return {Promise|undefined} Returns a promise if no callback given
* @example
* client.updateApiKey('APIKEY', ['search'], {
* validity: 300,
* maxQueriesPerIPPerHour: 2000,
* maxHitsPerQuery: 3,
* indexes: ['fruits'],
* description: 'Eat three fruits',
* referers: ['*.algolia.com'],
* queryParameters: {
* tagFilters: ['public'],
* }
* })
* @see {@link https://www.algolia.com/doc/rest_api#UpdateIndexKey|Algolia REST API Documentation}
*/
AlgoliaSearch.prototype.updateApiKey = function(key, acls, params, callback) {
var isArray = require('isarray');
var usage = 'Usage: client.updateApiKey(key, arrayOfAcls[, params, callback])';
if (!isArray(acls)) {
throw new Error(usage);
}
if (arguments.length === 2 || typeof params === 'function') {
callback = params;
params = null;
}
var putObj = {
acl: acls
};
if (params) {
putObj.validity = params.validity;
putObj.maxQueriesPerIPPerHour = params.maxQueriesPerIPPerHour;
putObj.maxHitsPerQuery = params.maxHitsPerQuery;
putObj.indexes = params.indexes;
putObj.description = params.description;
if (params.queryParameters) {
putObj.queryParameters = this._getSearchParams(params.queryParameters, '');
}
putObj.referers = params.referers;
}
return this._jsonRequest({
method: 'PUT',
url: '/1/keys/' + key,
body: putObj,
hostType: 'write',
callback: callback
});
};
/**
* Initialize a new batch of search queries
* @deprecated use client.search()
*/
AlgoliaSearch.prototype.startQueriesBatch = deprecate(function startQueriesBatchDeprecated() {
this._batch = [];
}, deprecatedMessage('client.startQueriesBatch()', 'client.search()'));
/**
* Add a search query in the batch
* @deprecated use client.search()
*/
AlgoliaSearch.prototype.addQueryInBatch = deprecate(function addQueryInBatchDeprecated(indexName, query, args) {
this._batch.push({
indexName: indexName,
query: query,
params: args
});
}, deprecatedMessage('client.addQueryInBatch()', 'client.search()'));
/**
* Launch the batch of queries using XMLHttpRequest.
* @deprecated use client.search()
*/
AlgoliaSearch.prototype.sendQueriesBatch = deprecate(function sendQueriesBatchDeprecated(callback) {
return this.search(this._batch, callback);
}, deprecatedMessage('client.sendQueriesBatch()', 'client.search()'));
/**
* Perform write operations across multiple indexes.
*
* To reduce the amount of time spent on network round trips,
* you can create, update, or delete several objects in one call,
* using the batch endpoint (all operations are done in the given order).
*
* Available actions:
* - addObject
* - updateObject
* - partialUpdateObject
* - partialUpdateObjectNoCreate
* - deleteObject
*
* https://www.algolia.com/doc/rest_api#Indexes
* @param {Object[]} operations An array of operations to perform
* @return {Promise|undefined} Returns a promise if no callback given
* @example
* client.batch([{
* action: 'addObject',
* indexName: 'clients',
* body: {
* name: 'Bill'
* }
* }, {
* action: 'udpateObject',
* indexName: 'fruits',
* body: {
* objectID: '29138',
* name: 'banana'
* }
* }], cb)
*/
AlgoliaSearch.prototype.batch = function(operations, callback) {
var isArray = require('isarray');
var usage = 'Usage: client.batch(operations[, callback])';
if (!isArray(operations)) {
throw new Error(usage);
}
return this._jsonRequest({
method: 'POST',
url: '/1/indexes/*/batch',
body: {
requests: operations
},
hostType: 'write',
callback: callback
});
};
/**
* Assign or Move a userID to a cluster
*
* @param {string} data.userID The userID to assign to a new cluster
* @param {string} data.cluster The cluster to assign the user to
* @return {Promise|undefined} Returns a promise if no callback given
* @example
* client.assignUserID({ cluster: 'c1-test', userID: 'some-user' });
*/
AlgoliaSearch.prototype.assignUserID = function(data, callback) {
if (!data.userID || !data.cluster) {
throw new errors.AlgoliaSearchError('You have to provide both a userID and cluster', data);
}
return this._jsonRequest({
method: 'POST',
url: '/1/clusters/mapping',
hostType: 'write',
body: {cluster: data.cluster},
callback: callback,
headers: {
'x-algolia-user-id': data.userID
}
});
};
/**
* Assign a array of userIDs to a cluster.
*
* @param {Array} data.userIDs The array of userIDs to assign to a new cluster
* @param {string} data.cluster The cluster to assign the user to
* @return {Promise|undefined} Returns a promise if no callback given
* @example
* client.assignUserIDs({ cluster: 'c1-test', userIDs: ['some-user-1', 'some-user-2'] });
*/
AlgoliaSearch.prototype.assignUserIDs = function(data, callback) {
if (!data.userIDs || !data.cluster) {
throw new errors.AlgoliaSearchError('You have to provide both an array of userIDs and cluster', data);
}
return this._jsonRequest({
method: 'POST',
url: '/1/clusters/mapping/batch',
hostType: 'write',
body: {
cluster: data.cluster,
users: data.userIDs
},
callback: callback
});
};
/**
* Get the top userIDs
*
* (the callback is the second argument)
*
* @return {Promise|undefined} Returns a promise if no callback given
* @example
* client.getTopUserID();
*/
AlgoliaSearch.prototype.getTopUserID = function(callback) {
return this._jsonRequest({
method: 'GET',
url: '/1/clusters/mapping/top',
hostType: 'read',
callback: callback
});
};
/**
* Get userID
*
* @param {string} data.userID The userID to get info about
* @return {Promise|undefined} Returns a promise if no callback given
* @example
* client.getUserID({ userID: 'some-user' });
*/
AlgoliaSearch.prototype.getUserID = function(data, callback) {
if (!data.userID) {
throw new errors.AlgoliaSearchError('You have to provide a userID', {debugData: data});
}
return this._jsonRequest({
method: 'GET',
url: '/1/clusters/mapping/' + data.userID,
hostType: 'read',
callback: callback
});
};
/**
* List all the clusters
*
* (the callback is the second argument)
*
* @return {Promise|undefined} Returns a promise if no callback given
* @example
* client.listClusters();
*/
AlgoliaSearch.prototype.listClusters = function(callback) {
return this._jsonRequest({
method: 'GET',
url: '/1/clusters',
hostType: 'read',
callback: callback
});
};
/**
* List the userIDs
*
* (the callback is the second argument)
*
* @param {string} data.hitsPerPage How many hits on every page
* @param {string} data.page The page to retrieve
* @return {Promise|undefined} Returns a promise if no callback given
* @example
* client.listClusters();
* client.listClusters({ page: 3, hitsPerPage: 30});
*/
AlgoliaSearch.prototype.listUserIDs = function(data, callback) {
return this._jsonRequest({
method: 'GET',
url: '/1/clusters/mapping',
body: data,
hostType: 'read',
callback: callback
});
};
/**
* Remove an userID
*
* @param {string} data.userID The userID to assign to a new cluster
* @return {Promise|undefined} Returns a promise if no callback given
* @example
* client.removeUserID({ userID: 'some-user' });
*/
AlgoliaSearch.prototype.removeUserID = function(data, callback) {
if (!data.userID) {
throw new errors.AlgoliaSearchError('You have to provide a userID', {debugData: data});
}
return this._jsonRequest({
method: 'DELETE',
url: '/1/clusters/mapping',
hostType: 'write',
callback: callback,
headers: {
'x-algolia-user-id': data.userID
}
});
};
/**
* Search for userIDs
*
* @param {string} data.cluster The cluster to target
* @param {string} data.query The query to execute
* @param {string} data.hitsPerPage How many hits on every page
* @param {string} data.page The page to retrieve
* @return {Promise|undefined} Returns a promise if no callback given
* @example
* client.searchUserIDs({ cluster: 'c1-test', query: 'some-user' });
* client.searchUserIDs({
* cluster: "c1-test",
* query: "some-user",
* page: 3,
* hitsPerPage: 2
* });
*/
AlgoliaSearch.prototype.searchUserIDs = function(data, callback) {
return this._jsonRequest({
method: 'POST',
url: '/1/clusters/mapping/search',
body: data,
hostType: 'read',
callback: callback
});
};
/**
* Set strategy for personalization
*
* @param {Object} data
* @param {Object} data.eventsScoring Associate a score to an event
* @param {Object} data.eventsScoring.<eventName> The name of the event
* @param {Number} data.eventsScoring.<eventName>.score The score to associate to <eventName>
* @param {String} data.eventsScoring.<eventName>.type Either "click", "conversion" or "view"
* @param {Object} data.facetsScoring Associate a score to a facet
* @param {Object} data.facetsScoring.<facetName> The name of the facet
* @param {Number} data.facetsScoring.<facetName>.score The score to associate to <facetName>
* @return {Promise|undefined} Returns a promise if no callback given
* @example
* client.setPersonalizationStrategy({
* eventsScoring: {
* "Add to cart": { score: 50, type: "conversion" },
* Purchase: { score: 100, type: "conversion" }
* },
* facetsScoring: {
* brand: { score: 100 },
* categories: { score: 10 }
* }
* });
*/
AlgoliaSearch.prototype.setPersonalizationStrategy = function(data, callback) {
return this._jsonRequest({
method: 'POST',
url: '/1/recommendation/personalization/strategy',
body: data,
hostType: 'write',
callback: callback
});
};
/**
* Get strategy for personalization
*
* @return {Promise|undefined} Returns a promise if no callback given
* @example
* client.getPersonalizationStrategy();
*/
AlgoliaSearch.prototype.getPersonalizationStrategy = function(callback) {
return this._jsonRequest({
method: 'GET',
url: '/1/recommendation/personalization/strategy',
hostType: 'read',
callback: callback
});
};
// environment specific methods
AlgoliaSearch.prototype.destroy = notImplemented;
AlgoliaSearch.prototype.enableRateLimitForward = notImplemented;
AlgoliaSearch.prototype.disableRateLimitForward = notImplemented;
AlgoliaSearch.prototype.useSecuredAPIKey = notImplemented;
AlgoliaSearch.prototype.disableSecuredAPIKey = notImplemented;
AlgoliaSearch.prototype.generateSecuredApiKey = notImplemented;
AlgoliaSearch.prototype.getSecuredApiKeyRemainingValidity = notImplemented;
function notImplemented() {
var message = 'Not implemented in this environment.\n' +
'If you feel this is a mistake, write to support@algolia.com';
throw new errors.AlgoliaSearchError(message);
}

View File

@@ -0,0 +1,968 @@
module.exports = AlgoliaSearchCore;
var errors = require('./errors');
var exitPromise = require('./exitPromise.js');
var IndexCore = require('./IndexCore.js');
var store = require('./store.js');
// We will always put the API KEY in the JSON body in case of too long API KEY,
// to avoid query string being too long and failing in various conditions (our server limit, browser limit,
// proxies limit)
var MAX_API_KEY_LENGTH = 500;
var RESET_APP_DATA_TIMER =
process.env.RESET_APP_DATA_TIMER && parseInt(process.env.RESET_APP_DATA_TIMER, 10) ||
60 * 2 * 1000; // after 2 minutes reset to first host
/*
* Algolia Search library initialization
* https://www.algolia.com/
*
* @param {string} applicationID - Your applicationID, found in your dashboard
* @param {string} apiKey - Your API key, found in your dashboard
* @param {Object} [opts]
* @param {number} [opts.timeout=2000] - The request timeout set in milliseconds,
* another request will be issued after this timeout
* @param {string} [opts.protocol='https:'] - The protocol used to query Algolia Search API.
* Set to 'http:' to force using http.
* @param {Object|Array} [opts.hosts={
* read: [this.applicationID + '-dsn.algolia.net'].concat([
* this.applicationID + '-1.algolianet.com',
* this.applicationID + '-2.algolianet.com',
* this.applicationID + '-3.algolianet.com']
* ]),
* write: [this.applicationID + '.algolia.net'].concat([
* this.applicationID + '-1.algolianet.com',
* this.applicationID + '-2.algolianet.com',
* this.applicationID + '-3.algolianet.com']
* ]) - The hosts to use for Algolia Search API.
* If you provide them, you will less benefit from our HA implementation
*/
function AlgoliaSearchCore(applicationID, apiKey, opts) {
var debug = require('debug')('algoliasearch');
var clone = require('./clone.js');
var isArray = require('isarray');
var map = require('./map.js');
var usage = 'Usage: algoliasearch(applicationID, apiKey, opts)';
if (opts._allowEmptyCredentials !== true && !applicationID) {
throw new errors.AlgoliaSearchError('Please provide an application ID. ' + usage);
}
if (opts._allowEmptyCredentials !== true && !apiKey) {
throw new errors.AlgoliaSearchError('Please provide an API key. ' + usage);
}
this.applicationID = applicationID;
this.apiKey = apiKey;
this.hosts = {
read: [],
write: []
};
opts = opts || {};
this._timeouts = opts.timeouts || {
connect: 1 * 1000, // 500ms connect is GPRS latency
read: 2 * 1000,
write: 30 * 1000
};
// backward compat, if opts.timeout is passed, we use it to configure all timeouts like before
if (opts.timeout) {
this._timeouts.connect = this._timeouts.read = this._timeouts.write = opts.timeout;
}
var protocol = opts.protocol || 'https:';
// while we advocate for colon-at-the-end values: 'http:' for `opts.protocol`
// we also accept `http` and `https`. It's a common error.
if (!/:$/.test(protocol)) {
protocol = protocol + ':';
}
if (protocol !== 'http:' && protocol !== 'https:') {
throw new errors.AlgoliaSearchError('protocol must be `http:` or `https:` (was `' + opts.protocol + '`)');
}
this._checkAppIdData();
if (!opts.hosts) {
var defaultHosts = map(this._shuffleResult, function(hostNumber) {
return applicationID + '-' + hostNumber + '.algolianet.com';
});
// no hosts given, compute defaults
var mainSuffix = (opts.dsn === false ? '' : '-dsn') + '.algolia.net';
this.hosts.read = [this.applicationID + mainSuffix].concat(defaultHosts);
this.hosts.write = [this.applicationID + '.algolia.net'].concat(defaultHosts);
} else if (isArray(opts.hosts)) {
// when passing custom hosts, we need to have a different host index if the number
// of write/read hosts are different.
this.hosts.read = clone(opts.hosts);
this.hosts.write = clone(opts.hosts);
} else {
this.hosts.read = clone(opts.hosts.read);
this.hosts.write = clone(opts.hosts.write);
}
// add protocol and lowercase hosts
this.hosts.read = map(this.hosts.read, prepareHost(protocol));
this.hosts.write = map(this.hosts.write, prepareHost(protocol));
this.extraHeaders = {};
// In some situations you might want to warm the cache
this.cache = opts._cache || {};
this._ua = opts._ua;
this._useCache = opts._useCache === undefined || opts._cache ? true : opts._useCache;
this._useRequestCache = this._useCache && opts._useRequestCache;
this._useFallback = opts.useFallback === undefined ? true : opts.useFallback;
this._setTimeout = opts._setTimeout;
debug('init done, %j', this);
}
/*
* Get the index object initialized
*
* @param indexName the name of index
* @param callback the result callback with one argument (the Index instance)
*/
AlgoliaSearchCore.prototype.initIndex = function(indexName) {
return new IndexCore(this, indexName);
};
/**
* Add an extra field to the HTTP request
*
* @param name the header field name
* @param value the header field value
*/
AlgoliaSearchCore.prototype.setExtraHeader = function(name, value) {
this.extraHeaders[name.toLowerCase()] = value;
};
/**
* Get the value of an extra HTTP header
*
* @param name the header field name
*/
AlgoliaSearchCore.prototype.getExtraHeader = function(name) {
return this.extraHeaders[name.toLowerCase()];
};
/**
* Remove an extra field from the HTTP request
*
* @param name the header field name
*/
AlgoliaSearchCore.prototype.unsetExtraHeader = function(name) {
delete this.extraHeaders[name.toLowerCase()];
};
/**
* Augment sent x-algolia-agent with more data, each agent part
* is automatically separated from the others by a semicolon;
*
* @param algoliaAgent the agent to add
*/
AlgoliaSearchCore.prototype.addAlgoliaAgent = function(algoliaAgent) {
var algoliaAgentWithDelimiter = '; ' + algoliaAgent;
if (this._ua.indexOf(algoliaAgentWithDelimiter) === -1) {
this._ua += algoliaAgentWithDelimiter;
}
};
/*
* Wrapper that try all hosts to maximize the quality of service
*/
AlgoliaSearchCore.prototype._jsonRequest = function(initialOpts) {
this._checkAppIdData();
var requestDebug = require('debug')('algoliasearch:' + initialOpts.url);
var body;
var cacheID;
var additionalUA = initialOpts.additionalUA || '';
var cache = initialOpts.cache;
var client = this;
var tries = 0;
var usingFallback = false;
var hasFallback = client._useFallback && client._request.fallback && initialOpts.fallback;
var headers;
if (
this.apiKey.length > MAX_API_KEY_LENGTH &&
initialOpts.body !== undefined &&
(initialOpts.body.params !== undefined || // index.search()
initialOpts.body.requests !== undefined) // client.search()
) {
initialOpts.body.apiKey = this.apiKey;
headers = this._computeRequestHeaders({
additionalUA: additionalUA,
withApiKey: false,
headers: initialOpts.headers
});
} else {
headers = this._computeRequestHeaders({
additionalUA: additionalUA,
headers: initialOpts.headers
});
}
if (initialOpts.body !== undefined) {
body = safeJSONStringify(initialOpts.body);
}
requestDebug('request start');
var debugData = [];
function doRequest(requester, reqOpts) {
client._checkAppIdData();
var startTime = new Date();
if (client._useCache && !client._useRequestCache) {
cacheID = initialOpts.url;
}
// as we sometime use POST requests to pass parameters (like query='aa'),
// the cacheID must also include the body to be different between calls
if (client._useCache && !client._useRequestCache && body) {
cacheID += '_body_' + reqOpts.body;
}
// handle cache existence
if (isCacheValidWithCurrentID(!client._useRequestCache, cache, cacheID)) {
requestDebug('serving response from cache');
var responseText = cache[cacheID];
// Cache response must match the type of the original one
return client._promise.resolve({
body: JSON.parse(responseText),
responseText: responseText
});
}
// if we reached max tries
if (tries >= client.hosts[initialOpts.hostType].length) {
if (!hasFallback || usingFallback) {
requestDebug('could not get any response');
// then stop
return client._promise.reject(new errors.AlgoliaSearchError(
'Cannot connect to the AlgoliaSearch API.' +
' Send an email to support@algolia.com to report and resolve the issue.' +
' Application id was: ' + client.applicationID, {debugData: debugData}
));
}
requestDebug('switching to fallback');
// let's try the fallback starting from here
tries = 0;
// method, url and body are fallback dependent
reqOpts.method = initialOpts.fallback.method;
reqOpts.url = initialOpts.fallback.url;
reqOpts.jsonBody = initialOpts.fallback.body;
if (reqOpts.jsonBody) {
reqOpts.body = safeJSONStringify(reqOpts.jsonBody);
}
// re-compute headers, they could be omitting the API KEY
headers = client._computeRequestHeaders({
additionalUA: additionalUA,
headers: initialOpts.headers
});
reqOpts.timeouts = client._getTimeoutsForRequest(initialOpts.hostType);
client._setHostIndexByType(0, initialOpts.hostType);
usingFallback = true; // the current request is now using fallback
return doRequest(client._request.fallback, reqOpts);
}
var currentHost = client._getHostByType(initialOpts.hostType);
var url = currentHost + reqOpts.url;
var options = {
body: reqOpts.body,
jsonBody: reqOpts.jsonBody,
method: reqOpts.method,
headers: headers,
timeouts: reqOpts.timeouts,
debug: requestDebug,
forceAuthHeaders: reqOpts.forceAuthHeaders
};
requestDebug('method: %s, url: %s, headers: %j, timeouts: %d',
options.method, url, options.headers, options.timeouts);
if (requester === client._request.fallback) {
requestDebug('using fallback');
}
// `requester` is any of this._request or this._request.fallback
// thus it needs to be called using the client as context
return requester.call(client, url, options).then(success, tryFallback);
function success(httpResponse) {
// compute the status of the response,
//
// When in browser mode, using XDR or JSONP, we have no statusCode available
// So we rely on our API response `status` property.
// But `waitTask` can set a `status` property which is not the statusCode (it's the task status)
// So we check if there's a `message` along `status` and it means it's an error
//
// That's the only case where we have a response.status that's not the http statusCode
var status = httpResponse && httpResponse.body && httpResponse.body.message && httpResponse.body.status ||
// this is important to check the request statusCode AFTER the body eventual
// statusCode because some implementations (jQuery XDomainRequest transport) may
// send statusCode 200 while we had an error
httpResponse.statusCode ||
// When in browser mode, using XDR or JSONP
// we default to success when no error (no response.status && response.message)
// If there was a JSON.parse() error then body is null and it fails
httpResponse && httpResponse.body && 200;
requestDebug('received response: statusCode: %s, computed statusCode: %d, headers: %j',
httpResponse.statusCode, status, httpResponse.headers);
var httpResponseOk = Math.floor(status / 100) === 2;
var endTime = new Date();
debugData.push({
currentHost: currentHost,
headers: removeCredentials(headers),
content: body || null,
contentLength: body !== undefined ? body.length : null,
method: reqOpts.method,
timeouts: reqOpts.timeouts,
url: reqOpts.url,
startTime: startTime,
endTime: endTime,
duration: endTime - startTime,
statusCode: status
});
if (httpResponseOk) {
if (client._useCache && !client._useRequestCache && cache) {
cache[cacheID] = httpResponse.responseText;
}
return {
responseText: httpResponse.responseText,
body: httpResponse.body
};
}
var shouldRetry = Math.floor(status / 100) !== 4;
if (shouldRetry) {
tries += 1;
return retryRequest();
}
requestDebug('unrecoverable error');
// no success and no retry => fail
var unrecoverableError = new errors.AlgoliaSearchError(
httpResponse.body && httpResponse.body.message, {debugData: debugData, statusCode: status}
);
return client._promise.reject(unrecoverableError);
}
function tryFallback(err) {
// error cases:
// While not in fallback mode:
// - CORS not supported
// - network error
// While in fallback mode:
// - timeout
// - network error
// - badly formatted JSONP (script loaded, did not call our callback)
// In both cases:
// - uncaught exception occurs (TypeError)
requestDebug('error: %s, stack: %s', err.message, err.stack);
var endTime = new Date();
debugData.push({
currentHost: currentHost,
headers: removeCredentials(headers),
content: body || null,
contentLength: body !== undefined ? body.length : null,
method: reqOpts.method,
timeouts: reqOpts.timeouts,
url: reqOpts.url,
startTime: startTime,
endTime: endTime,
duration: endTime - startTime
});
if (!(err instanceof errors.AlgoliaSearchError)) {
err = new errors.Unknown(err && err.message, err);
}
tries += 1;
// stop the request implementation when:
if (
// we did not generate this error,
// it comes from a throw in some other piece of code
err instanceof errors.Unknown ||
// server sent unparsable JSON
err instanceof errors.UnparsableJSON ||
// max tries and already using fallback or no fallback
tries >= client.hosts[initialOpts.hostType].length &&
(usingFallback || !hasFallback)) {
// stop request implementation for this command
err.debugData = debugData;
return client._promise.reject(err);
}
// When a timeout occurred, retry by raising timeout
if (err instanceof errors.RequestTimeout) {
return retryRequestWithHigherTimeout();
}
return retryRequest();
}
function retryRequest() {
requestDebug('retrying request');
client._incrementHostIndex(initialOpts.hostType);
return doRequest(requester, reqOpts);
}
function retryRequestWithHigherTimeout() {
requestDebug('retrying request with higher timeout');
client._incrementHostIndex(initialOpts.hostType);
client._incrementTimeoutMultipler();
reqOpts.timeouts = client._getTimeoutsForRequest(initialOpts.hostType);
return doRequest(requester, reqOpts);
}
}
function isCacheValidWithCurrentID(
useRequestCache,
currentCache,
currentCacheID
) {
return (
client._useCache &&
useRequestCache &&
currentCache &&
currentCache[currentCacheID] !== undefined
);
}
function interopCallbackReturn(request, callback) {
if (isCacheValidWithCurrentID(client._useRequestCache, cache, cacheID)) {
request.catch(function() {
// Release the cache on error
delete cache[cacheID];
});
}
if (typeof initialOpts.callback === 'function') {
// either we have a callback
request.then(function okCb(content) {
exitPromise(function() {
initialOpts.callback(null, callback(content));
}, client._setTimeout || setTimeout);
}, function nookCb(err) {
exitPromise(function() {
initialOpts.callback(err);
}, client._setTimeout || setTimeout);
});
} else {
// either we are using promises
return request.then(callback);
}
}
if (client._useCache && client._useRequestCache) {
cacheID = initialOpts.url;
}
// as we sometime use POST requests to pass parameters (like query='aa'),
// the cacheID must also include the body to be different between calls
if (client._useCache && client._useRequestCache && body) {
cacheID += '_body_' + body;
}
if (isCacheValidWithCurrentID(client._useRequestCache, cache, cacheID)) {
requestDebug('serving request from cache');
var maybePromiseForCache = cache[cacheID];
// In case the cache is warmup with value that is not a promise
var promiseForCache = typeof maybePromiseForCache.then !== 'function'
? client._promise.resolve({responseText: maybePromiseForCache})
: maybePromiseForCache;
return interopCallbackReturn(promiseForCache, function(content) {
// In case of the cache request, return the original value
return JSON.parse(content.responseText);
});
}
var request = doRequest(
client._request, {
url: initialOpts.url,
method: initialOpts.method,
body: body,
jsonBody: initialOpts.body,
timeouts: client._getTimeoutsForRequest(initialOpts.hostType),
forceAuthHeaders: initialOpts.forceAuthHeaders
}
);
if (client._useCache && client._useRequestCache && cache) {
cache[cacheID] = request;
}
return interopCallbackReturn(request, function(content) {
// In case of the first request, return the JSON value
return content.body;
});
};
/*
* Transform search param object in query string
* @param {object} args arguments to add to the current query string
* @param {string} params current query string
* @return {string} the final query string
*/
AlgoliaSearchCore.prototype._getSearchParams = function(args, params) {
if (args === undefined || args === null) {
return params;
}
for (var key in args) {
if (key !== null && args[key] !== undefined && args.hasOwnProperty(key)) {
params += params === '' ? '' : '&';
params += key + '=' + encodeURIComponent(Object.prototype.toString.call(args[key]) === '[object Array]' ? safeJSONStringify(args[key]) : args[key]);
}
}
return params;
};
/**
* Compute the headers for a request
*
* @param [string] options.additionalUA semi-colon separated string with other user agents to add
* @param [boolean=true] options.withApiKey Send the api key as a header
* @param [Object] options.headers Extra headers to send
*/
AlgoliaSearchCore.prototype._computeRequestHeaders = function(options) {
var forEach = require('foreach');
var ua = options.additionalUA ?
this._ua + '; ' + options.additionalUA :
this._ua;
var requestHeaders = {
'x-algolia-agent': ua,
'x-algolia-application-id': this.applicationID
};
// browser will inline headers in the url, node.js will use http headers
// but in some situations, the API KEY will be too long (big secured API keys)
// so if the request is a POST and the KEY is very long, we will be asked to not put
// it into headers but in the JSON body
if (options.withApiKey !== false) {
requestHeaders['x-algolia-api-key'] = this.apiKey;
}
if (this.userToken) {
requestHeaders['x-algolia-usertoken'] = this.userToken;
}
if (this.securityTags) {
requestHeaders['x-algolia-tagfilters'] = this.securityTags;
}
forEach(this.extraHeaders, function addToRequestHeaders(value, key) {
requestHeaders[key] = value;
});
if (options.headers) {
forEach(options.headers, function addToRequestHeaders(value, key) {
requestHeaders[key] = value;
});
}
return requestHeaders;
};
/**
* Search through multiple indices at the same time
* @param {Object[]} queries An array of queries you want to run.
* @param {string} queries[].indexName The index name you want to target
* @param {string} [queries[].query] The query to issue on this index. Can also be passed into `params`
* @param {Object} queries[].params Any search param like hitsPerPage, ..
* @param {Function} callback Callback to be called
* @return {Promise|undefined} Returns a promise if no callback given
*/
AlgoliaSearchCore.prototype.search = function(queries, opts, callback) {
var isArray = require('isarray');
var map = require('./map.js');
var usage = 'Usage: client.search(arrayOfQueries[, callback])';
if (!isArray(queries)) {
throw new Error(usage);
}
if (typeof opts === 'function') {
callback = opts;
opts = {};
} else if (opts === undefined) {
opts = {};
}
var client = this;
var postObj = {
requests: map(queries, function prepareRequest(query) {
var params = '';
// allow query.query
// so we are mimicing the index.search(query, params) method
// {indexName:, query:, params:}
if (query.query !== undefined) {
params += 'query=' + encodeURIComponent(query.query);
}
return {
indexName: query.indexName,
params: client._getSearchParams(query.params, params)
};
})
};
var JSONPParams = map(postObj.requests, function prepareJSONPParams(request, requestId) {
return requestId + '=' +
encodeURIComponent(
'/1/indexes/' + encodeURIComponent(request.indexName) + '?' +
request.params
);
}).join('&');
var url = '/1/indexes/*/queries';
if (opts.strategy !== undefined) {
postObj.strategy = opts.strategy;
}
return this._jsonRequest({
cache: this.cache,
method: 'POST',
url: url,
body: postObj,
hostType: 'read',
fallback: {
method: 'GET',
url: '/1/indexes/*',
body: {
params: JSONPParams
}
},
callback: callback
});
};
/**
* Search for facet values
* https://www.algolia.com/doc/rest-api/search#search-for-facet-values
* This is the top-level API for SFFV.
*
* @param {object[]} queries An array of queries to run.
* @param {string} queries[].indexName Index name, name of the index to search.
* @param {object} queries[].params Query parameters.
* @param {string} queries[].params.facetName Facet name, name of the attribute to search for values in.
* Must be declared as a facet
* @param {string} queries[].params.facetQuery Query for the facet search
* @param {string} [queries[].params.*] Any search parameter of Algolia,
* see https://www.algolia.com/doc/api-client/javascript/search#search-parameters
* Pagination is not supported. The page and hitsPerPage parameters will be ignored.
*/
AlgoliaSearchCore.prototype.searchForFacetValues = function(queries) {
var isArray = require('isarray');
var map = require('./map.js');
var usage = 'Usage: client.searchForFacetValues([{indexName, params: {facetName, facetQuery, ...params}}, ...queries])'; // eslint-disable-line max-len
if (!isArray(queries)) {
throw new Error(usage);
}
var client = this;
return client._promise.all(map(queries, function performQuery(query) {
if (
!query ||
query.indexName === undefined ||
query.params.facetName === undefined ||
query.params.facetQuery === undefined
) {
throw new Error(usage);
}
var clone = require('./clone.js');
var omit = require('./omit.js');
var indexName = query.indexName;
var params = query.params;
var facetName = params.facetName;
var filteredParams = omit(clone(params), function(keyName) {
return keyName === 'facetName';
});
var searchParameters = client._getSearchParams(filteredParams, '');
return client._jsonRequest({
cache: client.cache,
method: 'POST',
url:
'/1/indexes/' +
encodeURIComponent(indexName) +
'/facets/' +
encodeURIComponent(facetName) +
'/query',
hostType: 'read',
body: {params: searchParameters}
});
}));
};
/**
* Set the extra security tagFilters header
* @param {string|array} tags The list of tags defining the current security filters
*/
AlgoliaSearchCore.prototype.setSecurityTags = function(tags) {
if (Object.prototype.toString.call(tags) === '[object Array]') {
var strTags = [];
for (var i = 0; i < tags.length; ++i) {
if (Object.prototype.toString.call(tags[i]) === '[object Array]') {
var oredTags = [];
for (var j = 0; j < tags[i].length; ++j) {
oredTags.push(tags[i][j]);
}
strTags.push('(' + oredTags.join(',') + ')');
} else {
strTags.push(tags[i]);
}
}
tags = strTags.join(',');
}
this.securityTags = tags;
};
/**
* Set the extra user token header
* @param {string} userToken The token identifying a uniq user (used to apply rate limits)
*/
AlgoliaSearchCore.prototype.setUserToken = function(userToken) {
this.userToken = userToken;
};
/**
* Clear all queries in client's cache
* @return undefined
*/
AlgoliaSearchCore.prototype.clearCache = function() {
this.cache = {};
};
/**
* Set the number of milliseconds a request can take before automatically being terminated.
* @deprecated
* @param {Number} milliseconds
*/
AlgoliaSearchCore.prototype.setRequestTimeout = function(milliseconds) {
if (milliseconds) {
this._timeouts.connect = this._timeouts.read = this._timeouts.write = milliseconds;
}
};
/**
* Set the three different (connect, read, write) timeouts to be used when requesting
* @param {Object} timeouts
*/
AlgoliaSearchCore.prototype.setTimeouts = function(timeouts) {
this._timeouts = timeouts;
};
/**
* Get the three different (connect, read, write) timeouts to be used when requesting
* @param {Object} timeouts
*/
AlgoliaSearchCore.prototype.getTimeouts = function() {
return this._timeouts;
};
AlgoliaSearchCore.prototype._getAppIdData = function() {
var data = store.get(this.applicationID);
if (data !== null) this._cacheAppIdData(data);
return data;
};
AlgoliaSearchCore.prototype._setAppIdData = function(data) {
data.lastChange = (new Date()).getTime();
this._cacheAppIdData(data);
return store.set(this.applicationID, data);
};
AlgoliaSearchCore.prototype._checkAppIdData = function() {
var data = this._getAppIdData();
var now = (new Date()).getTime();
if (data === null || now - data.lastChange > RESET_APP_DATA_TIMER) {
return this._resetInitialAppIdData(data);
}
return data;
};
AlgoliaSearchCore.prototype._resetInitialAppIdData = function(data) {
var newData = data || {};
newData.hostIndexes = {read: 0, write: 0};
newData.timeoutMultiplier = 1;
newData.shuffleResult = newData.shuffleResult || shuffle([1, 2, 3]);
return this._setAppIdData(newData);
};
AlgoliaSearchCore.prototype._cacheAppIdData = function(data) {
this._hostIndexes = data.hostIndexes;
this._timeoutMultiplier = data.timeoutMultiplier;
this._shuffleResult = data.shuffleResult;
};
AlgoliaSearchCore.prototype._partialAppIdDataUpdate = function(newData) {
var foreach = require('foreach');
var currentData = this._getAppIdData();
foreach(newData, function(value, key) {
currentData[key] = value;
});
return this._setAppIdData(currentData);
};
AlgoliaSearchCore.prototype._getHostByType = function(hostType) {
return this.hosts[hostType][this._getHostIndexByType(hostType)];
};
AlgoliaSearchCore.prototype._getTimeoutMultiplier = function() {
return this._timeoutMultiplier;
};
AlgoliaSearchCore.prototype._getHostIndexByType = function(hostType) {
return this._hostIndexes[hostType];
};
AlgoliaSearchCore.prototype._setHostIndexByType = function(hostIndex, hostType) {
var clone = require('./clone');
var newHostIndexes = clone(this._hostIndexes);
newHostIndexes[hostType] = hostIndex;
this._partialAppIdDataUpdate({hostIndexes: newHostIndexes});
return hostIndex;
};
AlgoliaSearchCore.prototype._incrementHostIndex = function(hostType) {
return this._setHostIndexByType(
(this._getHostIndexByType(hostType) + 1) % this.hosts[hostType].length, hostType
);
};
AlgoliaSearchCore.prototype._incrementTimeoutMultipler = function() {
var timeoutMultiplier = Math.max(this._timeoutMultiplier + 1, 4);
return this._partialAppIdDataUpdate({timeoutMultiplier: timeoutMultiplier});
};
AlgoliaSearchCore.prototype._getTimeoutsForRequest = function(hostType) {
return {
connect: this._timeouts.connect * this._timeoutMultiplier,
complete: this._timeouts[hostType] * this._timeoutMultiplier
};
};
function prepareHost(protocol) {
return function prepare(host) {
return protocol + '//' + host.toLowerCase();
};
}
// Prototype.js < 1.7, a widely used library, defines a weird
// Array.prototype.toJSON function that will fail to stringify our content
// appropriately
// refs:
// - https://groups.google.com/forum/#!topic/prototype-core/E-SAVvV_V9Q
// - https://github.com/sstephenson/prototype/commit/038a2985a70593c1a86c230fadbdfe2e4898a48c
// - http://stackoverflow.com/a/3148441/147079
function safeJSONStringify(obj) {
/* eslint no-extend-native:0 */
if (Array.prototype.toJSON === undefined) {
return JSON.stringify(obj);
}
var toJSON = Array.prototype.toJSON;
delete Array.prototype.toJSON;
var out = JSON.stringify(obj);
Array.prototype.toJSON = toJSON;
return out;
}
function shuffle(array) {
var currentIndex = array.length;
var temporaryValue;
var randomIndex;
// While there remain elements to shuffle...
while (currentIndex !== 0) {
// Pick a remaining element...
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex -= 1;
// And swap it with the current element.
temporaryValue = array[currentIndex];
array[currentIndex] = array[randomIndex];
array[randomIndex] = temporaryValue;
}
return array;
}
function removeCredentials(headers) {
var newHeaders = {};
for (var headerName in headers) {
if (Object.prototype.hasOwnProperty.call(headers, headerName)) {
var value;
if (headerName === 'x-algolia-api-key' || headerName === 'x-algolia-application-id') {
value = '**hidden for security purposes**';
} else {
value = headers[headerName];
}
newHeaders[headerName] = value;
}
}
return newHeaders;
}

1334
themes/keepit/node_modules/algoliasearch/src/Index.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,39 @@
'use strict';
// This is the object returned by the `index.browseAll()` method
module.exports = IndexBrowser;
var inherits = require('inherits');
var EventEmitter = require('events').EventEmitter;
function IndexBrowser() {
}
inherits(IndexBrowser, EventEmitter);
IndexBrowser.prototype.stop = function() {
this._stopped = true;
this._clean();
};
IndexBrowser.prototype._end = function() {
this.emit('end');
this._clean();
};
IndexBrowser.prototype._error = function(err) {
this.emit('error', err);
this._clean();
};
IndexBrowser.prototype._result = function(content) {
this.emit('result', content);
};
IndexBrowser.prototype._clean = function() {
this.removeAllListeners('stop');
this.removeAllListeners('end');
this.removeAllListeners('error');
this.removeAllListeners('result');
};

View File

@@ -0,0 +1,389 @@
var buildSearchMethod = require('./buildSearchMethod.js');
var deprecate = require('./deprecate.js');
var deprecatedMessage = require('./deprecatedMessage.js');
module.exports = IndexCore;
/*
* Index class constructor.
* You should not use this method directly but use initIndex() function
*/
function IndexCore(algoliasearch, indexName) {
this.indexName = indexName;
this.as = algoliasearch;
this.typeAheadArgs = null;
this.typeAheadValueOption = null;
// make sure every index instance has it's own cache
this.cache = {};
}
/*
* Clear all queries in cache
*/
IndexCore.prototype.clearCache = function() {
this.cache = {};
};
/*
* Search inside the index using XMLHttpRequest request (Using a POST query to
* minimize number of OPTIONS queries: Cross-Origin Resource Sharing).
*
* @param {string} [query] the full text query
* @param {object} [args] (optional) if set, contains an object with query parameters:
* - page: (integer) Pagination parameter used to select the page to retrieve.
* Page is zero-based and defaults to 0. Thus,
* to retrieve the 10th page you need to set page=9
* - hitsPerPage: (integer) Pagination parameter used to select the number of hits per page. Defaults to 20.
* - attributesToRetrieve: a string that contains the list of object attributes
* you want to retrieve (let you minimize the answer size).
* Attributes are separated with a comma (for example "name,address").
* You can also use an array (for example ["name","address"]).
* By default, all attributes are retrieved. You can also use '*' to retrieve all
* values when an attributesToRetrieve setting is specified for your index.
* - attributesToHighlight: a string that contains the list of attributes you
* want to highlight according to the query.
* Attributes are separated by a comma. You can also use an array (for example ["name","address"]).
* If an attribute has no match for the query, the raw value is returned.
* By default all indexed text attributes are highlighted.
* You can use `*` if you want to highlight all textual attributes.
* Numerical attributes are not highlighted.
* A matchLevel is returned for each highlighted attribute and can contain:
* - full: if all the query terms were found in the attribute,
* - partial: if only some of the query terms were found,
* - none: if none of the query terms were found.
* - attributesToSnippet: a string that contains the list of attributes to snippet alongside
* the number of words to return (syntax is `attributeName:nbWords`).
* Attributes are separated by a comma (Example: attributesToSnippet=name:10,content:10).
* You can also use an array (Example: attributesToSnippet: ['name:10','content:10']).
* By default no snippet is computed.
* - minWordSizefor1Typo: the minimum number of characters in a query word to accept one typo in this word.
* Defaults to 3.
* - minWordSizefor2Typos: the minimum number of characters in a query word
* to accept two typos in this word. Defaults to 7.
* - getRankingInfo: if set to 1, the result hits will contain ranking
* information in _rankingInfo attribute.
* - aroundLatLng: search for entries around a given
* latitude/longitude (specified as two floats separated by a comma).
* For example aroundLatLng=47.316669,5.016670).
* You can specify the maximum distance in meters with the aroundRadius parameter (in meters)
* and the precision for ranking with aroundPrecision
* (for example if you set aroundPrecision=100, two objects that are distant of
* less than 100m will be considered as identical for "geo" ranking parameter).
* At indexing, you should specify geoloc of an object with the _geoloc attribute
* (in the form {"_geoloc":{"lat":48.853409, "lng":2.348800}})
* - insideBoundingBox: search entries inside a given area defined by the two extreme points
* of a rectangle (defined by 4 floats: p1Lat,p1Lng,p2Lat,p2Lng).
* For example insideBoundingBox=47.3165,4.9665,47.3424,5.0201).
* At indexing, you should specify geoloc of an object with the _geoloc attribute
* (in the form {"_geoloc":{"lat":48.853409, "lng":2.348800}})
* - numericFilters: a string that contains the list of numeric filters you want to
* apply separated by a comma.
* The syntax of one filter is `attributeName` followed by `operand` followed by `value`.
* Supported operands are `<`, `<=`, `=`, `>` and `>=`.
* You can have multiple conditions on one attribute like for example numericFilters=price>100,price<1000.
* You can also use an array (for example numericFilters: ["price>100","price<1000"]).
* - tagFilters: filter the query by a set of tags. You can AND tags by separating them by commas.
* To OR tags, you must add parentheses. For example, tags=tag1,(tag2,tag3) means tag1 AND (tag2 OR tag3).
* You can also use an array, for example tagFilters: ["tag1",["tag2","tag3"]]
* means tag1 AND (tag2 OR tag3).
* At indexing, tags should be added in the _tags** attribute
* of objects (for example {"_tags":["tag1","tag2"]}).
* - facetFilters: filter the query by a list of facets.
* Facets are separated by commas and each facet is encoded as `attributeName:value`.
* For example: `facetFilters=category:Book,author:John%20Doe`.
* You can also use an array (for example `["category:Book","author:John%20Doe"]`).
* - facets: List of object attributes that you want to use for faceting.
* Comma separated list: `"category,author"` or array `['category','author']`
* Only attributes that have been added in **attributesForFaceting** index setting
* can be used in this parameter.
* You can also use `*` to perform faceting on all attributes specified in **attributesForFaceting**.
* - queryType: select how the query words are interpreted, it can be one of the following value:
* - prefixAll: all query words are interpreted as prefixes,
* - prefixLast: only the last word is interpreted as a prefix (default behavior),
* - prefixNone: no query word is interpreted as a prefix. This option is not recommended.
* - optionalWords: a string that contains the list of words that should
* be considered as optional when found in the query.
* Comma separated and array are accepted.
* - distinct: If set to 1, enable the distinct feature (disabled by default)
* if the attributeForDistinct index setting is set.
* This feature is similar to the SQL "distinct" keyword: when enabled
* in a query with the distinct=1 parameter,
* all hits containing a duplicate value for the attributeForDistinct attribute are removed from results.
* For example, if the chosen attribute is show_name and several hits have
* the same value for show_name, then only the best
* one is kept and others are removed.
* - restrictSearchableAttributes: List of attributes you want to use for
* textual search (must be a subset of the attributesToIndex index setting)
* either comma separated or as an array
* @param {function} [callback] the result callback called with two arguments:
* error: null or Error('message'). If false, the content contains the error.
* content: the server answer that contains the list of results.
*/
IndexCore.prototype.search = buildSearchMethod('query');
/*
* -- BETA --
* Search a record similar to the query inside the index using XMLHttpRequest request (Using a POST query to
* minimize number of OPTIONS queries: Cross-Origin Resource Sharing).
*
* @param {string} [query] the similar query
* @param {object} [args] (optional) if set, contains an object with query parameters.
* All search parameters are supported (see search function), restrictSearchableAttributes and facetFilters
* are the two most useful to restrict the similar results and get more relevant content
*/
IndexCore.prototype.similarSearch = deprecate(
buildSearchMethod('similarQuery'),
deprecatedMessage(
'index.similarSearch(query[, callback])',
'index.search({ similarQuery: query }[, callback])'
)
);
/*
* Browse index content. The response content will have a `cursor` property that you can use
* to browse subsequent pages for this query. Use `index.browseFrom(cursor)` when you want.
*
* @param {string} query - The full text query
* @param {Object} [queryParameters] - Any search query parameter
* @param {Function} [callback] - The result callback called with two arguments
* error: null or Error('message')
* content: the server answer with the browse result
* @return {Promise|undefined} Returns a promise if no callback given
* @example
* index.browse('cool songs', {
* tagFilters: 'public,comments',
* hitsPerPage: 500
* }, callback);
* @see {@link https://www.algolia.com/doc/rest_api#Browse|Algolia REST API Documentation}
*/
IndexCore.prototype.browse = function(query, queryParameters, callback) {
var merge = require('./merge.js');
var indexObj = this;
var page;
var hitsPerPage;
// we check variadic calls that are not the one defined
// .browse()/.browse(fn)
// => page = 0
if (arguments.length === 0 || arguments.length === 1 && typeof arguments[0] === 'function') {
page = 0;
callback = arguments[0];
query = undefined;
} else if (typeof arguments[0] === 'number') {
// .browse(2)/.browse(2, 10)/.browse(2, fn)/.browse(2, 10, fn)
page = arguments[0];
if (typeof arguments[1] === 'number') {
hitsPerPage = arguments[1];
} else if (typeof arguments[1] === 'function') {
callback = arguments[1];
hitsPerPage = undefined;
}
query = undefined;
queryParameters = undefined;
} else if (typeof arguments[0] === 'object') {
// .browse(queryParameters)/.browse(queryParameters, cb)
if (typeof arguments[1] === 'function') {
callback = arguments[1];
}
queryParameters = arguments[0];
query = undefined;
} else if (typeof arguments[0] === 'string' && typeof arguments[1] === 'function') {
// .browse(query, cb)
callback = arguments[1];
queryParameters = undefined;
}
// otherwise it's a .browse(query)/.browse(query, queryParameters)/.browse(query, queryParameters, cb)
// get search query parameters combining various possible calls
// to .browse();
queryParameters = merge({}, queryParameters || {}, {
page: page,
hitsPerPage: hitsPerPage,
query: query
});
var params = this.as._getSearchParams(queryParameters, '');
return this.as._jsonRequest({
method: 'POST',
url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/browse',
body: {params: params},
hostType: 'read',
callback: callback
});
};
/*
* Continue browsing from a previous position (cursor), obtained via a call to `.browse()`.
*
* @param {string} query - The full text query
* @param {Object} [queryParameters] - Any search query parameter
* @param {Function} [callback] - The result callback called with two arguments
* error: null or Error('message')
* content: the server answer with the browse result
* @return {Promise|undefined} Returns a promise if no callback given
* @example
* index.browseFrom('14lkfsakl32', callback);
* @see {@link https://www.algolia.com/doc/rest_api#Browse|Algolia REST API Documentation}
*/
IndexCore.prototype.browseFrom = function(cursor, callback) {
return this.as._jsonRequest({
method: 'POST',
url: '/1/indexes/' + encodeURIComponent(this.indexName) + '/browse',
body: {cursor: cursor},
hostType: 'read',
callback: callback
});
};
/*
* Search for facet values
* https://www.algolia.com/doc/rest-api/search#search-for-facet-values
*
* @param {string} params.facetName Facet name, name of the attribute to search for values in.
* Must be declared as a facet
* @param {string} params.facetQuery Query for the facet search
* @param {string} [params.*] Any search parameter of Algolia,
* see https://www.algolia.com/doc/api-client/javascript/search#search-parameters
* Pagination is not supported. The page and hitsPerPage parameters will be ignored.
* @param callback (optional)
*/
IndexCore.prototype.searchForFacetValues = function(params, callback) {
var clone = require('./clone.js');
var omit = require('./omit.js');
var usage = 'Usage: index.searchForFacetValues({facetName, facetQuery, ...params}[, callback])';
if (params.facetName === undefined || params.facetQuery === undefined) {
throw new Error(usage);
}
var facetName = params.facetName;
var filteredParams = omit(clone(params), function(keyName) {
return keyName === 'facetName';
});
var searchParameters = this.as._getSearchParams(filteredParams, '');
return this.as._jsonRequest({
method: 'POST',
url: '/1/indexes/' +
encodeURIComponent(this.indexName) + '/facets/' + encodeURIComponent(facetName) + '/query',
hostType: 'read',
body: {params: searchParameters},
callback: callback
});
};
IndexCore.prototype.searchFacet = deprecate(function(params, callback) {
return this.searchForFacetValues(params, callback);
}, deprecatedMessage(
'index.searchFacet(params[, callback])',
'index.searchForFacetValues(params[, callback])'
));
IndexCore.prototype._search = function(params, url, callback, additionalUA) {
return this.as._jsonRequest({
cache: this.cache,
method: 'POST',
url: url || '/1/indexes/' + encodeURIComponent(this.indexName) + '/query',
body: {params: params},
hostType: 'read',
fallback: {
method: 'GET',
url: '/1/indexes/' + encodeURIComponent(this.indexName),
body: {params: params}
},
callback: callback,
additionalUA: additionalUA
});
};
/*
* Get an object from this index
*
* @param objectID the unique identifier of the object to retrieve
* @param attrs (optional) if set, contains the array of attribute names to retrieve
* @param callback (optional) the result callback called with two arguments
* error: null or Error('message')
* content: the object to retrieve or the error message if a failure occurred
*/
IndexCore.prototype.getObject = function(objectID, attrs, callback) {
var indexObj = this;
if (arguments.length === 1 || typeof attrs === 'function') {
callback = attrs;
attrs = undefined;
}
var params = '';
if (attrs !== undefined) {
params = '?attributes=';
for (var i = 0; i < attrs.length; ++i) {
if (i !== 0) {
params += ',';
}
params += attrs[i];
}
}
return this.as._jsonRequest({
method: 'GET',
url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/' + encodeURIComponent(objectID) + params,
hostType: 'read',
callback: callback
});
};
/*
* Get several objects from this index
*
* @param objectIDs the array of unique identifier of objects to retrieve
*/
IndexCore.prototype.getObjects = function(objectIDs, attributesToRetrieve, callback) {
var isArray = require('isarray');
var map = require('./map.js');
var usage = 'Usage: index.getObjects(arrayOfObjectIDs[, callback])';
if (!isArray(objectIDs)) {
throw new Error(usage);
}
var indexObj = this;
if (arguments.length === 1 || typeof attributesToRetrieve === 'function') {
callback = attributesToRetrieve;
attributesToRetrieve = undefined;
}
var body = {
requests: map(objectIDs, function prepareRequest(objectID) {
var request = {
indexName: indexObj.indexName,
objectID: objectID
};
if (attributesToRetrieve) {
request.attributesToRetrieve = attributesToRetrieve.join(',');
}
return request;
})
};
return this.as._jsonRequest({
method: 'POST',
url: '/1/indexes/*/objects',
hostType: 'read',
body: body,
callback: callback
});
};
IndexCore.prototype.as = null;
IndexCore.prototype.indexName = null;
IndexCore.prototype.typeAheadArgs = null;
IndexCore.prototype.typeAheadValueOption = null;

View File

@@ -0,0 +1,205 @@
'use strict';
// This is the AngularJS Algolia Search module
// It's using $http to do requests with a JSONP fallback
// $q promises are returned
var inherits = require('inherits');
var forEach = require('foreach');
var AlgoliaSearch = require('../../AlgoliaSearch');
var errors = require('../../errors');
var inlineHeaders = require('../inline-headers');
var jsonpRequest = require('../jsonp-request');
var places = require('../../places.js');
// expose original algoliasearch fn in window
window.algoliasearch = require('./algoliasearch');
if (process.env.NODE_ENV === 'debug') {
require('debug').enable('algoliasearch*');
}
window.angular.module('algoliasearch', [])
.service('algolia', ['$http', '$q', '$timeout', function algoliaSearchService($http, $q, $timeout) {
function algoliasearch(applicationID, apiKey, opts) {
var cloneDeep = require('../../clone.js');
opts = cloneDeep(opts || {});
opts._ua = opts._ua || algoliasearch.ua;
return new AlgoliaSearchAngular(applicationID, apiKey, opts);
}
algoliasearch.version = require('../../version.js');
algoliasearch.ua =
'Algolia for JavaScript (' + algoliasearch.version + '); ' +
'AngularJS (' + window.angular.version.full + ')';
algoliasearch.initPlaces = places(algoliasearch);
// we expose into window no matter how we are used, this will allow
// us to easily debug any website running algolia
window.__algolia = {
debug: require('debug'),
algoliasearch: algoliasearch
};
function AlgoliaSearchAngular() {
// call AlgoliaSearch constructor
AlgoliaSearch.apply(this, arguments);
}
inherits(AlgoliaSearchAngular, AlgoliaSearch);
AlgoliaSearchAngular.prototype._request = function request(url, opts) {
// Support most Angular.js versions by using $q.defer() instead
// of the new $q() constructor everywhere we need a promise
var deferred = $q.defer();
var resolve = deferred.resolve;
var reject = deferred.reject;
var timedOut;
var body = opts.body;
url = inlineHeaders(url, opts.headers);
var timeoutDeferred = $q.defer();
var timeoutPromise = timeoutDeferred.promise;
$timeout(function timedout() {
timedOut = true;
// will cancel the xhr
timeoutDeferred.resolve('test');
reject(new errors.RequestTimeout());
}, opts.timeouts.complete);
var requestHeaders = {};
// "remove" (set to undefined) possible globally set headers
// in $httpProvider.defaults.headers.common
// otherwise we might fail sometimes
// ref: https://github.com/algolia/algoliasearch-client-js/issues/135
forEach(
$http.defaults.headers.common,
function removeIt(headerValue, headerName) {
requestHeaders[headerName] = undefined;
}
);
requestHeaders.accept = 'application/json';
if (body) {
if (opts.method === 'POST') {
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Simple_requests
requestHeaders['content-type'] = 'application/x-www-form-urlencoded';
} else {
requestHeaders['content-type'] = 'application/json';
}
}
$http({
url: url,
method: opts.method,
data: body,
cache: false,
timeout: timeoutPromise,
headers: requestHeaders,
transformResponse: transformResponse,
// if client uses $httpProvider.defaults.withCredentials = true,
// we revert it to false to avoid CORS failure
withCredentials: false
}).then(success, error);
function success(response) {
resolve({
statusCode: response.status,
headers: response.headers,
body: JSON.parse(response.data),
responseText: response.data
});
}
// we force getting the raw data because we need it so
// for cache keys
function transformResponse(data) {
return data;
}
function error(response) {
if (timedOut) {
return;
}
// network error
if (response.status === 0) {
reject(
new errors.Network({
more: response
})
);
return;
}
resolve({
body: JSON.parse(response.data),
statusCode: response.status
});
}
return deferred.promise;
};
// using IE8 or IE9 we will always end up here
// AngularJS does not fallback to XDomainRequest
AlgoliaSearchAngular.prototype._request.fallback = function requestFallback(url, opts) {
url = inlineHeaders(url, opts.headers);
var deferred = $q.defer();
var resolve = deferred.resolve;
var reject = deferred.reject;
jsonpRequest(url, opts, function jsonpRequestDone(err, content) {
if (err) {
reject(err);
return;
}
resolve(content);
});
return deferred.promise;
};
AlgoliaSearchAngular.prototype._promise = {
reject: function(val) {
return $q.reject(val);
},
resolve: function(val) {
// http://www.bennadel.com/blog/2735-q-when-is-the-missing-q-resolve-method-in-angularjs.htm
return $q.when(val);
},
delay: function(ms) {
var deferred = $q.defer();
var resolve = deferred.resolve;
$timeout(resolve, ms);
return deferred.promise;
},
all: function(promises) {
return $q.all(promises);
}
};
return {
Client: function(applicationID, apiKey, options) {
return algoliasearch(applicationID, apiKey, options);
},
ua: algoliasearch.ua,
version: algoliasearch.version
};
}]);

View File

@@ -0,0 +1,151 @@
'use strict';
// This is the jQuery Algolia Search module
// It's using $.ajax to do requests with a JSONP fallback
// jQuery promises are returned
var inherits = require('inherits');
var AlgoliaSearch = require('../../AlgoliaSearch');
var errors = require('../../errors');
var inlineHeaders = require('../inline-headers');
var jsonpRequest = require('../jsonp-request');
var places = require('../../places.js');
// expose original algoliasearch fn in window
window.algoliasearch = require('./algoliasearch');
if (process.env.NODE_ENV === 'debug') {
require('debug').enable('algoliasearch*');
}
function algoliasearch(applicationID, apiKey, opts) {
var cloneDeep = require('../../clone.js');
opts = cloneDeep(opts || {});
opts._ua = opts._ua || algoliasearch.ua;
return new AlgoliaSearchJQuery(applicationID, apiKey, opts);
}
algoliasearch.version = require('../../version.js');
algoliasearch.ua =
'Algolia for JavaScript (' + algoliasearch.version + '); ' +
'jQuery (' + window.jQuery().jquery + ')';
algoliasearch.initPlaces = places(algoliasearch);
// we expose into window no matter how we are used, this will allow
// us to easily debug any website running algolia
window.__algolia = {
debug: require('debug'),
algoliasearch: algoliasearch
};
var $ = window.jQuery;
$.algolia = {
Client: algoliasearch,
ua: algoliasearch.ua,
version: algoliasearch.version
};
function AlgoliaSearchJQuery() {
// call AlgoliaSearch constructor
AlgoliaSearch.apply(this, arguments);
}
inherits(AlgoliaSearchJQuery, AlgoliaSearch);
AlgoliaSearchJQuery.prototype._request = function request(url, opts) {
return new $.Deferred(function(deferred) {
var body = opts.body;
url = inlineHeaders(url, opts.headers);
var requestHeaders = {
accept: 'application/json'
};
if (body) {
if (opts.method === 'POST') {
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Simple_requests
requestHeaders['content-type'] = 'application/x-www-form-urlencoded';
} else {
requestHeaders['content-type'] = 'application/json';
}
}
$.ajax(url, {
type: opts.method,
timeout: opts.timeouts.complete,
dataType: 'json',
data: body,
headers: requestHeaders,
complete: function onComplete(jqXHR, textStatus/* , error*/) {
if (textStatus === 'timeout') {
deferred.reject(new errors.RequestTimeout());
return;
}
if (jqXHR.status === 0) {
deferred.reject(
new errors.Network({
more: jqXHR
})
);
return;
}
deferred.resolve({
statusCode: jqXHR.status,
body: jqXHR.responseJSON,
responseText: jqXHR.responseText,
headers: jqXHR.getAllResponseHeaders()
});
}
});
}).promise();
};
// using IE8 or IE9 we will always end up here
// jQuery does not not fallback to XDomainRequest
AlgoliaSearchJQuery.prototype._request.fallback = function requestFallback(url, opts) {
url = inlineHeaders(url, opts.headers);
return new $.Deferred(function wrapJsonpRequest(deferred) {
jsonpRequest(url, opts, function jsonpRequestDone(err, content) {
if (err) {
deferred.reject(err);
return;
}
deferred.resolve(content);
});
}).promise();
};
AlgoliaSearchJQuery.prototype._promise = {
reject: function reject(val) {
return new $.Deferred(function rejectDeferred(deferred) {
deferred.reject(val);
}).promise();
},
resolve: function resolve(val) {
return new $.Deferred(function resolveDeferred(deferred) {
deferred.resolve(val);
}).promise();
},
delay: function delay(ms) {
return new $.Deferred(function delayResolve(deferred) {
setTimeout(function resolveDeferred() {
deferred.resolve();
}, ms);
}).promise();
},
all: function all(promises) {
return $.when.apply(null, promises);
}
};

View File

@@ -0,0 +1,6 @@
'use strict';
var AlgoliaSearch = require('../../AlgoliaSearch.js');
var createAlgoliasearch = require('../createAlgoliasearch.js');
module.exports = createAlgoliasearch(AlgoliaSearch, 'Browser');

View File

@@ -0,0 +1,6 @@
'use strict';
var AlgoliaSearchCore = require('../../AlgoliaSearchCore.js');
var createAlgoliasearch = require('../createAlgoliasearch.js');
module.exports = createAlgoliasearch(AlgoliaSearchCore, 'Browser (lite)');

View File

@@ -0,0 +1,236 @@
'use strict';
var global = require('global');
var Promise = global.Promise || require('es6-promise').Promise;
// This is the standalone browser build entry point
// Browser implementation of the Algolia Search JavaScript client,
// using XMLHttpRequest, XDomainRequest and JSONP as fallback
module.exports = function createAlgoliasearch(AlgoliaSearch, uaSuffix) {
var inherits = require('inherits');
var errors = require('../errors');
var inlineHeaders = require('./inline-headers');
var jsonpRequest = require('./jsonp-request');
var places = require('../places.js');
uaSuffix = uaSuffix || '';
if (process.env.NODE_ENV === 'debug') {
require('debug').enable('algoliasearch*');
}
function algoliasearch(applicationID, apiKey, opts) {
var cloneDeep = require('../clone.js');
opts = cloneDeep(opts || {});
opts._ua = opts._ua || algoliasearch.ua;
return new AlgoliaSearchBrowser(applicationID, apiKey, opts);
}
algoliasearch.version = require('../version.js');
algoliasearch.ua =
'Algolia for JavaScript (' + algoliasearch.version + '); ' + uaSuffix;
algoliasearch.initPlaces = places(algoliasearch);
// we expose into window no matter how we are used, this will allow
// us to easily debug any website running algolia
global.__algolia = {
debug: require('debug'),
algoliasearch: algoliasearch
};
var support = {
hasXMLHttpRequest: 'XMLHttpRequest' in global,
hasXDomainRequest: 'XDomainRequest' in global
};
if (support.hasXMLHttpRequest) {
support.cors = 'withCredentials' in new XMLHttpRequest();
}
function AlgoliaSearchBrowser() {
// call AlgoliaSearch constructor
AlgoliaSearch.apply(this, arguments);
}
inherits(AlgoliaSearchBrowser, AlgoliaSearch);
AlgoliaSearchBrowser.prototype._request = function request(url, opts) {
return new Promise(function wrapRequest(resolve, reject) {
// no cors or XDomainRequest, no request
if (!support.cors && !support.hasXDomainRequest) {
// very old browser, not supported
reject(new errors.Network('CORS not supported'));
return;
}
url = inlineHeaders(url, opts.headers);
var body = opts.body;
var req = support.cors ? new XMLHttpRequest() : new XDomainRequest();
var reqTimeout;
var timedOut;
var connected = false;
reqTimeout = setTimeout(onTimeout, opts.timeouts.connect);
// we set an empty onprogress listener
// so that XDomainRequest on IE9 is not aborted
// refs:
// - https://github.com/algolia/algoliasearch-client-js/issues/76
// - https://social.msdn.microsoft.com/Forums/ie/en-US/30ef3add-767c-4436-b8a9-f1ca19b4812e/ie9-rtm-xdomainrequest-issued-requests-may-abort-if-all-event-handlers-not-specified?forum=iewebdevelopment
req.onprogress = onProgress;
if ('onreadystatechange' in req) req.onreadystatechange = onReadyStateChange;
req.onload = onLoad;
req.onerror = onError;
// do not rely on default XHR async flag, as some analytics code like hotjar
// breaks it and set it to false by default
if (req instanceof XMLHttpRequest) {
req.open(opts.method, url, true);
// The Analytics API never accepts Auth headers as query string
// this option exists specifically for them.
if (opts.forceAuthHeaders) {
req.setRequestHeader(
'x-algolia-application-id',
opts.headers['x-algolia-application-id']
);
req.setRequestHeader(
'x-algolia-api-key',
opts.headers['x-algolia-api-key']
);
}
} else {
req.open(opts.method, url);
}
// headers are meant to be sent after open
if (support.cors) {
if (body) {
if (opts.method === 'POST') {
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Simple_requests
req.setRequestHeader('content-type', 'application/x-www-form-urlencoded');
} else {
req.setRequestHeader('content-type', 'application/json');
}
}
req.setRequestHeader('accept', 'application/json');
}
if (body) {
req.send(body);
} else {
req.send();
}
// event object not received in IE8, at least
// but we do not use it, still important to note
function onLoad(/* event */) {
// When browser does not supports req.timeout, we can
// have both a load and timeout event, since handled by a dumb setTimeout
if (timedOut) {
return;
}
clearTimeout(reqTimeout);
var out;
try {
out = {
body: JSON.parse(req.responseText),
responseText: req.responseText,
statusCode: req.status,
// XDomainRequest does not have any response headers
headers: req.getAllResponseHeaders && req.getAllResponseHeaders() || {}
};
} catch (e) {
out = new errors.UnparsableJSON({
more: req.responseText
});
}
if (out instanceof errors.UnparsableJSON) {
reject(out);
} else {
resolve(out);
}
}
function onError(event) {
if (timedOut) {
return;
}
clearTimeout(reqTimeout);
// error event is trigerred both with XDR/XHR on:
// - DNS error
// - unallowed cross domain request
reject(
new errors.Network({
more: event
})
);
}
function onTimeout() {
timedOut = true;
req.abort();
reject(new errors.RequestTimeout());
}
function onConnect() {
connected = true;
clearTimeout(reqTimeout);
reqTimeout = setTimeout(onTimeout, opts.timeouts.complete);
}
function onProgress() {
if (!connected) onConnect();
}
function onReadyStateChange() {
if (!connected && req.readyState > 1) onConnect();
}
});
};
AlgoliaSearchBrowser.prototype._request.fallback = function requestFallback(url, opts) {
url = inlineHeaders(url, opts.headers);
return new Promise(function wrapJsonpRequest(resolve, reject) {
jsonpRequest(url, opts, function jsonpRequestDone(err, content) {
if (err) {
reject(err);
return;
}
resolve(content);
});
});
};
AlgoliaSearchBrowser.prototype._promise = {
reject: function rejectPromise(val) {
return Promise.reject(val);
},
resolve: function resolvePromise(val) {
return Promise.resolve(val);
},
delay: function delayPromise(ms) {
return new Promise(function resolveOnTimeout(resolve/* , reject*/) {
setTimeout(resolve, ms);
});
},
all: function all(promises) {
return Promise.all(promises);
}
};
return algoliasearch;
};

View File

@@ -0,0 +1,15 @@
'use strict';
module.exports = inlineHeaders;
var encode = require('querystring-es3/encode');
function inlineHeaders(url, headers) {
if (/\?/.test(url)) {
url += '&';
} else {
url += '?';
}
return url + encode(headers);
}

View File

@@ -0,0 +1,126 @@
'use strict';
module.exports = jsonpRequest;
var errors = require('../errors');
var JSONPCounter = 0;
function jsonpRequest(url, opts, cb) {
if (opts.method !== 'GET') {
cb(new Error('Method ' + opts.method + ' ' + url + ' is not supported by JSONP.'));
return;
}
opts.debug('JSONP: start');
var cbCalled = false;
var timedOut = false;
JSONPCounter += 1;
var head = document.getElementsByTagName('head')[0];
var script = document.createElement('script');
var cbName = 'algoliaJSONP_' + JSONPCounter;
var done = false;
window[cbName] = function(data) {
removeGlobals();
if (timedOut) {
opts.debug('JSONP: Late answer, ignoring');
return;
}
cbCalled = true;
clean();
cb(null, {
body: data,
responseText: JSON.stringify(data)/* ,
// We do not send the statusCode, there's no statusCode in JSONP, it will be
// computed using data.status && data.message like with XDR
statusCode*/
});
};
// add callback by hand
url += '&callback=' + cbName;
// add body params manually
if (opts.jsonBody && opts.jsonBody.params) {
url += '&' + opts.jsonBody.params;
}
var ontimeout = setTimeout(timeout, opts.timeouts.complete);
// script onreadystatechange needed only for
// <= IE8
// https://github.com/angular/angular.js/issues/4523
script.onreadystatechange = readystatechange;
script.onload = success;
script.onerror = error;
script.async = true;
script.defer = true;
script.src = url;
head.appendChild(script);
function success() {
opts.debug('JSONP: success');
if (done || timedOut) {
return;
}
done = true;
// script loaded but did not call the fn => script loading error
if (!cbCalled) {
opts.debug('JSONP: Fail. Script loaded but did not call the callback');
clean();
cb(new errors.JSONPScriptFail());
}
}
function readystatechange() {
if (this.readyState === 'loaded' || this.readyState === 'complete') {
success();
}
}
function clean() {
clearTimeout(ontimeout);
script.onload = null;
script.onreadystatechange = null;
script.onerror = null;
head.removeChild(script);
}
function removeGlobals() {
try {
delete window[cbName];
delete window[cbName + '_loaded'];
} catch (e) {
window[cbName] = window[cbName + '_loaded'] = undefined;
}
}
function timeout() {
opts.debug('JSONP: Script timeout');
timedOut = true;
clean();
cb(new errors.RequestTimeout());
}
function error() {
opts.debug('JSONP: Script error');
if (done || timedOut) {
return;
}
clean();
cb(new errors.JSONPScriptError());
}
}

View File

@@ -0,0 +1,23 @@
'use strict';
// this module helps finding if the current page is using
// the cdn.jsdelivr.net/algoliasearch/latest/$BUILDNAME.min.js version
module.exports = isUsingLatest;
function isUsingLatest(buildName) {
var toFind = new RegExp('cdn\\.jsdelivr\\.net/algoliasearch/latest/' +
buildName.replace('.', '\\.') + // algoliasearch, algoliasearch.angular
'(?:\\.min)?\\.js$'); // [.min].js
var scripts = document.getElementsByTagName('script');
var found = false;
for (var currentScript = 0, nbScripts = scripts.length; currentScript < nbScripts; currentScript++) {
if (scripts[currentScript].src && toFind.test(scripts[currentScript].src)) {
found = true;
break;
}
}
return found;
}

View File

@@ -0,0 +1,50 @@
'use strict';
module.exports = loadV2;
function loadV2(buildName) {
var loadScript = require('load-script');
var v2ScriptUrl = '//cdn.jsdelivr.net/algoliasearch/2/' + buildName + '.min.js';
var message = '-- AlgoliaSearch `latest` warning --\n' +
'Warning, you are using the `latest` version string from jsDelivr to load the AlgoliaSearch library.\n' +
'Using `latest` is no more recommended, you should load //cdn.jsdelivr.net/algoliasearch/2/algoliasearch.min.js\n\n' +
'Also, we updated the AlgoliaSearch JavaScript client to V3. If you want to upgrade,\n' +
'please read our migration guide at https://github.com/algolia/algoliasearch-client-js/wiki/Migration-guide-from-2.x.x-to-3.x.x\n' +
'-- /AlgoliaSearch `latest` warning --';
if (window.console) {
if (window.console.warn) {
window.console.warn(message);
} else if (window.console.log) {
window.console.log(message);
}
}
// If current script loaded asynchronously,
// it will load the script with DOMElement
// otherwise, it will load the script with document.write
try {
// why \x3c? http://stackoverflow.com/a/236106/147079
document.write('\x3Cscript>window.ALGOLIA_SUPPORTS_DOCWRITE = true\x3C/script>');
if (window.ALGOLIA_SUPPORTS_DOCWRITE === true) {
document.write('\x3Cscript src="' + v2ScriptUrl + '">\x3C/script>');
scriptLoaded('document.write')();
} else {
loadScript(v2ScriptUrl, scriptLoaded('DOMElement'));
}
} catch (e) {
loadScript(v2ScriptUrl, scriptLoaded('DOMElement'));
}
}
function scriptLoaded(method) {
return function log() {
var message = 'AlgoliaSearch: loaded V2 script using ' + method;
if (window.console && window.console.log) {
window.console.log(message);
}
};
}

View File

@@ -0,0 +1,26 @@
'use strict';
/* eslint no-unused-vars: [2, {"vars": "local"}] */
module.exports = oldGlobals;
// put old window.AlgoliaSearch.. into window. again so that
// users upgrading to V3 without changing their code, will be warned
function oldGlobals() {
var message = '-- AlgoliaSearch V2 => V3 error --\n' +
'You are trying to use a new version of the AlgoliaSearch JavaScript client with an old notation.\n' +
'Please read our migration guide at https://github.com/algolia/algoliasearch-client-js/wiki/Migration-guide-from-2.x.x-to-3.x.x\n' +
'-- /AlgoliaSearch V2 => V3 error --';
window.AlgoliaSearch = function() {
throw new Error(message);
};
window.AlgoliaSearchHelper = function() {
throw new Error(message);
};
window.AlgoliaExplainResults = function() {
throw new Error(message);
};
}

View File

@@ -0,0 +1,25 @@
'use strict';
// This script will be browserified and prepended to the normal build
// directly in window, not wrapped in any module definition
// To avoid cases where we are loaded with /latest/ along with
migrationLayer(process.env.ALGOLIA_BUILDNAME);
// Now onto the V2 related code:
// If the client is using /latest/$BUILDNAME.min.js, load V2 of the library
//
// Otherwise, setup a migration layer that will throw on old constructors like
// new AlgoliaSearch().
// So that users upgrading from v2 to v3 will have a clear information
// message on what to do if they did not read the migration guide
function migrationLayer(buildName) {
var isUsingLatest = require('./is-using-latest');
var loadV2 = require('./load-v2');
var oldGlobals = require('./old-globals');
if (isUsingLatest(buildName)) {
loadV2(buildName);
} else {
oldGlobals();
}
}

View File

@@ -0,0 +1,67 @@
module.exports = buildSearchMethod;
var errors = require('./errors.js');
/**
* Creates a search method to be used in clients
* @param {string} queryParam the name of the attribute used for the query
* @param {string} url the url
* @return {function} the search method
*/
function buildSearchMethod(queryParam, url) {
/**
* The search method. Prepares the data and send the query to Algolia.
* @param {string} query the string used for query search
* @param {object} args additional parameters to send with the search
* @param {function} [callback] the callback to be called with the client gets the answer
* @return {undefined|Promise} If the callback is not provided then this methods returns a Promise
*/
return function search(query, args, callback) {
// warn V2 users on how to search
if (typeof query === 'function' && typeof args === 'object' ||
typeof callback === 'object') {
// .search(query, params, cb)
// .search(cb, params)
throw new errors.AlgoliaSearchError('index.search usage is index.search(query, params, cb)');
}
// Normalizing the function signature
if (arguments.length === 0 || typeof query === 'function') {
// Usage : .search(), .search(cb)
callback = query;
query = '';
} else if (arguments.length === 1 || typeof args === 'function') {
// Usage : .search(query/args), .search(query, cb)
callback = args;
args = undefined;
}
// At this point we have 3 arguments with values
// Usage : .search(args) // careful: typeof null === 'object'
if (typeof query === 'object' && query !== null) {
args = query;
query = undefined;
} else if (query === undefined || query === null) { // .search(undefined/null)
query = '';
}
var params = '';
if (query !== undefined) {
params += queryParam + '=' + encodeURIComponent(query);
}
var additionalUA;
if (args !== undefined) {
if (args.additionalUA) {
additionalUA = args.additionalUA;
delete args.additionalUA;
}
// `_getSearchParams` will augment params, do not be fooled by the = versus += from previous if
params = this.as._getSearchParams(args, params);
}
return this._search(params, url, callback, additionalUA);
};
}

View File

@@ -0,0 +1,3 @@
module.exports = function clone(obj) {
return JSON.parse(JSON.stringify(obj));
};

View File

@@ -0,0 +1,85 @@
module.exports = createAnalyticsClient;
var algoliasearch = require('../index.js');
function createAnalyticsClient(appId, apiKey, opts) {
var analytics = {};
opts = opts || {};
// there need to be 4 hosts, like on the client, since if requests fail,
// the counter goes up by 1, so we need to have the same amount of hosts
// 4 because: -dsn, -1, -2, -3
// This is done because the APPID used for search will be the same for the analytics client created,
// and since the state of available hosts is shared by APPID globally for the module, we had issues
// where the hostIndex would be 1 while the array was only one entry (you got an empty host)
opts.hosts = opts.hosts || [
'analytics.algolia.com',
'analytics.algolia.com',
'analytics.algolia.com',
'analytics.algolia.com'
];
opts.protocol = opts.protocol || 'https:';
analytics.as = algoliasearch(appId, apiKey, opts);
analytics.getABTests = function(_params, callback) {
var params = params || {};
var offset = params.offset || 0;
var limit = params.limit || 10;
return this.as._jsonRequest({
method: 'GET',
url: '/2/abtests?offset=' + encodeURIComponent(offset) + '&limit=' + encodeURIComponent(limit),
hostType: 'read',
forceAuthHeaders: true,
callback: callback
});
};
analytics.getABTest = function(abTestID, callback) {
return this.as._jsonRequest({
method: 'GET',
url: '/2/abtests/' + encodeURIComponent(abTestID),
hostType: 'read',
forceAuthHeaders: true,
callback: callback
});
};
analytics.addABTest = function(abTest, callback) {
return this.as._jsonRequest({
method: 'POST',
url: '/2/abtests',
body: abTest,
hostType: 'read',
forceAuthHeaders: true,
callback: callback
});
};
analytics.stopABTest = function(abTestID, callback) {
return this.as._jsonRequest({
method: 'POST',
url: '/2/abtests/' + encodeURIComponent(abTestID) + '/stop',
hostType: 'read',
forceAuthHeaders: true,
callback: callback
});
};
analytics.deleteABTest = function(abTestID, callback) {
return this.as._jsonRequest({
method: 'DELETE',
url: '/2/abtests/' + encodeURIComponent(abTestID),
hostType: 'write',
forceAuthHeaders: true,
callback: callback
});
};
analytics.waitTask = function(indexName, taskID, callback) {
return this.as.initIndex(indexName).waitTask(taskID, callback);
};
return analytics;
}

View File

@@ -0,0 +1,15 @@
module.exports = function deprecate(fn, message) {
var warned = false;
function deprecated() {
if (!warned) {
/* eslint no-console:0 */
console.warn(message);
warned = true;
}
return fn.apply(this, arguments);
}
return deprecated;
};

View File

@@ -0,0 +1,7 @@
module.exports = function deprecatedMessage(previousUsage, newUsage) {
var githubAnchorLink = previousUsage.toLowerCase()
.replace(/[\.\(\)]/g, '');
return 'algoliasearch: `' + previousUsage + '` was replaced by `' + newUsage +
'`. Please see https://github.com/algolia/algoliasearch-client-javascript/wiki/Deprecated#' + githubAnchorLink;
};

86
themes/keepit/node_modules/algoliasearch/src/errors.js generated vendored Normal file
View File

@@ -0,0 +1,86 @@
'use strict';
// This file hosts our error definitions
// We use custom error "types" so that we can act on them when we need it
// e.g.: if error instanceof errors.UnparsableJSON then..
var inherits = require('inherits');
function AlgoliaSearchError(message, extraProperties) {
var forEach = require('foreach');
var error = this;
// try to get a stacktrace
if (typeof Error.captureStackTrace === 'function') {
Error.captureStackTrace(this, this.constructor);
} else {
error.stack = (new Error()).stack || 'Cannot get a stacktrace, browser is too old';
}
this.name = 'AlgoliaSearchError';
this.message = message || 'Unknown error';
if (extraProperties) {
forEach(extraProperties, function addToErrorObject(value, key) {
error[key] = value;
});
}
}
inherits(AlgoliaSearchError, Error);
function createCustomError(name, message) {
function AlgoliaSearchCustomError() {
var args = Array.prototype.slice.call(arguments, 0);
// custom message not set, use default
if (typeof args[0] !== 'string') {
args.unshift(message);
}
AlgoliaSearchError.apply(this, args);
this.name = 'AlgoliaSearch' + name + 'Error';
}
inherits(AlgoliaSearchCustomError, AlgoliaSearchError);
return AlgoliaSearchCustomError;
}
// late exports to let various fn defs and inherits take place
module.exports = {
AlgoliaSearchError: AlgoliaSearchError,
UnparsableJSON: createCustomError(
'UnparsableJSON',
'Could not parse the incoming response as JSON, see err.more for details'
),
RequestTimeout: createCustomError(
'RequestTimeout',
'Request timed out before getting a response'
),
Network: createCustomError(
'Network',
'Network issue, see err.more for details'
),
JSONPScriptFail: createCustomError(
'JSONPScriptFail',
'<script> was loaded but did not call our provided callback'
),
ValidUntilNotFound: createCustomError(
'ValidUntilNotFound',
'The SecuredAPIKey does not have a validUntil parameter.'
),
JSONPScriptError: createCustomError(
'JSONPScriptError',
'<script> unable to load due to an `error` event on it'
),
ObjectNotFound: createCustomError(
'ObjectNotFound',
'Object not found'
),
Unknown: createCustomError(
'Unknown',
'Unknown error occured'
)
};

View File

@@ -0,0 +1,7 @@
// Parse cloud does not supports setTimeout
// We do not store a setTimeout reference in the client everytime
// We only fallback to a fake setTimeout when not available
// setTimeout cannot be override globally sadly
module.exports = function exitPromise(fn, _setTimeout) {
_setTimeout(fn, 0);
};

9
themes/keepit/node_modules/algoliasearch/src/map.js generated vendored Normal file
View File

@@ -0,0 +1,9 @@
var foreach = require('foreach');
module.exports = function map(arr, fn) {
var newArr = [];
foreach(arr, function(item, itemIndex) {
newArr.push(fn(item, itemIndex, arr));
});
return newArr;
};

19
themes/keepit/node_modules/algoliasearch/src/merge.js generated vendored Normal file
View File

@@ -0,0 +1,19 @@
var foreach = require('foreach');
module.exports = function merge(destination/* , sources */) {
var sources = Array.prototype.slice.call(arguments);
foreach(sources, function(source) {
for (var keyName in source) {
if (source.hasOwnProperty(keyName)) {
if (typeof destination[keyName] === 'object' && typeof source[keyName] === 'object') {
destination[keyName] = merge({}, destination[keyName], source[keyName]);
} else if (source[keyName] !== undefined) {
destination[keyName] = source[keyName];
}
}
}
});
return destination;
};

Some files were not shown because too many files have changed in this diff Show More