├── app
├── templates
│ └── setup.html
├── images
│ ├── agroups-logo.png
│ └── github-circled-alt.svg
├── hello_world
│ ├── hello_universe.js
│ ├── hello_world.js
│ ├── hello_universe.spec.js
│ └── hello_world.spec.js
├── spec.js
├── app.js
├── controllers
│ ├── project.js
│ ├── dashboard.js
│ ├── setupLoader.js
│ └── setup.js
├── spec.html
├── lib
│ ├── electron_boilerplate
│ │ ├── env_config.js
│ │ ├── dev_helper.js
│ │ ├── context_menu.js
│ │ ├── window_state.js
│ │ └── external_links.js
│ └── jasmine
│ │ ├── boot.js
│ │ ├── jasmine-html.js
│ │ ├── jasmine.css
│ │ └── jasmine.js
├── package.json
├── stylesheets
│ └── main.less
├── background.js
└── app.html
├── .bowerrc
├── .travis.yml
├── gulpfile.js
├── resources
├── icon.png
├── icon.psd
├── osx
│ ├── icon.icns
│ ├── dmg-icon.icns
│ ├── dmg-background.png
│ ├── dmg-background.psd
│ ├── dmg-background@2x.png
│ ├── dmg-background@2x.psd
│ ├── appdmg.json
│ ├── helper_apps
│ │ ├── Info.plist
│ │ ├── Info EH.plist
│ │ └── Info NP.plist
│ └── Info.plist
├── windows
│ ├── icon.ico
│ ├── setup-icon.ico
│ ├── setup-banner.bmp
│ ├── setup-banner.psd
│ └── installer.nsi
├── agroups-io-printscreen.png
└── linux
│ ├── DEBIAN
│ └── control
│ └── app.desktop
├── .gitignore
├── config
├── env_test.json
├── env_development.json
└── env_production.json
├── tasks
├── release.js
├── generate_specs_import.js
├── utils.js
├── start.js
├── app_npm_install.js
├── release_linux.js
├── release_windows.js
├── build.js
└── release_osx.js
├── bower.json
├── package.json
├── LICENSE
├── README.md
└── .jshintrc
/app/templates/setup.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.bowerrc:
--------------------------------------------------------------------------------
1 | {
2 | "directory": "app/vendor"
3 | }
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "0.12"
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | require('./tasks/build');
4 | require('./tasks/release');
5 |
--------------------------------------------------------------------------------
/resources/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgroupsIO/AgroupsIO-Desktop/HEAD/resources/icon.png
--------------------------------------------------------------------------------
/resources/icon.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgroupsIO/AgroupsIO-Desktop/HEAD/resources/icon.psd
--------------------------------------------------------------------------------
/resources/osx/icon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgroupsIO/AgroupsIO-Desktop/HEAD/resources/osx/icon.icns
--------------------------------------------------------------------------------
/app/images/agroups-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgroupsIO/AgroupsIO-Desktop/HEAD/app/images/agroups-logo.png
--------------------------------------------------------------------------------
/resources/osx/dmg-icon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgroupsIO/AgroupsIO-Desktop/HEAD/resources/osx/dmg-icon.icns
--------------------------------------------------------------------------------
/resources/windows/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgroupsIO/AgroupsIO-Desktop/HEAD/resources/windows/icon.ico
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | *.log
3 | .DS_Store
4 | Thumbs.db
5 | /build/
6 | /releases/
7 | /tmp/
8 | /.idea/
9 | /app/vendor
--------------------------------------------------------------------------------
/config/env_test.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "test",
3 | "description": "Add here any environment specific stuff you like."
4 | }
5 |
--------------------------------------------------------------------------------
/resources/osx/dmg-background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgroupsIO/AgroupsIO-Desktop/HEAD/resources/osx/dmg-background.png
--------------------------------------------------------------------------------
/resources/osx/dmg-background.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgroupsIO/AgroupsIO-Desktop/HEAD/resources/osx/dmg-background.psd
--------------------------------------------------------------------------------
/resources/windows/setup-icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgroupsIO/AgroupsIO-Desktop/HEAD/resources/windows/setup-icon.ico
--------------------------------------------------------------------------------
/resources/osx/dmg-background@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgroupsIO/AgroupsIO-Desktop/HEAD/resources/osx/dmg-background@2x.png
--------------------------------------------------------------------------------
/resources/osx/dmg-background@2x.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgroupsIO/AgroupsIO-Desktop/HEAD/resources/osx/dmg-background@2x.psd
--------------------------------------------------------------------------------
/resources/windows/setup-banner.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgroupsIO/AgroupsIO-Desktop/HEAD/resources/windows/setup-banner.bmp
--------------------------------------------------------------------------------
/resources/windows/setup-banner.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgroupsIO/AgroupsIO-Desktop/HEAD/resources/windows/setup-banner.psd
--------------------------------------------------------------------------------
/app/hello_world/hello_universe.js:
--------------------------------------------------------------------------------
1 | var greet = function () {
2 | return 'Hello Universe!';
3 | };
4 |
5 | export default greet;
6 |
--------------------------------------------------------------------------------
/resources/agroups-io-printscreen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgroupsIO/AgroupsIO-Desktop/HEAD/resources/agroups-io-printscreen.png
--------------------------------------------------------------------------------
/config/env_development.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "development",
3 | "description": "Add here any environment specific stuff you like."
4 | }
5 |
--------------------------------------------------------------------------------
/config/env_production.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "production",
3 | "description": "Add here any environment specific stuff you like."
4 | }
5 |
--------------------------------------------------------------------------------
/app/hello_world/hello_world.js:
--------------------------------------------------------------------------------
1 | export var greet = function () {
2 | return 'Agroups.IO';
3 | };
4 |
5 | export var bye = function () {
6 | return 'See ya!';
7 | };
8 |
--------------------------------------------------------------------------------
/resources/linux/DEBIAN/control:
--------------------------------------------------------------------------------
1 | Package: {{name}}
2 | Version: {{version}}
3 | Maintainer: {{author}}
4 | Priority: optional
5 | Architecture: amd64
6 | Installed-Size: {{size}}
7 | Description: {{description}}
8 |
--------------------------------------------------------------------------------
/app/spec.js:
--------------------------------------------------------------------------------
1 | // This file is generated automatically.
2 | // All your modifications to it will be lost (so don't do it).
3 | import "./hello_world/hello_universe.spec.js";
4 | import "./hello_world/hello_world.spec.js";
--------------------------------------------------------------------------------
/resources/linux/app.desktop:
--------------------------------------------------------------------------------
1 | [Desktop Entry]
2 | Version=1.0
3 | Type=Application
4 | Encoding=UTF-8
5 | Name={{productName}}
6 | Comment={{description}}
7 | Exec=/opt/{{name}}/{{name}}
8 | Icon=/opt/{{name}}/icon.png
9 | Terminal=false
10 | Categories=Application;
11 |
--------------------------------------------------------------------------------
/app/app.js:
--------------------------------------------------------------------------------
1 | import { setup } from './controllers/setup';
2 |
3 | window.jQuery = window.$ = require('jquery');
4 | window.Hammer = require('hammerjs');
5 |
6 | $(document).ready(() => {
7 |
8 | setup.render();
9 | !navigator.onLine ? setup.offlineChecker() : null;
10 |
11 | });
12 |
--------------------------------------------------------------------------------
/app/controllers/project.js:
--------------------------------------------------------------------------------
1 | export var project = {
2 | projectDir: null,
3 | npm: {
4 | file: null,
5 | dependenciesShow: [],
6 | devDependenciesShow: [],
7 | optionalDependenciesShow: []
8 | },
9 | bower: null,
10 | composer: null
11 | };
12 |
--------------------------------------------------------------------------------
/app/hello_world/hello_universe.spec.js:
--------------------------------------------------------------------------------
1 | // Default imports test
2 | import greet from './hello_universe';
3 |
4 | describe("hello universe", function () {
5 |
6 | it("greets better than hello world", function () {
7 | expect(greet()).toBe('Hello Universe!');
8 | });
9 |
10 | });
11 |
--------------------------------------------------------------------------------
/resources/osx/appdmg.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "{{productName}}",
3 | "icon": "{{dmgIcon}}",
4 | "background": "{{dmgBackground}}",
5 | "icon-size": 128,
6 | "contents": [
7 | { "x": 410, "y": 220, "type": "link", "path": "/Applications" },
8 | { "x": 130, "y": 220, "type": "file", "path": "{{appPath}}" }
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/app/hello_world/hello_world.spec.js:
--------------------------------------------------------------------------------
1 | // Named imports test
2 | import { greet, bye } from './hello_world';
3 |
4 | describe("hello world", function () {
5 |
6 | it("greets", function () {
7 | expect(greet()).toBe('Hello World!');
8 | });
9 |
10 | it("says goodbye", function () {
11 | expect(bye()).toBe('See ya!');
12 | });
13 |
14 | });
15 |
--------------------------------------------------------------------------------
/tasks/release.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var gulp = require('gulp');
4 | var utils = require('./utils');
5 |
6 | var releaseForOs = {
7 | osx: require('./release_osx'),
8 | linux: require('./release_linux'),
9 | windows: require('./release_windows'),
10 | };
11 |
12 | gulp.task('release', ['build'], function () {
13 | return releaseForOs[utils.os()]();
14 | });
15 |
--------------------------------------------------------------------------------
/app/spec.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Jasmine Spec Runner
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/app/lib/electron_boilerplate/env_config.js:
--------------------------------------------------------------------------------
1 | // Loads config/env_XXX.json file and puts it
2 | // in proper place for given Electron context.
3 |
4 | 'use strict';
5 |
6 | (function () {
7 | var jetpack = require('fs-jetpack');
8 | if (typeof window === 'object') {
9 | // Web browser context, __dirname points to folder where app.html file is.
10 | window.env = jetpack.read(__dirname + '/env_config.json', 'json');
11 | } else {
12 | // Node context
13 | module.exports = jetpack.read(__dirname + '/../../env_config.json', 'json');
14 | }
15 | }());
16 |
--------------------------------------------------------------------------------
/resources/osx/helper_apps/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleIdentifier
6 | {{identifier}}.helper
7 | CFBundleName
8 | {{productName}} Helper
9 | CFBundlePackageType
10 | APPL
11 | DTSDKName
12 | macosx
13 | LSUIElement
14 |
15 | NSSupportsAutomaticGraphicsSwitching
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/app/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "agroups-io",
3 | "productName": "Agroups.IO",
4 | "identifier": "com.example.agroups-io",
5 | "description": "A open-source desktop GUI app to manage all package managers like a bower, npm and composer.",
6 | "version": "0.1.0",
7 | "author": "Jonatas Freitas ",
8 | "main": "background.js",
9 | "config": {
10 | "target": "development"
11 | },
12 | "dependencies": {
13 | "fs-jetpack": "^0.7.0",
14 | "hammerjs": "^2.0.4",
15 | "jquery": "^2.1.4",
16 | "mustache": "^2.1.3",
17 | "npm": "^3.3.5",
18 | "q": "^1.4.1"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "agroups-io",
3 | "version": "0.1.0",
4 | "homepage": "https://github.com/AgroupsIO/AgroupsIO-Desktop",
5 | "authors": [
6 | "Jonatas Freitas "
7 | ],
8 | "moduleType": [
9 | "amd",
10 | "es6",
11 | "node"
12 | ],
13 | "license": "MIT",
14 | "ignore": [
15 | "**/.*",
16 | "node_modules",
17 | "bower_components",
18 | "app/vendor",
19 | "test",
20 | "tests"
21 | ],
22 | "dependencies": {
23 | "hammerjs": "~2.0.4",
24 | "Materialize": "materialize#~0.97.1",
25 | "mustache.js": "mustache#~2.1.3"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/resources/osx/helper_apps/Info EH.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDisplayName
6 | {{productName}} Helper EH
7 | CFBundleExecutable
8 | {{productName}} Helper EH
9 | CFBundleIdentifier
10 | {{identifier}}.helper.EH
11 | CFBundleName
12 | {{productName}} Helper EH
13 | CFBundlePackageType
14 | APPL
15 | DTSDKName
16 | macosx
17 | LSUIElement
18 |
19 | NSSupportsAutomaticGraphicsSwitching
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/resources/osx/helper_apps/Info NP.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDisplayName
6 | {{productName}} Helper NP
7 | CFBundleExecutable
8 | {{productName}} Helper NP
9 | CFBundleIdentifier
10 | {{identifier}}.helper.NP
11 | CFBundleName
12 | {{productName}} Helper NP
13 | CFBundlePackageType
14 | APPL
15 | DTSDKName
16 | macosx
17 | LSUIElement
18 |
19 | NSSupportsAutomaticGraphicsSwitching
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "devDependencies": {
3 | "asar": "^0.7.2",
4 | "electron-prebuilt": "^0.32.2",
5 | "fs-jetpack": "^0.7.0",
6 | "gulp": "^3.9.0",
7 | "gulp-less": "^3.0.3",
8 | "gulp-mustache": "^1.1.2",
9 | "gulp-util": "^3.0.6",
10 | "q": "^1.4.1",
11 | "rollup": "^0.16.1",
12 | "tree-kill": "^0.1.1",
13 | "yargs": "^3.15.0"
14 | },
15 | "optionalDependencies": {
16 | "appdmg": "^0.3.2",
17 | "rcedit": "^0.3.0"
18 | },
19 | "scripts": {
20 | "postinstall": "node ./tasks/app_npm_install && npm install bower -g && bower install",
21 | "app-install": "node ./tasks/app_npm_install && npm install bower -g && bower install",
22 | "build": "./node_modules/.bin/gulp build",
23 | "release": "./node_modules/.bin/gulp release --env=production",
24 | "start": "node ./tasks/start",
25 | "test": "node ./tasks/start --env=test"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/tasks/generate_specs_import.js:
--------------------------------------------------------------------------------
1 | // Spec files are scattered through the whole project. Here we're searching
2 | // for them and generate one entry file which will run all the tests.
3 |
4 | 'use strict';
5 |
6 | var jetpack = require('fs-jetpack');
7 | var srcDir = jetpack.cwd('app');
8 |
9 | var fileName = 'spec.js';
10 | var fileBanner = "// This file is generated automatically.\n"
11 | + "// All your modifications to it will be lost (so don't do it).\n";
12 | var whatToInclude = [
13 | '*.spec.js',
14 | '!node_modules/**',
15 | ];
16 |
17 | module.exports = function () {
18 | return srcDir.findAsync('.', { matching: whatToInclude }, 'relativePath')
19 | .then(function (specPaths) {
20 | var fileContent = specPaths.map(function (path) {
21 | return 'import "' + path + '";';
22 | }).join('\n');
23 | return srcDir.writeAsync(fileName, fileBanner + fileContent);
24 | })
25 | .then(function () {
26 | return srcDir.path(fileName);
27 | });
28 | };
29 |
--------------------------------------------------------------------------------
/app/lib/electron_boilerplate/dev_helper.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var app = require('app');
4 | var Menu = require('menu');
5 | var BrowserWindow = require('browser-window');
6 |
7 | module.exports.setDevMenu = function () {
8 | var devMenu = Menu.buildFromTemplate([{
9 | label: 'Development',
10 | submenu: [{
11 | label: 'Reload',
12 | accelerator: 'CmdOrCtrl+R',
13 | click: function () {
14 | BrowserWindow.getFocusedWindow().reloadIgnoringCache();
15 | }
16 | },{
17 | label: 'Toggle DevTools',
18 | accelerator: 'Alt+CmdOrCtrl+I',
19 | click: function () {
20 | BrowserWindow.getFocusedWindow().toggleDevTools();
21 | }
22 | },{
23 | label: 'Quit',
24 | accelerator: 'CmdOrCtrl+Q',
25 | click: function () {
26 | app.quit();
27 | }
28 | }]
29 | }]);
30 | Menu.setApplicationMenu(devMenu);
31 | };
32 |
--------------------------------------------------------------------------------
/tasks/utils.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var argv = require('yargs').argv;
4 | var os = require('os');
5 | var jetpack = require('fs-jetpack');
6 |
7 | module.exports.os = function () {
8 | switch (os.platform()) {
9 | case 'darwin':
10 | return 'osx';
11 | case 'linux':
12 | return 'linux';
13 | case 'win32':
14 | return 'windows';
15 | }
16 | return 'unsupported';
17 | };
18 |
19 | module.exports.replace = function (str, patterns) {
20 | Object.keys(patterns).forEach(function (pattern) {
21 | var matcher = new RegExp('{{' + pattern + '}}', 'g');
22 | str = str.replace(matcher, patterns[pattern]);
23 | });
24 | return str;
25 | };
26 |
27 | module.exports.getEnvName = function () {
28 | return argv.env || 'development';
29 | };
30 |
31 | module.exports.getElectronVersion = function () {
32 | var manifest = jetpack.read(__dirname + '/../package.json', 'json');
33 | return manifest.devDependencies['electron-prebuilt'].substring(1);
34 | };
35 |
--------------------------------------------------------------------------------
/resources/osx/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDisplayName
6 | {{productName}}
7 | CFBundleExecutable
8 | {{productName}}
9 | CFBundleIconFile
10 | icon.icns
11 | CFBundleIdentifier
12 | {{identifier}}
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | {{productName}}
17 | CFBundlePackageType
18 | APPL
19 | CFBundleVersion
20 | {{version}}
21 | CFBundleGetInfoString
22 | {{version}}
23 | LSMinimumSystemVersion
24 | 10.8.0
25 | NSMainNibFile
26 | MainMenu
27 | NSPrincipalClass
28 | AtomApplication
29 | NSSupportsAutomaticGraphicsSwitching
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 AgroupsIO
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/app/lib/electron_boilerplate/context_menu.js:
--------------------------------------------------------------------------------
1 | // This gives you default context menu (cut, copy, paste)
2 | // in all input fields and textareas across your app.
3 |
4 | (function () {
5 | 'use strict';
6 |
7 | var remote = require('remote');
8 | var Menu = remote.require('menu');
9 | var MenuItem = remote.require('menu-item');
10 |
11 | var cut = new MenuItem({
12 | label: "Cut",
13 | click: function () {
14 | document.execCommand("cut");
15 | }
16 | });
17 |
18 | var copy = new MenuItem({
19 | label: "Copy",
20 | click: function () {
21 | document.execCommand("copy");
22 | }
23 | });
24 |
25 | var paste = new MenuItem({
26 | label: "Paste",
27 | click: function () {
28 | document.execCommand("paste");
29 | }
30 | });
31 |
32 | var textMenu = new Menu();
33 | textMenu.append(cut);
34 | textMenu.append(copy);
35 | textMenu.append(paste);
36 |
37 | document.addEventListener('contextmenu', function(e) {
38 |
39 | switch (e.target.nodeName) {
40 | case 'TEXTAREA':
41 | case 'INPUT':
42 | e.preventDefault();
43 | textMenu.popup(remote.getCurrentWindow());
44 | break;
45 | }
46 |
47 | }, false);
48 |
49 | }());
50 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | #### In progress, not have any release.
2 |
3 | # Agroups.io
4 | []()
5 |
6 |
7 | 
8 |
9 | The open-source project Agroups.io is a desktop application for OSX, Linux and Windows developed entirely with javascript based on *[Electron](http://electron.atom.io/)*.
10 |
11 | With it you can in a graphical interface manage packages managers like npm (node.js), composer (php) and Bower (js).
12 |
13 | In the future we will also implement the functionality to manage the Grunt and Karma configuration files.
14 |
15 | ####Contribute to the Agroups.io, it's yours too.
16 |
17 | ## To run and build project:
18 | ##### Prerequisites:
19 | - *[Node.js](https://nodejs.org/download)*
20 |
21 | ##### After install prerequisites, run:
22 | - ```npm install``` to install all Node and Bower dependencies.
23 |
24 | ##### To start development environment, run:
25 | - ```npm start```
26 |
27 | ##### To start test environment, run:
28 | - ```npm test```
29 |
30 | ##### To build release for OSX, Win and Linux, run:
31 | - ```npm run release```
32 |
33 | ## Documentation
34 | - *[Roadmap](https://trello.com/b/IgWdeH1j/agroups-io-roadmap)*
35 | - *[Wireframe](https://moqups.com/jonatasfreitasv@gmail.com/dOHIovDo)*
36 |
37 | ## Licence
38 | *[MIT](http://opensource.org/licenses/MIT)*
39 |
--------------------------------------------------------------------------------
/app/lib/electron_boilerplate/window_state.js:
--------------------------------------------------------------------------------
1 | // Simple module to help you remember the size and position of windows.
2 | // Can be used for more than one window, just construct many
3 | // instances of it and give each different name.
4 |
5 | 'use strict';
6 |
7 | var app = require('app');
8 | var jetpack = require('fs-jetpack');
9 |
10 | module.exports = function (name, defaults) {
11 |
12 | var userDataDir = jetpack.cwd(app.getPath('userData'));
13 | var stateStoreFile = 'window-state-' + name +'.json';
14 |
15 | var state = userDataDir.read(stateStoreFile, 'json') || {
16 | width: defaults.width,
17 | height: defaults.height
18 | };
19 |
20 | var saveState = function (win) {
21 | if (!win.isMaximized() && !win.isMinimized()) {
22 | var position = win.getPosition();
23 | var size = win.getSize();
24 | state.x = position[0];
25 | state.y = position[1];
26 | state.width = size[0];
27 | state.height = size[1];
28 | }
29 | state.isMaximized = win.isMaximized();
30 | userDataDir.write(stateStoreFile, state, { atomic: true });
31 | };
32 |
33 | return {
34 | get x() { return state.x; },
35 | get y() { return state.y; },
36 | get width() { return state.width; },
37 | get height() { return state.height; },
38 | get isMaximized() { return state.isMaximized; },
39 | saveState: saveState
40 | };
41 | };
42 |
--------------------------------------------------------------------------------
/app/stylesheets/main.less:
--------------------------------------------------------------------------------
1 | @import (css) "../vendor/Materialize/dist/css/materialize.min.css";
2 |
3 | html, body {
4 | width: 100%;
5 | height: 100%;
6 | margin: 0;
7 | padding: 0;
8 | -webkit-touch-callout: none;
9 | -webkit-user-select: none;
10 | -khtml-user-select: none;
11 | -moz-user-select: none;
12 | -ms-user-select: none;
13 | user-select: none;
14 | }
15 |
16 | h1, h2, h3, h4, h5
17 | {
18 | color: #9E9E9E;
19 | }
20 |
21 | body {
22 | display: flex;
23 | justify-content: center;
24 | align-items: center;
25 | font-family: 'Roboto', sans-serif;
26 | background-color: #263238;
27 | }
28 |
29 | a {
30 | text-decoration: none;
31 | }
32 |
33 | #target{
34 | height: 100%;
35 | width: 100%;
36 | }
37 |
38 | #setupContainer{
39 | margin-top: 90px;
40 | }
41 |
42 |
43 | #setupLoaderContainer {
44 | margin-top: 226px;
45 | }
46 |
47 | #logoHeader{
48 | float: left;
49 | margin-top: 15px;
50 | height: 37px;
51 | }
52 |
53 | #titleHeader{
54 | float: left;
55 | font-size: 26px;
56 | margin-left: 13px;
57 | cursor: pointer;
58 | }
59 |
60 | .top-nav {
61 | padding-left: 18px;
62 | }
63 |
64 | .preLoaderContainer{
65 | position: fixed;
66 | top: 250px;
67 | left: 470px;
68 | }
69 |
70 | .top-nav{
71 | background-color: #455a64;
72 | margin-bottom: 13px;
73 | }
74 |
75 | .dep-collection {
76 | padding-bottom: 10px;
77 | }
78 |
79 | .dep-collection li {
80 | float: left;
81 | width: 312px;
82 | margin-left: 10px !important;
83 | margin-top: 10px !important;
84 | }
85 |
--------------------------------------------------------------------------------
/app/lib/electron_boilerplate/external_links.js:
--------------------------------------------------------------------------------
1 | // Convenient way for opening links in external browser, not in the app.
2 | // Useful especially if you have a lot of links to deal with.
3 | //
4 | // Usage:
5 | //
6 | // Every link with class ".js-external-link" will be opened in external browser.
7 | // google
8 | //
9 | // The same behaviour for many links can be achieved by adding
10 | // this class to any parent tag of an anchor tag.
11 | //
12 | // google
13 | // bing
14 | //
15 |
16 | (function () {
17 | 'use strict';
18 |
19 | var shell = require('shell');
20 |
21 | var supportExternalLinks = function (e) {
22 | var href;
23 | var isExternal = false;
24 |
25 | var checkDomElement = function (element) {
26 | if (element.nodeName === 'A') {
27 | href = element.getAttribute('href');
28 | }
29 | if (element.classList.contains('js-external-link')) {
30 | isExternal = true;
31 | }
32 | if (href && isExternal) {
33 | shell.openExternal(href);
34 | e.preventDefault();
35 | } else if (element.parentElement) {
36 | checkDomElement(element.parentElement);
37 | }
38 | }
39 |
40 | checkDomElement(e.target);
41 | }
42 |
43 | document.addEventListener('click', supportExternalLinks, false);
44 | }());
45 |
--------------------------------------------------------------------------------
/app/background.js:
--------------------------------------------------------------------------------
1 | // This is main process of Electron, started as first thing when the Electron
2 | // app starts, and running through entire life of your application.
3 | // It doesn't have any windows which you can see on screen, but we can open
4 | // window from here.
5 |
6 | var app = require('app');
7 | var BrowserWindow = require('browser-window');
8 | var env = require('./lib/electron_boilerplate/env_config');
9 | var devHelper = require('./lib/electron_boilerplate/dev_helper');
10 | var windowStateKeeper = require('./lib/electron_boilerplate/window_state');
11 | var dialog = require('dialog');
12 |
13 | var mainWindow;
14 |
15 | // Preserver of the window size and position between app launches.
16 | var mainWindowState = windowStateKeeper('main', {
17 | width: 1000,
18 | height: 600
19 | });
20 |
21 | app.on('ready', function () {
22 |
23 | mainWindow = new BrowserWindow({
24 | width: mainWindowState.width,
25 | height: mainWindowState.height,
26 | center: true,
27 | resizable: false,
28 | fullscreen: false
29 | });
30 |
31 | /*
32 | if (mainWindowState.isMaximized) {
33 | mainWindow.maximize();
34 | }
35 | */
36 |
37 | if (env.name === 'test') {
38 | mainWindow.loadUrl('file://' + __dirname + '/spec.html');
39 | } else {
40 | mainWindow.loadUrl('file://' + __dirname + '/app.html');
41 | }
42 |
43 | if (env.name !== 'production') {
44 | devHelper.setDevMenu();
45 | //mainWindow.openDevTools();
46 | }
47 |
48 | mainWindow.on('close', function () {
49 | mainWindowState.saveState(mainWindow);
50 | });
51 | });
52 |
53 | app.on('window-all-closed', function () {
54 | app.quit();
55 | });
56 |
--------------------------------------------------------------------------------
/tasks/start.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var Q = require('q');
4 | var electron = require('electron-prebuilt');
5 | var pathUtil = require('path');
6 | var childProcess = require('child_process');
7 | var kill = require('tree-kill');
8 | var utils = require('./utils');
9 | var watch;
10 |
11 | var gulpPath = pathUtil.resolve('./node_modules/.bin/gulp');
12 | if (process.platform === 'win32') {
13 | gulpPath += '.cmd';
14 | }
15 |
16 | var runBuild = function () {
17 | var deferred = Q.defer();
18 |
19 | var build = childProcess.spawn(gulpPath, [
20 | 'build',
21 | '--env=' + utils.getEnvName(),
22 | '--color'
23 | ], {
24 | stdio: 'inherit'
25 | });
26 |
27 | build.on('close', function (code) {
28 | deferred.resolve();
29 | });
30 |
31 | return deferred.promise;
32 | };
33 |
34 | var runGulpWatch = function () {
35 | watch = childProcess.spawn(gulpPath, [
36 | 'watch',
37 | '--env=' + utils.getEnvName(),
38 | '--color'
39 | ], {
40 | stdio: 'inherit'
41 | });
42 |
43 | watch.on('close', function (code) {
44 | // Gulp watch exits when error occured during build.
45 | // Just respawn it then.
46 | runGulpWatch();
47 | });
48 | };
49 |
50 | var runApp = function () {
51 | var app = childProcess.spawn(electron, ['./build'], {
52 | stdio: 'inherit'
53 | });
54 |
55 | app.on('close', function (code) {
56 | // User closed the app. Kill the host process.
57 | kill(watch.pid, 'SIGKILL', function () {
58 | process.exit();
59 | });
60 | });
61 | };
62 |
63 | runBuild()
64 | .then(function () {
65 | runGulpWatch();
66 | runApp();
67 | });
--------------------------------------------------------------------------------
/app/images/github-circled-alt.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tasks/app_npm_install.js:
--------------------------------------------------------------------------------
1 | // This script allows you to install native modules (those which have
2 | // to be compiled) for your Electron app.
3 | // The problem is that 'npm install' compiles them against node.js you have
4 | // installed on your computer, NOT against node.js used in Electron
5 | // runtime we've downloaded to this project.
6 |
7 | 'use strict';
8 |
9 | var childProcess = require('child_process');
10 | var jetpack = require('fs-jetpack');
11 | var argv = require('yargs').argv;
12 |
13 | var utils = require('./utils');
14 |
15 | var electronVersion = utils.getElectronVersion();
16 |
17 | var nodeModulesDir = jetpack.cwd(__dirname + '/../app/node_modules')
18 | var dependenciesCompiledAgainst = nodeModulesDir.read('electron_version');
19 |
20 | // When you raised version of Electron used in your project, the safest
21 | // thing to do is remove all installed dependencies and install them
22 | // once again (so they compile against new version if you use any
23 | // native package).
24 | if (electronVersion !== dependenciesCompiledAgainst) {
25 | nodeModulesDir.dir('.', { empty: true });
26 | nodeModulesDir.write('electron_version', electronVersion);
27 | }
28 |
29 | // Tell the 'npm install' which is about to start that we want for it
30 | // to compile for Electron.
31 | process.env.npm_config_disturl = "https://atom.io/download/atom-shell";
32 | process.env.npm_config_target = electronVersion;
33 |
34 | var params = ['install'];
35 | // Maybe there was name of package user wants to install passed as a parameter.
36 | if (argv._.length > 0) {
37 | params.push(argv._[0]);
38 | params.push('--save');
39 | }
40 |
41 |
42 | var installCommand = null;
43 |
44 | if (process.platform === 'win32') {
45 | installCommand = 'npm.cmd'
46 | } else {
47 | installCommand = 'npm'
48 | }
49 |
50 | var install = childProcess.spawn(installCommand, params, {
51 | cwd: __dirname + '/../app',
52 | env: process.env,
53 | stdio: 'inherit'
54 | });
55 |
--------------------------------------------------------------------------------
/app/controllers/dashboard.js:
--------------------------------------------------------------------------------
1 | var Mustache = require('mustache');
2 | var npm = require('npm');
3 | var Q = require('q');
4 | var shell = require('shell');
5 |
6 | window.jQuery = window.$ = require('jquery');
7 | window.Hammer = require('hammerjs');
8 |
9 | import { project } from './project';
10 |
11 | export const dashboard = {
12 |
13 | template: $('#dashboard').html(),
14 |
15 | render: function() {
16 | $('#target').html(Mustache.render(this.template, this.view()));
17 | $('#dashboardContainer').fadeIn(1000);
18 | $('.collapsible').collapsible();
19 | this.bindEvents();
20 | },
21 |
22 | view: function(){
23 |
24 | let view = {
25 | dependenciesShow:[],
26 | dependenciesShowLength: 0,
27 | devDependenciesShow:[],
28 | devDependenciesShowLength: 0
29 | };
30 |
31 | // TODO: Change array to JSON
32 | $.each(project.npm.dependenciesShow, (key, value) => {
33 | $.each(value, (k, v) => {
34 | view.dependenciesShow.push(v);
35 | });
36 | });
37 | view.dependenciesShowLength = view.dependenciesShow.length;
38 |
39 | $.each(project.npm.devDependenciesShow, (key, value) => {
40 | $.each(value, (k, v) => {
41 | view.devDependenciesShow.push(v);
42 | });
43 | });
44 | view.devDependenciesShowLength = view.devDependenciesShow.length;
45 |
46 | return view;
47 |
48 | },
49 |
50 | bindEvents: function(){
51 |
52 | $('#titleHeader').on('click', () => location.reload());
53 |
54 | $('.external-link').on('click', (e) => {
55 |
56 | e.preventDefault();
57 |
58 | const url = e.currentTarget.href.replace(
59 | 'git+', '').replace(
60 | 'git:', 'https:').replace(
61 | 'ssh:', 'https:');
62 |
63 | shell.openExternal(url);
64 | console.log(url);
65 | });
66 |
67 | }
68 |
69 | };
--------------------------------------------------------------------------------
/tasks/release_linux.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var Q = require('q');
4 | var gulpUtil = require('gulp-util');
5 | var childProcess = require('child_process');
6 | var jetpack = require('fs-jetpack');
7 | var asar = require('asar');
8 | var utils = require('./utils');
9 |
10 | var projectDir;
11 | var releasesDir;
12 | var packName;
13 | var packDir;
14 | var tmpDir;
15 | var readyAppDir;
16 | var manifest;
17 |
18 | var init = function () {
19 | projectDir = jetpack;
20 | tmpDir = projectDir.dir('./tmp', { empty: true });
21 | releasesDir = projectDir.dir('./releases');
22 | manifest = projectDir.read('app/package.json', 'json');
23 | packName = manifest.name + '_' + manifest.version;
24 | packDir = tmpDir.dir(packName);
25 | readyAppDir = packDir.cwd('opt', manifest.name);
26 |
27 | return Q();
28 | };
29 |
30 | var copyRuntime = function () {
31 | return projectDir.copyAsync('node_modules/electron-prebuilt/dist', readyAppDir.path(), { overwrite: true });
32 | };
33 |
34 | var packageBuiltApp = function () {
35 | var deferred = Q.defer();
36 |
37 | asar.createPackage(projectDir.path('build'), readyAppDir.path('resources/app.asar'), function() {
38 | deferred.resolve();
39 | });
40 |
41 | return deferred.promise;
42 | };
43 |
44 | var finalize = function () {
45 | // Create .desktop file from the template
46 | var desktop = projectDir.read('resources/linux/app.desktop');
47 | desktop = utils.replace(desktop, {
48 | name: manifest.name,
49 | productName: manifest.productName,
50 | description: manifest.description,
51 | version: manifest.version,
52 | author: manifest.author
53 | });
54 | packDir.write('usr/share/applications/' + manifest.name + '.desktop', desktop);
55 |
56 | // Copy icon
57 | projectDir.copy('resources/icon.png', readyAppDir.path('icon.png'));
58 |
59 | return Q();
60 | };
61 |
62 | var renameApp = function() {
63 | return readyAppDir.renameAsync("electron", manifest.name);
64 | };
65 |
66 | var packToDebFile = function () {
67 | var deferred = Q.defer();
68 |
69 | var debFileName = packName + '_amd64.deb';
70 | var debPath = releasesDir.path(debFileName);
71 |
72 | gulpUtil.log('Creating DEB package...');
73 |
74 | // Counting size of the app in KiB
75 | var appSize = Math.round(readyAppDir.inspectTree('.').size / 1024);
76 |
77 | // Preparing debian control file
78 | var control = projectDir.read('resources/linux/DEBIAN/control');
79 | control = utils.replace(control, {
80 | name: manifest.name,
81 | description: manifest.description,
82 | version: manifest.version,
83 | author: manifest.author,
84 | size: appSize
85 | });
86 | packDir.write('DEBIAN/control', control);
87 |
88 | // Build the package...
89 | childProcess.exec('fakeroot dpkg-deb -Zxz --build ' + packDir.path() + ' ' + debPath,
90 | function (error, stdout, stderr) {
91 | if (error || stderr) {
92 | console.log("ERROR while building DEB package:");
93 | console.log(error);
94 | console.log(stderr);
95 | } else {
96 | gulpUtil.log('DEB package ready!', debPath);
97 | }
98 | deferred.resolve();
99 | });
100 |
101 | return deferred.promise;
102 | };
103 |
104 | var cleanClutter = function () {
105 | return tmpDir.removeAsync('.');
106 | };
107 |
108 | module.exports = function () {
109 | return init()
110 | .then(copyRuntime)
111 | .then(packageBuiltApp)
112 | .then(finalize)
113 | .then(renameApp)
114 | .then(packToDebFile)
115 | .then(cleanClutter);
116 | };
117 |
--------------------------------------------------------------------------------
/tasks/release_windows.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var Q = require('q');
4 | var gulpUtil = require('gulp-util');
5 | var childProcess = require('child_process');
6 | var jetpack = require('fs-jetpack');
7 | var asar = require('asar');
8 | var utils = require('./utils');
9 |
10 | var projectDir;
11 | var tmpDir;
12 | var releasesDir;
13 | var readyAppDir;
14 | var manifest;
15 |
16 | var init = function () {
17 | projectDir = jetpack;
18 | tmpDir = projectDir.dir('./tmp', { empty: true });
19 | releasesDir = projectDir.dir('./releases');
20 | manifest = projectDir.read('app/package.json', 'json');
21 | readyAppDir = tmpDir.cwd(manifest.name);
22 |
23 | return Q();
24 | };
25 |
26 | var copyRuntime = function () {
27 | return projectDir.copyAsync('node_modules/electron-prebuilt/dist', readyAppDir.path(), { overwrite: true });
28 | };
29 |
30 | var cleanupRuntime = function () {
31 | return readyAppDir.removeAsync('resources/default_app');
32 | };
33 |
34 | var packageBuiltApp = function () {
35 | var deferred = Q.defer();
36 |
37 | asar.createPackage(projectDir.path('build'), readyAppDir.path('resources/app.asar'), function() {
38 | deferred.resolve();
39 | });
40 |
41 | return deferred.promise;
42 | };
43 |
44 | var finalize = function () {
45 | var deferred = Q.defer();
46 |
47 | projectDir.copy('resources/windows/icon.ico', readyAppDir.path('icon.ico'));
48 |
49 | // Replace Electron icon for your own.
50 | var rcedit = require('rcedit');
51 | rcedit(readyAppDir.path('electron.exe'), {
52 | 'icon': projectDir.path('resources/windows/icon.ico'),
53 | 'version-string': {
54 | 'ProductName': manifest.productName,
55 | 'FileDescription': manifest.description,
56 | }
57 | }, function (err) {
58 | if (!err) {
59 | deferred.resolve();
60 | }
61 | });
62 |
63 | return deferred.promise;
64 | };
65 |
66 | var renameApp = function () {
67 | return readyAppDir.renameAsync('electron.exe', manifest.productName + '.exe');
68 | };
69 |
70 | var createInstaller = function () {
71 | var deferred = Q.defer();
72 |
73 | var finalPackageName = manifest.name + '_' + manifest.version + '.exe';
74 | var installScript = projectDir.read('resources/windows/installer.nsi');
75 | installScript = utils.replace(installScript, {
76 | name: manifest.name,
77 | productName: manifest.productName,
78 | version: manifest.version,
79 | src: readyAppDir.path(),
80 | dest: releasesDir.path(finalPackageName),
81 | icon: readyAppDir.path('icon.ico'),
82 | setupIcon: projectDir.path('resources/windows/setup-icon.ico'),
83 | banner: projectDir.path('resources/windows/setup-banner.bmp'),
84 | });
85 | tmpDir.write('installer.nsi', installScript);
86 |
87 | gulpUtil.log('Building installer with NSIS...');
88 |
89 | // Remove destination file if already exists.
90 | releasesDir.remove(finalPackageName);
91 |
92 | // Note: NSIS have to be added to PATH (environment variables).
93 | var nsis = childProcess.spawn('makensis', [
94 | tmpDir.path('installer.nsi')
95 | ], {
96 | stdio: 'inherit'
97 | });
98 | nsis.on('error', function (err) {
99 | if (err.message === 'spawn makensis ENOENT') {
100 | throw "Can't find NSIS. Are you sure you've installed it and"
101 | + " added to PATH environment variable?";
102 | } else {
103 | throw err;
104 | }
105 | });
106 | nsis.on('close', function () {
107 | gulpUtil.log('Installer ready!', releasesDir.path(finalPackageName));
108 | deferred.resolve();
109 | });
110 |
111 | return deferred.promise;
112 | };
113 |
114 | var cleanClutter = function () {
115 | return tmpDir.removeAsync('.');
116 | };
117 |
118 | module.exports = function () {
119 | return init()
120 | .then(copyRuntime)
121 | .then(cleanupRuntime)
122 | .then(packageBuiltApp)
123 | .then(finalize)
124 | .then(renameApp)
125 | .then(createInstaller)
126 | .then(cleanClutter);
127 | };
128 |
--------------------------------------------------------------------------------
/app/controllers/setupLoader.js:
--------------------------------------------------------------------------------
1 | var Mustache = require('mustache');
2 | var npm = require('npm');
3 | var Q = require('q');
4 |
5 | window.jQuery = window.$ = require('jquery');
6 | window.Hammer = require('hammerjs');
7 |
8 | import { project } from './project';
9 | import { dashboard } from './dashboard';
10 |
11 | export const setupLoader = {
12 |
13 | template: $('#setupLoader').html(),
14 |
15 | render: function() {
16 | $('#target').html(Mustache.render(this.template));
17 | $('#setupLoaderContainer').fadeIn(1000);
18 | this.load();
19 | },
20 |
21 | load: function() {
22 |
23 | this.loadDependencies().then(() => {
24 | this.loadDevDependencies().then(() => {
25 | console.log(project);
26 | dashboard.render();
27 | });
28 | });
29 |
30 | },
31 |
32 | increment: function(number){
33 | $('#setupLoaderProgress').width(number + '%');
34 | },
35 |
36 | text: function(text){
37 | $('#loaderText').html(text);
38 | },
39 |
40 | // TODO: refactor this with loadDevDependencies
41 | loadDependencies: function(){
42 |
43 | var deferred = Q.defer();
44 |
45 | if(!project.npm.file.dependencies) {
46 | deferred.resolve();
47 | return deferred.promise;
48 | }
49 |
50 | let totalOfDependencies = Object.keys(
51 | project.npm.file.dependencies).length;
52 |
53 | npm.load((err) => {
54 |
55 | err ? deferred.reject(new Error(err)) : null;
56 |
57 | $.each(project.npm.file.dependencies, (key, value) => {
58 | npm.commands.show([`${key}@${value.replace('^', '')}`], (err, data) => {
59 |
60 | // TODO: Change array to JSON
61 | !err ? project.npm.dependenciesShow.push(data) :
62 | project.npm.dependenciesShow.push(err);
63 |
64 | });
65 | });
66 |
67 | });
68 |
69 | var resolve = setInterval(() => {
70 | this.increment((project.npm.dependenciesShow.length/totalOfDependencies) * 100);
71 | this.text(`Load NPM dependencies information - ${project.npm.dependenciesShow.length} / ${totalOfDependencies}`);
72 |
73 | if(project.npm.dependenciesShow.length === totalOfDependencies){
74 | clearInterval(resolve);
75 | deferred.resolve();
76 | }
77 | }, 50);
78 |
79 | return deferred.promise;
80 |
81 | },
82 |
83 | // TODO: refactor this with loadDependencies
84 | loadDevDependencies: function(){
85 |
86 | var deferred = Q.defer();
87 |
88 | if(!project.npm.file.devDependencies) {
89 | deferred.resolve();
90 | return deferred.promise;
91 | }
92 |
93 | let totalOfDependencies = Object.keys(
94 | project.npm.file.devDependencies).length;
95 |
96 | totalOfDependencies < 0 ? deferred.resolve() : null;
97 |
98 | npm.load((err) => {
99 |
100 | err ? deferred.reject(new Error(err)) : null;
101 |
102 | $.each(project.npm.file.devDependencies, (key, value) => {
103 | npm.commands.show([`${key}@${value.replace('^', '')}`],
104 | (err, data) => {
105 |
106 | // TODO: Change array to JSON
107 | !err ? project.npm.devDependenciesShow.push(data) :
108 | project.npm.devDependenciesShow.push(err);
109 |
110 | });
111 | });
112 |
113 | });
114 |
115 | var resolve = setInterval(() => {
116 | this.increment((project.npm.devDependenciesShow.length/totalOfDependencies) * 100);
117 | this.text(`Load NPM dev dependencies information - ${project.npm.devDependenciesShow.length} / ${totalOfDependencies}`);
118 |
119 | if(project.npm.devDependenciesShow.length === totalOfDependencies){
120 | clearInterval(resolve);
121 | deferred.resolve();
122 | }
123 | }, 50);
124 |
125 | return deferred.promise;
126 |
127 | }
128 |
129 | };
130 |
--------------------------------------------------------------------------------
/tasks/build.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var pathUtil = require('path');
4 | var Q = require('q');
5 | var gulp = require('gulp');
6 | var rollup = require('rollup');
7 | var less = require('gulp-less');
8 | var jetpack = require('fs-jetpack');
9 |
10 | var utils = require('./utils');
11 | var generateSpecsImportFile = require('./generate_specs_import');
12 |
13 | var projectDir = jetpack;
14 | var srcDir = projectDir.cwd('./app');
15 | var destDir = projectDir.cwd('./build');
16 |
17 | var paths = {
18 | copyFromAppDir: [
19 | './node_modules/**',
20 | './vendor/**',
21 | './lib/**',
22 | './**/*.html',
23 | './images/*'
24 | ]
25 | };
26 |
27 | // -------------------------------------
28 | // Tasks
29 | // -------------------------------------
30 |
31 | gulp.task('clean', function(callback) {
32 | return destDir.dirAsync('.', { empty: true });
33 | });
34 |
35 |
36 | var copyTask = function () {
37 | return projectDir.copyAsync('app', destDir.path(), {
38 | overwrite: true,
39 | matching: paths.copyFromAppDir
40 | });
41 | };
42 | gulp.task('copy', ['clean'], copyTask);
43 | gulp.task('copy-watch', copyTask);
44 |
45 |
46 | var bundle = function (src, dest) {
47 | var deferred = Q.defer();
48 |
49 | rollup.rollup({
50 | entry: src
51 | }).then(function (bundle) {
52 | var jsFile = pathUtil.basename(dest);
53 | var result = bundle.generate({
54 | format: 'iife',
55 | sourceMap: true,
56 | sourceMapFile: jsFile
57 | });
58 | return Q.all([
59 | destDir.writeAsync(dest, result.code + '\n//# sourceMappingURL=' + jsFile + '.map'),
60 | destDir.writeAsync(dest + '.map', result.map.toString())
61 | ]);
62 | }).then(function () {
63 | deferred.resolve();
64 | }).catch(function (err) {
65 | console.error(err);
66 | });
67 |
68 | return deferred.promise;
69 | };
70 |
71 | var bundleApplication = function () {
72 | return Q.all([
73 | bundle(srcDir.path('background.js'), destDir.path('background.js')),
74 | bundle(srcDir.path('app.js'), destDir.path('app.js'))
75 | ]);
76 | };
77 |
78 | var bundleSpecs = function () {
79 | generateSpecsImportFile().then(function (specEntryPointPath) {
80 | return Q.all([
81 | bundle(srcDir.path('background.js'), destDir.path('background.js')),
82 | bundle(specEntryPointPath, destDir.path('spec.js'))
83 | ]);
84 | });
85 | };
86 |
87 | var bundleTask = function () {
88 | if (utils.getEnvName() === 'test') {
89 | return bundleSpecs();
90 | }
91 | return bundleApplication();
92 | };
93 | gulp.task('bundle', ['clean'], bundleTask);
94 | gulp.task('bundle-watch', bundleTask);
95 |
96 |
97 | var lessTask = function () {
98 | return gulp.src('app/stylesheets/main.less')
99 | .pipe(less())
100 | .pipe(gulp.dest(destDir.path('stylesheets')));
101 | };
102 | gulp.task('less', ['clean'], lessTask);
103 | gulp.task('less-watch', lessTask);
104 |
105 |
106 | gulp.task('finalize', ['clean'], function () {
107 | var manifest = srcDir.read('package.json', 'json');
108 | // Add "dev" or "test" suffix to name, so Electron will write all data
109 | // like cookies and localStorage in separate places for each environment.
110 | switch (utils.getEnvName()) {
111 | case 'development':
112 | manifest.name += '-dev';
113 | manifest.productName += ' Dev';
114 | break;
115 | case 'test':
116 | manifest.name += '-test';
117 | manifest.productName += ' Test';
118 | break;
119 | }
120 | destDir.write('package.json', manifest);
121 |
122 | var configFilePath = projectDir.path('config/env_' + utils.getEnvName() + '.json');
123 | destDir.copy(configFilePath, 'env_config.json');
124 | });
125 |
126 |
127 | gulp.task('watch', function () {
128 | gulp.watch('app/**/*.js', ['bundle-watch']);
129 | gulp.watch(paths.copyFromAppDir, { cwd: 'app' }, ['copy-watch']);
130 | gulp.watch('app/**/*.less', ['less-watch']);
131 | });
132 |
133 |
134 | gulp.task('build', ['bundle', 'less', 'copy', 'finalize']);
135 |
--------------------------------------------------------------------------------
/resources/windows/installer.nsi:
--------------------------------------------------------------------------------
1 | ; NSIS packaging/install script
2 | ; Docs: http://nsis.sourceforge.net/Docs/Contents.html
3 |
4 | !include LogicLib.nsh
5 | !include nsDialogs.nsh
6 |
7 | ; --------------------------------
8 | ; Variables
9 | ; --------------------------------
10 |
11 | !define dest "{{dest}}"
12 | !define src "{{src}}"
13 | !define name "{{name}}"
14 | !define productName "{{productName}}"
15 | !define version "{{version}}"
16 | !define icon "{{icon}}"
17 | !define setupIcon "{{setupIcon}}"
18 | !define banner "{{banner}}"
19 |
20 | !define exec "{{productName}}.exe"
21 |
22 | !define regkey "Software\${productName}"
23 | !define uninstkey "Software\Microsoft\Windows\CurrentVersion\Uninstall\${productName}"
24 |
25 | !define uninstaller "uninstall.exe"
26 |
27 | ; --------------------------------
28 | ; Installation
29 | ; --------------------------------
30 |
31 | SetCompressor lzma
32 |
33 | Name "${productName}"
34 | Icon "${setupIcon}"
35 | OutFile "${dest}"
36 | InstallDir "$PROGRAMFILES\${productName}"
37 | InstallDirRegKey HKLM "${regkey}" ""
38 |
39 | CRCCheck on
40 | SilentInstall normal
41 |
42 | XPStyle on
43 | ShowInstDetails nevershow
44 | AutoCloseWindow false
45 | WindowIcon off
46 |
47 | Caption "${productName} Setup"
48 | ; Don't add sub-captions to title bar
49 | SubCaption 3 " "
50 | SubCaption 4 " "
51 |
52 | Page custom welcome
53 | Page instfiles
54 |
55 | Var Image
56 | Var ImageHandle
57 |
58 | Function .onInit
59 |
60 | ; Extract banner image for welcome page
61 | InitPluginsDir
62 | ReserveFile "${banner}"
63 | File /oname=$PLUGINSDIR\banner.bmp "${banner}"
64 |
65 | FunctionEnd
66 |
67 | ; Custom welcome page
68 | Function welcome
69 |
70 | nsDialogs::Create 1018
71 |
72 | ${NSD_CreateLabel} 185 1u 210 100% "Welcome to ${productName} version ${version} installer.$\r$\n$\r$\nClick install to begin."
73 |
74 | ${NSD_CreateBitmap} 0 0 170 210 ""
75 | Pop $Image
76 | ${NSD_SetImage} $Image $PLUGINSDIR\banner.bmp $ImageHandle
77 |
78 | nsDialogs::Show
79 |
80 | ${NSD_FreeImage} $ImageHandle
81 |
82 | FunctionEnd
83 |
84 | ; Installation declarations
85 | Section "Install"
86 |
87 | WriteRegStr HKLM "${regkey}" "Install_Dir" "$INSTDIR"
88 | WriteRegStr HKLM "${uninstkey}" "DisplayName" "${productName}"
89 | WriteRegStr HKLM "${uninstkey}" "DisplayIcon" '"$INSTDIR\icon.ico"'
90 | WriteRegStr HKLM "${uninstkey}" "UninstallString" '"$INSTDIR\${uninstaller}"'
91 |
92 | ; Remove all application files copied by previous installation
93 | RMDir /r "$INSTDIR"
94 |
95 | SetOutPath $INSTDIR
96 |
97 | ; Include all files from /build directory
98 | File /r "${src}\*"
99 |
100 | ; Create start menu shortcut
101 | CreateShortCut "$SMPROGRAMS\${productName}.lnk" "$INSTDIR\${exec}" "" "$INSTDIR\icon.ico"
102 |
103 | WriteUninstaller "${uninstaller}"
104 |
105 | SectionEnd
106 |
107 | ; --------------------------------
108 | ; Uninstaller
109 | ; --------------------------------
110 |
111 | ShowUninstDetails nevershow
112 |
113 | UninstallCaption "Uninstall ${productName}"
114 | UninstallText "Don't like ${productName} anymore? Hit uninstall button."
115 | UninstallIcon "${icon}"
116 |
117 | UninstPage custom un.confirm un.confirmOnLeave
118 | UninstPage instfiles
119 |
120 | Var RemoveAppDataCheckbox
121 | Var RemoveAppDataCheckbox_State
122 |
123 | ; Custom uninstall confirm page
124 | Function un.confirm
125 |
126 | nsDialogs::Create 1018
127 |
128 | ${NSD_CreateLabel} 1u 1u 100% 24u "If you really want to remove ${productName} from your computer press uninstall button."
129 |
130 | ${NSD_CreateCheckbox} 1u 35u 100% 10u "Remove also my ${productName} personal data"
131 | Pop $RemoveAppDataCheckbox
132 |
133 | nsDialogs::Show
134 |
135 | FunctionEnd
136 |
137 | Function un.confirmOnLeave
138 |
139 | ; Save checkbox state on page leave
140 | ${NSD_GetState} $RemoveAppDataCheckbox $RemoveAppDataCheckbox_State
141 |
142 | FunctionEnd
143 |
144 | ; Uninstall declarations
145 | Section "Uninstall"
146 |
147 | DeleteRegKey HKLM "${uninstkey}"
148 | DeleteRegKey HKLM "${regkey}"
149 |
150 | Delete "$SMPROGRAMS\${productName}.lnk"
151 |
152 | ; Remove whole directory from Program Files
153 | RMDir /r "$INSTDIR"
154 |
155 | ; Remove also appData directory generated by your app if user checked this option
156 | ${If} $RemoveAppDataCheckbox_State == ${BST_CHECKED}
157 | RMDir /r "$LOCALAPPDATA\${name}"
158 | ${EndIf}
159 |
160 | SectionEnd
161 |
--------------------------------------------------------------------------------
/tasks/release_osx.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var Q = require('q');
4 | var gulpUtil = require('gulp-util');
5 | var jetpack = require('fs-jetpack');
6 | var asar = require('asar');
7 | var utils = require('./utils');
8 |
9 | var projectDir;
10 | var releasesDir;
11 | var tmpDir;
12 | var finalAppDir;
13 | var manifest;
14 |
15 | var init = function () {
16 | projectDir = jetpack;
17 | tmpDir = projectDir.dir('./tmp', { empty: true });
18 | releasesDir = projectDir.dir('./releases');
19 | manifest = projectDir.read('app/package.json', 'json');
20 | finalAppDir = tmpDir.cwd(manifest.productName + '.app');
21 |
22 | return Q();
23 | };
24 |
25 | var copyRuntime = function () {
26 | return projectDir.copyAsync('node_modules/electron-prebuilt/dist/Electron.app', finalAppDir.path());
27 | };
28 |
29 | var cleanupRuntime = function() {
30 | finalAppDir.remove('Contents/Resources/default_app');
31 | finalAppDir.remove('Contents/Resources/atom.icns');
32 | return Q();
33 | }
34 |
35 | var packageBuiltApp = function () {
36 | var deferred = Q.defer();
37 |
38 | asar.createPackage(projectDir.path('build'), finalAppDir.path('Contents/Resources/app.asar'), function() {
39 | deferred.resolve();
40 | });
41 |
42 | return deferred.promise;
43 | };
44 |
45 | var finalize = function () {
46 | // Prepare main Info.plist
47 | var info = projectDir.read('resources/osx/Info.plist');
48 | info = utils.replace(info, {
49 | productName: manifest.productName,
50 | identifier: manifest.identifier,
51 | version: manifest.version
52 | });
53 | finalAppDir.write('Contents/Info.plist', info);
54 |
55 | // Prepare Info.plist of Helper apps
56 | [' EH', ' NP', ''].forEach(function (helper_suffix) {
57 | info = projectDir.read('resources/osx/helper_apps/Info' + helper_suffix + '.plist');
58 | info = utils.replace(info, {
59 | productName: manifest.productName,
60 | identifier: manifest.identifier
61 | });
62 | finalAppDir.write('Contents/Frameworks/Electron Helper' + helper_suffix + '.app/Contents/Info.plist', info);
63 | });
64 |
65 | // Copy icon
66 | projectDir.copy('resources/osx/icon.icns', finalAppDir.path('Contents/Resources/icon.icns'));
67 |
68 | return Q();
69 | };
70 |
71 | var renameApp = function() {
72 | // Rename helpers
73 | [' Helper EH', ' Helper NP', ' Helper'].forEach(function (helper_suffix) {
74 | finalAppDir.rename('Contents/Frameworks/Electron' + helper_suffix + '.app/Contents/MacOS/Electron' + helper_suffix, manifest.productName + helper_suffix );
75 | finalAppDir.rename('Contents/Frameworks/Electron' + helper_suffix + '.app', manifest.productName + helper_suffix + '.app');
76 | });
77 | // Rename application
78 | finalAppDir.rename('Contents/MacOS/Electron', manifest.productName);
79 | return Q();
80 | }
81 |
82 | var packToDmgFile = function () {
83 | var deferred = Q.defer();
84 |
85 | var appdmg = require('appdmg');
86 | var dmgName = manifest.name + '_' + manifest.version + '.dmg';
87 |
88 | // Prepare appdmg config
89 | var dmgManifest = projectDir.read('resources/osx/appdmg.json');
90 | dmgManifest = utils.replace(dmgManifest, {
91 | productName: manifest.productName,
92 | appPath: finalAppDir.path(),
93 | dmgIcon: projectDir.path("resources/osx/dmg-icon.icns"),
94 | dmgBackground: projectDir.path("resources/osx/dmg-background.png")
95 | });
96 | tmpDir.write('appdmg.json', dmgManifest);
97 |
98 | // Delete DMG file with this name if already exists
99 | releasesDir.remove(dmgName);
100 |
101 | gulpUtil.log('Packaging to DMG file...');
102 |
103 | var readyDmgPath = releasesDir.path(dmgName);
104 | appdmg({
105 | source: tmpDir.path('appdmg.json'),
106 | target: readyDmgPath
107 | })
108 | .on('error', function (err) {
109 | console.error(err);
110 | })
111 | .on('finish', function () {
112 | gulpUtil.log('DMG file ready!', readyDmgPath);
113 | deferred.resolve();
114 | });
115 |
116 | return deferred.promise;
117 | };
118 |
119 | var cleanClutter = function () {
120 | return tmpDir.removeAsync('.');
121 | };
122 |
123 | module.exports = function () {
124 | return init()
125 | .then(copyRuntime)
126 | .then(cleanupRuntime)
127 | .then(packageBuiltApp)
128 | .then(finalize)
129 | .then(renameApp)
130 | .then(packToDmgFile)
131 | .then(cleanClutter);
132 | };
133 |
--------------------------------------------------------------------------------
/app/controllers/setup.js:
--------------------------------------------------------------------------------
1 | var os = require('os');
2 | var remote = require('remote');
3 | var dialog = remote.require('dialog');
4 | var app = remote.require('app');
5 | var jetpack = require('fs-jetpack');
6 | var Mustache = require('mustache');
7 | window.jQuery = window.$ = require('jquery');
8 | window.Hammer = require('hammerjs');
9 |
10 | import { project } from './project';
11 | import { setupLoader } from './setupLoader';
12 |
13 | export const setup = {
14 |
15 | template: $('#setup').html(),
16 |
17 | name: localStorage.getItem('name'),
18 |
19 | render: function() {
20 |
21 | $('#target').html(Mustache.render(this.template));
22 |
23 | !navigator.onLine ? this.offlineChecker() :
24 | this.name ? this.sayHello() : this.termsModal();
25 |
26 | $('#setupContainer').delay(500).fadeIn(1000);
27 | this.bindEvents();
28 |
29 | },
30 |
31 | bindEvents: function() {
32 |
33 | $('#btnDisagree').on('click', () => app.quit());
34 |
35 | $('#btnAgree').on('click',
36 | () => {
37 | $('#agreeModal').closeModal();
38 | this.nameModal();
39 | });
40 |
41 | $('#btnContinue').on('click',
42 | () => {
43 | if($('#firstName').val() !== '') {
44 | $('#nameModal').closeModal();
45 | localStorage.setItem('name', $('#firstName').val());
46 | this.sayHello();
47 | }
48 | });
49 |
50 | $('#selectFolder').on('click', () => {
51 | setup.setProjectFolder();
52 | this.setManagerFiles();
53 | this.start();
54 | });
55 |
56 | $('#firstName').on('keypress', (e) => {
57 | if(e.which === 13) {
58 | e.preventDefault();
59 | if($('#firstName').val() !== '') {
60 | $('#nameModal').closeModal();
61 | localStorage.setItem('name', $('#firstName').val());
62 | this.sayHello();
63 | }
64 | }
65 | });
66 |
67 | },
68 |
69 | termsModal: function () {
70 | $('#agreeModal').openModal({
71 | dismissible: false
72 | });
73 | },
74 |
75 | nameModal: function() {
76 | $('#nameModal').openModal({
77 | dismissible: false
78 | });
79 | },
80 |
81 | offlineChecker: function(){
82 |
83 | $('#offline').openModal({
84 | dismissible: false
85 | });
86 |
87 | const check = setInterval(() => {
88 | console.log('Checking');
89 | if(navigator.onLine){
90 | $('#offline').closeModal();
91 | this.render();
92 | clearInterval(check);
93 | }
94 | }, 1000);
95 |
96 | },
97 |
98 | noManagerFilesModal: function(){
99 | $('#noManagerFiles').openModal();
100 | },
101 |
102 | sayHello: function() {
103 |
104 | setTimeout(() => {
105 |
106 | const now = new Date();
107 | const hour = now.getHours();
108 | const greeting = hour < 12 ? 'morning' :
109 | hour >= 12 && hour <= 17 ? 'afternoon' :
110 | hour >= 17 ? 'evening' : 'job';
111 |
112 | const msg = name => `Hello ${name}, good ${greeting}! `;
113 |
114 | Materialize.toast(msg(localStorage.getItem('name')), 1500, 'black-text white');
115 |
116 | }, 1000);
117 |
118 | },
119 |
120 | setProjectFolder: function(){
121 | project.projectDir = dialog.showOpenDialog(
122 | { properties: ['openDirectory'] }
123 | )[0];
124 | },
125 |
126 | setManagerFiles: function(){
127 | project.npm.file = jetpack.read(project.projectDir + '/package.json', 'json');
128 | project.bower = jetpack.read(project.projectDir + '/bower.json', 'json');
129 | project.composer = jetpack.read(project.projectDir + '/composer.json', 'json');
130 | },
131 |
132 | checkManagerFiles: function(){
133 | return !!(project.npm || project.bower || project.composer);
134 | },
135 |
136 | start: function(){
137 |
138 | this.checkManagerFiles() ? this.goToSetupLoader() :
139 | this.noManagerFilesModal();
140 |
141 | },
142 |
143 | goToSetupLoader: function(){
144 |
145 | $('#setupContainer').fadeOut(500, () => {
146 | setupLoader.render();
147 | });
148 |
149 | }
150 |
151 | };
152 |
--------------------------------------------------------------------------------
/app/lib/jasmine/boot.js:
--------------------------------------------------------------------------------
1 | /**
2 | Starting with version 2.0, this file "boots" Jasmine, performing all of the necessary initialization before executing the loaded environment and all of a project's specs. This file should be loaded after `jasmine.js` and `jasmine_html.js`, but before any project source files or spec files are loaded. Thus this file can also be used to customize Jasmine for a project.
3 |
4 | If a project is using Jasmine via the standalone distribution, this file can be customized directly. If a project is using Jasmine via the [Ruby gem][jasmine-gem], this file can be copied into the support directory via `jasmine copy_boot_js`. Other environments (e.g., Python) will have different mechanisms.
5 |
6 | The location of `boot.js` can be specified and/or overridden in `jasmine.yml`.
7 |
8 | [jasmine-gem]: http://github.com/pivotal/jasmine-gem
9 | */
10 |
11 | (function() {
12 |
13 | /**
14 | * ## Require & Instantiate
15 | *
16 | * Require Jasmine's core files. Specifically, this requires and attaches all of Jasmine's code to the `jasmine` reference.
17 | */
18 | window.jasmine = jasmineRequire.core(jasmineRequire);
19 |
20 | /**
21 | * Since this is being run in a browser and the results should populate to an HTML page, require the HTML-specific Jasmine code, injecting the same reference.
22 | */
23 | jasmineRequire.html(jasmine);
24 |
25 | /**
26 | * Create the Jasmine environment. This is used to run all specs in a project.
27 | */
28 | var env = jasmine.getEnv();
29 |
30 | /**
31 | * ## The Global Interface
32 | *
33 | * Build up the functions that will be exposed as the Jasmine public interface. A project can customize, rename or alias any of these functions as desired, provided the implementation remains unchanged.
34 | */
35 | var jasmineInterface = jasmineRequire.interface(jasmine, env);
36 |
37 | /**
38 | * Add all of the Jasmine global/public interface to the proper global, so a project can use the public interface directly. For example, calling `describe` in specs instead of `jasmine.getEnv().describe`.
39 | */
40 | if (typeof window == "undefined" && typeof exports == "object") {
41 | extend(exports, jasmineInterface);
42 | } else {
43 | extend(window, jasmineInterface);
44 | }
45 |
46 | /**
47 | * ## Runner Parameters
48 | *
49 | * More browser specific code - wrap the query string in an object and to allow for getting/setting parameters from the runner user interface.
50 | */
51 |
52 | var queryString = new jasmine.QueryString({
53 | getWindowLocation: function() { return window.location; }
54 | });
55 |
56 | var catchingExceptions = queryString.getParam("catch");
57 | env.catchExceptions(typeof catchingExceptions === "undefined" ? true : catchingExceptions);
58 |
59 | /**
60 | * ## Reporters
61 | * The `HtmlReporter` builds all of the HTML UI for the runner page. This reporter paints the dots, stars, and x's for specs, as well as all spec names and all failures (if any).
62 | */
63 | var htmlReporter = new jasmine.HtmlReporter({
64 | env: env,
65 | onRaiseExceptionsClick: function() { queryString.navigateWithNewParam("catch", !env.catchingExceptions()); },
66 | addToExistingQueryString: function(key, value) { return queryString.fullStringWithNewParam(key, value); },
67 | getContainer: function() { return document.body; },
68 | createElement: function() { return document.createElement.apply(document, arguments); },
69 | createTextNode: function() { return document.createTextNode.apply(document, arguments); },
70 | timer: new jasmine.Timer()
71 | });
72 |
73 | /**
74 | * The `jsApiReporter` also receives spec results, and is used by any environment that needs to extract the results from JavaScript.
75 | */
76 | env.addReporter(jasmineInterface.jsApiReporter);
77 | env.addReporter(htmlReporter);
78 |
79 | /**
80 | * Filter which specs will be run by matching the start of the full name against the `spec` query param.
81 | */
82 | var specFilter = new jasmine.HtmlSpecFilter({
83 | filterString: function() { return queryString.getParam("spec"); }
84 | });
85 |
86 | env.specFilter = function(spec) {
87 | return specFilter.matches(spec.getFullName());
88 | };
89 |
90 | /**
91 | * Setting up timing functions to be able to be overridden. Certain browsers (Safari, IE 8, phantomjs) require this hack.
92 | */
93 | window.setTimeout = window.setTimeout;
94 | window.setInterval = window.setInterval;
95 | window.clearTimeout = window.clearTimeout;
96 | window.clearInterval = window.clearInterval;
97 |
98 | /**
99 | * ## Execution
100 | *
101 | * Replace the browser window's `onload`, ensure it's called, and then run all of the loaded specs. This includes initializing the `HtmlReporter` instance and then executing the loaded Jasmine environment. All of this will happen after all of the specs are loaded.
102 | */
103 | var currentWindowOnload = window.onload;
104 |
105 | window.onload = function() {
106 | if (currentWindowOnload) {
107 | currentWindowOnload();
108 | }
109 | htmlReporter.initialize();
110 | env.execute();
111 | };
112 |
113 | /**
114 | * Helper function for readability above.
115 | */
116 | function extend(destination, source) {
117 | for (var property in source) destination[property] = source[property];
118 | return destination;
119 | }
120 |
121 | }());
122 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | // --------------------------------------------------------------------
3 | // JSHint Configuration, Strict Edition
4 | // --------------------------------------------------------------------
5 | //
6 | // This is a options template for [JSHint][1], using [JSHint example][2]
7 | // and [Ory Band's example][3] as basis and setting config values to
8 | // be most strict:
9 | //
10 | // * set all enforcing options to true
11 | // * set all relaxing options to false
12 | // * set all environment options to false, except the browser value
13 | // * set all JSLint legacy options to false
14 | //
15 | // [1]: http://www.jshint.com/
16 | // [2]: https://github.com/jshint/node-jshint/blob/master/example/config.json
17 | // [3]: https://github.com/oryband/dotfiles/blob/master/jshintrc
18 | //
19 | // @author http://michael.haschke.biz/
20 | // @license http://unlicense.org/
21 |
22 | // == Enforcing Options ===============================================
23 | //
24 | // These options tell JSHint to be more strict towards your code. Use
25 | // them if you want to allow only a safe subset of JavaScript, very
26 | // useful when your codebase is shared with a big number of developers
27 | // with different skill levels.
28 | "bitwise" : true, // Prohibit bitwise operators (&, |, ^, etc.).
29 | "curly" : true, // Require {} for every new block or scope.
30 | "eqeqeq" : true, // Require triple equals i.e. `===`.
31 | "forin" : true, // Tolerate `for in` loops without `hasOwnPrototype`.
32 | "immed" : true, // Require immediate invocations to be wrapped in parens e.g. `( function(){}() );`
33 | "latedef" : true, // Prohibit variable use before definition.
34 | "newcap" : true, // Require capitalization of all constructor functions e.g. `new F()`.
35 | "noarg" : true, // Prohibit use of `arguments.caller` and `arguments.callee`.
36 | "noempty" : true, // Prohibit use of empty blocks.
37 | "nonew" : false, // Prohibit use of constructors for side-effects.
38 | "plusplus" : true, // Prohibit use of `++` & `--`.
39 | "regexp" : true, // Prohibit `.` and `[^...]` in regular expressions.
40 | "undef" : true, // Require all non-global variables be declared before they are used.
41 | "strict" : false, // Require `use strict` pragma in every file.
42 | "trailing" : true, // Prohibit trailing whitespaces.
43 |
44 | // == Relaxing Options ================================================
45 | //
46 | // These options allow you to suppress certain types of warnings. Use
47 | // them only if you are absolutely positive that you know what you are
48 | // doing.
49 |
50 | "asi" : false, // Tolerate Automatic Semicolon Insertion (no semicolons).
51 | "boss" : false, // Tolerate assignments inside if, for & while. Usually conditions & loops are for comparison, not assignments.
52 | "debug" : false, // Allow debugger statements e.g. browser breakpoints.
53 | "eqnull" : false, // Tolerate use of `== null`.
54 | "es6" : true, // Allow EcmaScript 5 syntax.
55 | "esnext" : true, // Allow ES.next specific features such as `const` and `let`.
56 | "evil" : false, // Tolerate use of `eval`.
57 | "expr" : false, // Tolerate `ExpressionStatement` as Programs.
58 | "funcscope" : false, // Tolerate declarations of variables inside of control structures while accessing them later from the outside.
59 | "globalstrict" : false, // Allow global "use strict" (also enables 'strict').
60 | "iterator" : false, // Allow usage of __iterator__ property.
61 | "lastsemic" : false, // Tolerat missing semicolons when the it is omitted for the last statement in a one-line block.
62 | "laxbreak" : false, // Tolerate unsafe line breaks e.g. `return [\n] x` without semicolons.
63 | "laxcomma" : false, // Suppress warnings about comma-first coding style.
64 | "loopfunc" : false, // Allow functions to be defined within loops.
65 | "multistr" : false, // Tolerate multi-line strings.
66 | "onecase" : false, // Tolerate switches with just one case.
67 | "proto" : false, // Tolerate __proto__ property. This property is deprecated.
68 | "regexdash" : false, // Tolerate unescaped last dash i.e. `[-...]`.
69 | "scripturl" : true, // Tolerate script-targeted URLs.
70 | "smarttabs" : false, // Tolerate mixed tabs and spaces when the latter are used for alignmnent only.
71 | "shadow" : false, // Allows re-define variables later in code e.g. `var x=1; x=2;`.
72 | "sub" : true, // Tolerate all forms of subscript notation besides dot notation e.g. `dict['key']` instead of `dict.key`.
73 | "supernew" : false, // Tolerate `new function () { ... };` and `new Object;`.
74 | "validthis" : false, // Tolerate strict violations when the code is running in strict mode and you use this in a non-constructor function.
75 |
76 | // == Environments ====================================================
77 | //
78 | // These options pre-define global variables that are exposed by
79 | // popular JavaScript libraries and runtime environments—such as
80 | // browser or node.js.
81 |
82 | "browser" : true, // Standard browser globals e.g. `window`, `document`.
83 | "couch" : false, // Enable globals exposed by CouchDB.
84 | "devel" : false, // Allow development statements e.g. `console.log();`.
85 | "dojo" : false, // Enable globals exposed by Dojo Toolkit.
86 | "jquery" : true, // Enable globals exposed by jQuery JavaScript library.
87 | "mootools" : false, // Enable globals exposed by MooTools JavaScript framework.
88 | "node" : true, // Enable globals available when code is running inside of the NodeJS runtime environment.
89 | "nonstandard" : false, // Define non-standard but widely adopted globals such as escape and unescape.
90 | "prototypejs" : false, // Enable globals exposed by Prototype JavaScript framework.
91 | "rhino" : false, // Enable globals available when your code is running inside of the Rhino runtime environment.
92 | "wsh" : false, // Enable globals available when your code is running as a script for the Windows Script Host.
93 |
94 | // == Formatting and Complexity =======================================
95 | //
96 | // Indentation and max length work to naturally keep files small,
97 | // editable, and with limited complexity. The other options are a
98 | // strict way to enforce these.
99 |
100 | "maxlen" : 80, // Maximum line length, helps reduce cyclomatic complexity
101 | "indent" : 2, // Specify indentation spacing
102 | "maxparams" : 10, // Maximum number of formal parameters allowed per function
103 | "maxdepth" : 4, // Maximum nested statement depth for a function
104 | "maxstatements" : 33, // Maximum number of statements allowed per function
105 | "maxcomplexity" : 15, // Maximum cyclomatic complexity of a function
106 | // http://en.wikipedia.org/wiki/Cyclomatic_complexity
107 |
108 | // == JSLint Legacy ===================================================
109 | //
110 | // These options are legacy from JSLint. Aside from bug fixes they will
111 | // not be improved in any way and might be removed at any point.
112 |
113 | "nomen" : false, // Prohibit use of initial or trailing underbars in names.
114 | "onevar" : true, // Allow only one `var` statement per function.
115 | "passfail" : false, // Stop on first error.
116 | "white" : true, // Check against strict whitespace and indentation rules.
117 |
118 | // == Undocumented Options ============================================
119 | //
120 | // While I've found these options in [example1][2] and [example2][3]
121 | // they are not described in the [JSHint Options documentation][4].
122 | //
123 | // [4]: http://www.jshint.com/options/
124 |
125 | "maxerr" : 100, // Maximum errors before stopping.
126 | "predef" : [ // Extra globals.
127 | "require",
128 | "define",
129 | "escape"
130 | ],
131 | "globals":[
132 | // Jasmine Globals
133 | "describe",
134 | "it",
135 | "expect",
136 | // Node Globals
137 | "require",
138 | "process",
139 | "module",
140 | "exports",
141 | "app.quit()",
142 | "Materialize"
143 | ]
144 | }
--------------------------------------------------------------------------------
/app/app.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Agroups.io
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
30 |
31 |
102 |
103 |
126 |
127 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
--------------------------------------------------------------------------------
/app/lib/jasmine/jasmine-html.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2008-2015 Pivotal Labs
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining
5 | a copy of this software and associated documentation files (the
6 | "Software"), to deal in the Software without restriction, including
7 | without limitation the rights to use, copy, modify, merge, publish,
8 | distribute, sublicense, and/or sell copies of the Software, and to
9 | permit persons to whom the Software is furnished to do so, subject to
10 | the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be
13 | included in all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 | */
23 | jasmineRequire.html = function(j$) {
24 | j$.ResultsNode = jasmineRequire.ResultsNode();
25 | j$.HtmlReporter = jasmineRequire.HtmlReporter(j$);
26 | j$.QueryString = jasmineRequire.QueryString();
27 | j$.HtmlSpecFilter = jasmineRequire.HtmlSpecFilter();
28 | };
29 |
30 | jasmineRequire.HtmlReporter = function(j$) {
31 |
32 | var noopTimer = {
33 | start: function() {},
34 | elapsed: function() { return 0; }
35 | };
36 |
37 | function HtmlReporter(options) {
38 | var env = options.env || {},
39 | getContainer = options.getContainer,
40 | createElement = options.createElement,
41 | createTextNode = options.createTextNode,
42 | onRaiseExceptionsClick = options.onRaiseExceptionsClick || function() {},
43 | addToExistingQueryString = options.addToExistingQueryString || defaultQueryString,
44 | timer = options.timer || noopTimer,
45 | results = [],
46 | specsExecuted = 0,
47 | failureCount = 0,
48 | pendingSpecCount = 0,
49 | htmlReporterMain,
50 | symbols,
51 | failedSuites = [];
52 |
53 | this.initialize = function() {
54 | clearPrior();
55 | htmlReporterMain = createDom('div', {className: 'jasmine_html-reporter'},
56 | createDom('div', {className: 'banner'},
57 | createDom('a', {className: 'title', href: 'http://jasmine.github.io/', target: '_blank'}),
58 | createDom('span', {className: 'version'}, j$.version)
59 | ),
60 | createDom('ul', {className: 'symbol-summary'}),
61 | createDom('div', {className: 'alert'}),
62 | createDom('div', {className: 'results'},
63 | createDom('div', {className: 'failures'})
64 | )
65 | );
66 | getContainer().appendChild(htmlReporterMain);
67 |
68 | symbols = find('.symbol-summary');
69 | };
70 |
71 | var totalSpecsDefined;
72 | this.jasmineStarted = function(options) {
73 | totalSpecsDefined = options.totalSpecsDefined || 0;
74 | timer.start();
75 | };
76 |
77 | var summary = createDom('div', {className: 'summary'});
78 |
79 | var topResults = new j$.ResultsNode({}, '', null),
80 | currentParent = topResults;
81 |
82 | this.suiteStarted = function(result) {
83 | currentParent.addChild(result, 'suite');
84 | currentParent = currentParent.last();
85 | };
86 |
87 | this.suiteDone = function(result) {
88 | if (result.status == 'failed') {
89 | failedSuites.push(result);
90 | }
91 |
92 | if (currentParent == topResults) {
93 | return;
94 | }
95 |
96 | currentParent = currentParent.parent;
97 | };
98 |
99 | this.specStarted = function(result) {
100 | currentParent.addChild(result, 'spec');
101 | };
102 |
103 | var failures = [];
104 | this.specDone = function(result) {
105 | if(noExpectations(result) && typeof console !== 'undefined' && typeof console.error !== 'undefined') {
106 | console.error('Spec \'' + result.fullName + '\' has no expectations.');
107 | }
108 |
109 | if (result.status != 'disabled') {
110 | specsExecuted++;
111 | }
112 |
113 | symbols.appendChild(createDom('li', {
114 | className: noExpectations(result) ? 'empty' : result.status,
115 | id: 'spec_' + result.id,
116 | title: result.fullName
117 | }
118 | ));
119 |
120 | if (result.status == 'failed') {
121 | failureCount++;
122 |
123 | var failure =
124 | createDom('div', {className: 'spec-detail failed'},
125 | createDom('div', {className: 'description'},
126 | createDom('a', {title: result.fullName, href: specHref(result)}, result.fullName)
127 | ),
128 | createDom('div', {className: 'messages'})
129 | );
130 | var messages = failure.childNodes[1];
131 |
132 | for (var i = 0; i < result.failedExpectations.length; i++) {
133 | var expectation = result.failedExpectations[i];
134 | messages.appendChild(createDom('div', {className: 'result-message'}, expectation.message));
135 | messages.appendChild(createDom('div', {className: 'stack-trace'}, expectation.stack));
136 | }
137 |
138 | failures.push(failure);
139 | }
140 |
141 | if (result.status == 'pending') {
142 | pendingSpecCount++;
143 | }
144 | };
145 |
146 | this.jasmineDone = function() {
147 | var banner = find('.banner');
148 | banner.appendChild(createDom('span', {className: 'duration'}, 'finished in ' + timer.elapsed() / 1000 + 's'));
149 |
150 | var alert = find('.alert');
151 |
152 | alert.appendChild(createDom('span', { className: 'exceptions' },
153 | createDom('label', { className: 'label', 'for': 'raise-exceptions' }, 'raise exceptions'),
154 | createDom('input', {
155 | className: 'raise',
156 | id: 'raise-exceptions',
157 | type: 'checkbox'
158 | })
159 | ));
160 | var checkbox = find('#raise-exceptions');
161 |
162 | checkbox.checked = !env.catchingExceptions();
163 | checkbox.onclick = onRaiseExceptionsClick;
164 |
165 | if (specsExecuted < totalSpecsDefined) {
166 | var skippedMessage = 'Ran ' + specsExecuted + ' of ' + totalSpecsDefined + ' specs - run all';
167 | alert.appendChild(
168 | createDom('span', {className: 'bar skipped'},
169 | createDom('a', {href: '?', title: 'Run all specs'}, skippedMessage)
170 | )
171 | );
172 | }
173 | var statusBarMessage = '';
174 | var statusBarClassName = 'bar ';
175 |
176 | if (totalSpecsDefined > 0) {
177 | statusBarMessage += pluralize('spec', specsExecuted) + ', ' + pluralize('failure', failureCount);
178 | if (pendingSpecCount) { statusBarMessage += ', ' + pluralize('pending spec', pendingSpecCount); }
179 | statusBarClassName += (failureCount > 0) ? 'failed' : 'passed';
180 | } else {
181 | statusBarClassName += 'skipped';
182 | statusBarMessage += 'No specs found';
183 | }
184 |
185 | alert.appendChild(createDom('span', {className: statusBarClassName}, statusBarMessage));
186 |
187 | for(i = 0; i < failedSuites.length; i++) {
188 | var failedSuite = failedSuites[i];
189 | for(var j = 0; j < failedSuite.failedExpectations.length; j++) {
190 | var errorBarMessage = 'AfterAll ' + failedSuite.failedExpectations[j].message;
191 | var errorBarClassName = 'bar errored';
192 | alert.appendChild(createDom('span', {className: errorBarClassName}, errorBarMessage));
193 | }
194 | }
195 |
196 | var results = find('.results');
197 | results.appendChild(summary);
198 |
199 | summaryList(topResults, summary);
200 |
201 | function summaryList(resultsTree, domParent) {
202 | var specListNode;
203 | for (var i = 0; i < resultsTree.children.length; i++) {
204 | var resultNode = resultsTree.children[i];
205 | if (resultNode.type == 'suite') {
206 | var suiteListNode = createDom('ul', {className: 'suite', id: 'suite-' + resultNode.result.id},
207 | createDom('li', {className: 'suite-detail'},
208 | createDom('a', {href: specHref(resultNode.result)}, resultNode.result.description)
209 | )
210 | );
211 |
212 | summaryList(resultNode, suiteListNode);
213 | domParent.appendChild(suiteListNode);
214 | }
215 | if (resultNode.type == 'spec') {
216 | if (domParent.getAttribute('class') != 'specs') {
217 | specListNode = createDom('ul', {className: 'specs'});
218 | domParent.appendChild(specListNode);
219 | }
220 | var specDescription = resultNode.result.description;
221 | if(noExpectations(resultNode.result)) {
222 | specDescription = 'SPEC HAS NO EXPECTATIONS ' + specDescription;
223 | }
224 | if(resultNode.result.status === 'pending' && resultNode.result.pendingReason !== '') {
225 | specDescription = specDescription + ' PENDING WITH MESSAGE: ' + resultNode.result.pendingReason;
226 | }
227 | specListNode.appendChild(
228 | createDom('li', {
229 | className: resultNode.result.status,
230 | id: 'spec-' + resultNode.result.id
231 | },
232 | createDom('a', {href: specHref(resultNode.result)}, specDescription)
233 | )
234 | );
235 | }
236 | }
237 | }
238 |
239 | if (failures.length) {
240 | alert.appendChild(
241 | createDom('span', {className: 'menu bar spec-list'},
242 | createDom('span', {}, 'Spec List | '),
243 | createDom('a', {className: 'failures-menu', href: '#'}, 'Failures')));
244 | alert.appendChild(
245 | createDom('span', {className: 'menu bar failure-list'},
246 | createDom('a', {className: 'spec-list-menu', href: '#'}, 'Spec List'),
247 | createDom('span', {}, ' | Failures ')));
248 |
249 | find('.failures-menu').onclick = function() {
250 | setMenuModeTo('failure-list');
251 | };
252 | find('.spec-list-menu').onclick = function() {
253 | setMenuModeTo('spec-list');
254 | };
255 |
256 | setMenuModeTo('failure-list');
257 |
258 | var failureNode = find('.failures');
259 | for (var i = 0; i < failures.length; i++) {
260 | failureNode.appendChild(failures[i]);
261 | }
262 | }
263 | };
264 |
265 | return this;
266 |
267 | function find(selector) {
268 | return getContainer().querySelector('.jasmine_html-reporter ' + selector);
269 | }
270 |
271 | function clearPrior() {
272 | // return the reporter
273 | var oldReporter = find('');
274 |
275 | if(oldReporter) {
276 | getContainer().removeChild(oldReporter);
277 | }
278 | }
279 |
280 | function createDom(type, attrs, childrenVarArgs) {
281 | var el = createElement(type);
282 |
283 | for (var i = 2; i < arguments.length; i++) {
284 | var child = arguments[i];
285 |
286 | if (typeof child === 'string') {
287 | el.appendChild(createTextNode(child));
288 | } else {
289 | if (child) {
290 | el.appendChild(child);
291 | }
292 | }
293 | }
294 |
295 | for (var attr in attrs) {
296 | if (attr == 'className') {
297 | el[attr] = attrs[attr];
298 | } else {
299 | el.setAttribute(attr, attrs[attr]);
300 | }
301 | }
302 |
303 | return el;
304 | }
305 |
306 | function pluralize(singular, count) {
307 | var word = (count == 1 ? singular : singular + 's');
308 |
309 | return '' + count + ' ' + word;
310 | }
311 |
312 | function specHref(result) {
313 | return addToExistingQueryString('spec', result.fullName);
314 | }
315 |
316 | function defaultQueryString(key, value) {
317 | return '?' + key + '=' + value;
318 | }
319 |
320 | function setMenuModeTo(mode) {
321 | htmlReporterMain.setAttribute('class', 'jasmine_html-reporter ' + mode);
322 | }
323 |
324 | function noExpectations(result) {
325 | return (result.failedExpectations.length + result.passedExpectations.length) === 0 &&
326 | result.status === 'passed';
327 | }
328 | }
329 |
330 | return HtmlReporter;
331 | };
332 |
333 | jasmineRequire.HtmlSpecFilter = function() {
334 | function HtmlSpecFilter(options) {
335 | var filterString = options && options.filterString() && options.filterString().replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
336 | var filterPattern = new RegExp(filterString);
337 |
338 | this.matches = function(specName) {
339 | return filterPattern.test(specName);
340 | };
341 | }
342 |
343 | return HtmlSpecFilter;
344 | };
345 |
346 | jasmineRequire.ResultsNode = function() {
347 | function ResultsNode(result, type, parent) {
348 | this.result = result;
349 | this.type = type;
350 | this.parent = parent;
351 |
352 | this.children = [];
353 |
354 | this.addChild = function(result, type) {
355 | this.children.push(new ResultsNode(result, type, this));
356 | };
357 |
358 | this.last = function() {
359 | return this.children[this.children.length - 1];
360 | };
361 | }
362 |
363 | return ResultsNode;
364 | };
365 |
366 | jasmineRequire.QueryString = function() {
367 | function QueryString(options) {
368 |
369 | this.navigateWithNewParam = function(key, value) {
370 | options.getWindowLocation().search = this.fullStringWithNewParam(key, value);
371 | };
372 |
373 | this.fullStringWithNewParam = function(key, value) {
374 | var paramMap = queryStringToParamMap();
375 | paramMap[key] = value;
376 | return toQueryString(paramMap);
377 | };
378 |
379 | this.getParam = function(key) {
380 | return queryStringToParamMap()[key];
381 | };
382 |
383 | return this;
384 |
385 | function toQueryString(paramMap) {
386 | var qStrPairs = [];
387 | for (var prop in paramMap) {
388 | qStrPairs.push(encodeURIComponent(prop) + '=' + encodeURIComponent(paramMap[prop]));
389 | }
390 | return '?' + qStrPairs.join('&');
391 | }
392 |
393 | function queryStringToParamMap() {
394 | var paramStr = options.getWindowLocation().search.substring(1),
395 | params = [],
396 | paramMap = {};
397 |
398 | if (paramStr.length > 0) {
399 | params = paramStr.split('&');
400 | for (var i = 0; i < params.length; i++) {
401 | var p = params[i].split('=');
402 | var value = decodeURIComponent(p[1]);
403 | if (value === 'true' || value === 'false') {
404 | value = JSON.parse(value);
405 | }
406 | paramMap[decodeURIComponent(p[0])] = value;
407 | }
408 | }
409 |
410 | return paramMap;
411 | }
412 |
413 | }
414 |
415 | return QueryString;
416 | };
417 |
--------------------------------------------------------------------------------
/app/lib/jasmine/jasmine.css:
--------------------------------------------------------------------------------
1 | body { overflow-y: scroll; }
2 |
3 | .jasmine_html-reporter { background-color: #eee; padding: 5px; margin: -8px; font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333; }
4 | .jasmine_html-reporter a { text-decoration: none; }
5 | .jasmine_html-reporter a:hover { text-decoration: underline; }
6 | .jasmine_html-reporter p, .jasmine_html-reporter h1, .jasmine_html-reporter h2, .jasmine_html-reporter h3, .jasmine_html-reporter h4, .jasmine_html-reporter h5, .jasmine_html-reporter h6 { margin: 0; line-height: 14px; }
7 | .jasmine_html-reporter .banner, .jasmine_html-reporter .symbol-summary, .jasmine_html-reporter .summary, .jasmine_html-reporter .result-message, .jasmine_html-reporter .spec .description, .jasmine_html-reporter .spec-detail .description, .jasmine_html-reporter .alert .bar, .jasmine_html-reporter .stack-trace { padding-left: 9px; padding-right: 9px; }
8 | .jasmine_html-reporter .banner { position: relative; }
9 | .jasmine_html-reporter .banner .title { background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFoAAAAZCAMAAACGusnyAAACdlBMVEX/////AP+AgICqVaqAQICZM5mAVYCSSZKAQICOOY6ATYCLRouAQICJO4mSSYCIRIiPQICHPIeOR4CGQ4aMQICGPYaLRoCFQ4WKQICPPYWJRYCOQoSJQICNPoSIRICMQoSHQICHRICKQoOHQICKPoOJO4OJQYOMQICMQ4CIQYKLQICIPoKLQ4CKQICNPoKJQISMQ4KJQoSLQYKJQISLQ4KIQoSKQYKIQICIQISMQoSKQYKLQIOLQoOJQYGLQIOKQIOMQoGKQYOLQYGKQIOLQoGJQYOJQIOKQYGJQIOKQoGKQIGLQIKLQ4KKQoGLQYKJQIGKQYKJQIGKQIKJQoGKQYKLQIGKQYKLQIOJQoKKQoOJQYKKQIOJQoKKQoOKQIOLQoKKQYOLQYKJQIOKQoKKQYKKQoKJQYOKQYKLQIOKQoKLQYOKQYKLQIOJQoGKQYKJQYGJQoGKQYKLQoGLQYGKQoGJQYKKQYGJQIKKQoGJQYKLQIKKQYGLQYKKQYGKQYGKQYKJQYOKQoKJQYOKQYKLQYOLQYOKQYKLQYOKQoKKQYKKQYOKQYOJQYKKQYKLQYKKQIKKQoKKQYKKQYKKQoKJQIKKQYKLQYKKQYKKQIKKQYKKQYKKQYKKQIKKQYKJQYGLQYGKQYKKQYKKQYGKQIKKQYGKQYOJQoKKQYOLQYKKQYOKQoKKQYKKQoKKQYKKQYKJQYKLQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKJQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKLQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKmIDpEAAAA0XRSTlMAAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHyAiIyQlJycoKissLS4wMTQ1Njc4OTo7PDw+P0BCQ0RISUpLTE1OUFNUVVdYWFlaW15fYGFiY2ZnaGlqa2xtb3BxcnN0dnh5ent8fX5/gIGChIWIioyNjo+QkZOUlZaYmZqbnJ2eoKGio6WmqKmsra6vsLGztre4ubq7vL2+wMHDxMjJysvNzs/Q0dLU1tfY2dvc3t/g4eLj5ebn6Onq6+zt7u/w8vP09fb3+Pn6+/z9/vkVQXAAAAMaSURBVHhe5dXxV1N1GMfxz2ABbDgIAm5VDJOyVDIJLUMaVpBWUZUaGbmqoGpZRSiGiRWp6KoZ5AB0ZY50RImZQIlahKkMYXv/R90dBvET/rJfOr3Ouc8v99zPec59zvf56j+vYKlViSf7250X4Mr3O29Tgq08BdGB4DhcekEJ5YkQKFsgWZdtj9JpV+I8xPjLFqkrsEIqO8PHSpis36jWazcqjEsfJjkvRssVU37SdIOu4XCf5vEJPsnwJpnRNU9JmxhMk8l1gehIrq7hTFjzOD+Vf88629qKMJVNltInFeRexRQyJlNeqd1iGDlSzrIUIyXbyFfm3RYprcQRe7lqtWyGYbfc6dT0R2vmdOOkX3u55C1rP37ftiH+tDby4r/RBT0w8TyEkr+epB9XgPDmSYYWbrhCuFYaIyw3fDQAXTnSkh+ANofiHmWf9l+FY1I90FdQTetstO00o23novzVsJ7uB3/C5TkbjRwZ5JerwV4iRWq9HFbFMaK/d0TYqayRiQPuIxxS3Bu8JWU90/60tKi7vkhaznez0a/TbVOKj5CaOZh6fWG6/Lyv9B/ZLR1gw/S/fpbeVD3MCW1li6SvWDOn65tr99/uvWtBS0XDm4s1t+sOHpG0kpBKx/l77wOSnxLpcx6TXmXLTPQOKYOf9Q1dfr8/SJ2mFdCvl1Yl93DiHUZvXeLJbGSzYu5gVJ2slbSakOR8dxCq5adQ2oFLqsE9Ex3L4qQO0eOPeU5x56bypXp4onSEb5OkICX6lDat55TeoztNKQcJaakrz9KCb95oD69IKq+yKW4XPjknaS52V0TZqE2cTtXjcHSCRmUO88e+85hj3EP74i9p8pylw7lxgMDyyl6OV7ZejnjNMfatu87LxRbH0IS35gt2a4ZjmGpVBdKK3Wr6INk8jWWSGqbA55CKgjBRC6E9w78ydTg3ABS3AFV1QN0Y4Aa2pgEjWnQURj9L0ayK6R2ysEqxHUKzYnLvvyU+i9KM2JHJzE4vyZOyDcOwOsySajeLPc8sNvPJkFlyJd20wpqAzZeAfZ3oWybxd+P/3j+SG3uSBdf2VQAAAABJRU5ErkJggg==') no-repeat; background: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIKICAgdmVyc2lvbj0iMS4xIgogICB3aWR0aD0iNjgxLjk2MjUyIgogICBoZWlnaHQ9IjE4Ny41IgogICBpZD0ic3ZnMiIKICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhOCI+PHJkZjpSREY+PGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPjxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PjxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz48L2NjOldvcms+PC9yZGY6UkRGPjwvbWV0YWRhdGE+PGRlZnMKICAgICBpZD0iZGVmczYiPjxjbGlwUGF0aAogICAgICAgaWQ9ImNsaXBQYXRoMTgiPjxwYXRoCiAgICAgICAgIGQ9Ik0gMCwxNTAwIDAsMCBsIDU0NTUuNzQsMCAwLDE1MDAgTCAwLDE1MDAgeiIKICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgaWQ9InBhdGgyMCIgLz48L2NsaXBQYXRoPjwvZGVmcz48ZwogICAgIHRyYW5zZm9ybT0ibWF0cml4KDEuMjUsMCwwLC0xLjI1LDAsMTg3LjUpIgogICAgIGlkPSJnMTAiPjxnCiAgICAgICB0cmFuc2Zvcm09InNjYWxlKDAuMSwwLjEpIgogICAgICAgaWQ9ImcxMiI+PGcKICAgICAgICAgaWQ9ImcxNCI+PGcKICAgICAgICAgICBjbGlwLXBhdGg9InVybCgjY2xpcFBhdGgxOCkiCiAgICAgICAgICAgaWQ9ImcxNiI+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gMTU0NCw1OTkuNDM0IGMgMC45MiwtNDAuMzUyIDI1LjY4LC04MS42MDIgNzEuNTMsLTgxLjYwMiAyNy41MSwwIDQ3LjY4LDEyLjgzMiA2MS40NCwzNS43NTQgMTIuODMsMjIuOTMgMTIuODMsNTYuODUyIDEyLjgzLDgyLjUyNyBsIDAsMzI5LjE4NCAtNzEuNTIsMCAwLDEwNC41NDMgMjY2LjgzLDAgMCwtMTA0LjU0MyAtNzAuNiwwIDAsLTM0NC43NyBjIDAsLTU4LjY5MSAtMy42OCwtMTA0LjUzMSAtNDQuOTMsLTE1Mi4yMTggLTM2LjY4LC00Mi4xOCAtOTYuMjgsLTY2LjAyIC0xNTMuMTQsLTY2LjAyIC0xMTcuMzcsMCAtMjA3LjI0LDc3Ljk0MSAtMjAyLjY0LDE5Ny4xNDUgbCAxMzAuMiwwIgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMjIiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDIzMDEuNCw2NjIuNjk1IGMgMCw4MC43MDMgLTY2Ljk0LDE0NS44MTMgLTE0Ny42MywxNDUuODEzIC04My40NCwwIC0xNDcuNjMsLTY4Ljc4MSAtMTQ3LjYzLC0xNTEuMzAxIDAsLTc5Ljc4NSA2Ni45NCwtMTQ1LjgwMSAxNDUuOCwtMTQ1LjgwMSA4NC4zNSwwIDE0OS40Niw2Ny44NTIgMTQ5LjQ2LDE1MS4yODkgeiBtIC0xLjgzLC0xODEuNTQ3IGMgLTM1Ljc3LC01NC4wOTcgLTkzLjUzLC03OC44NTkgLTE1Ny43MiwtNzguODU5IC0xNDAuMywwIC0yNTEuMjQsMTE2LjQ0OSAtMjUxLjI0LDI1NC45MTggMCwxNDIuMTI5IDExMy43LDI2MC40MSAyNTYuNzQsMjYwLjQxIDYzLjI3LDAgMTE4LjI5LC0yOS4zMzYgMTUyLjIyLC04Mi41MjMgbCAwLDY5LjY4NyAxNzUuMTQsMCAwLC0xMDQuNTI3IC02MS40NCwwIDAsLTI4MC41OTggNjEuNDQsMCAwLC0xMDQuNTI3IC0xNzUuMTQsMCAwLDY2LjAxOSIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDI0IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSAyNjIyLjMzLDU1Ny4yNTggYyAzLjY3LC00NC4wMTYgMzMuMDEsLTczLjM0OCA3OC44NiwtNzMuMzQ4IDMzLjkzLDAgNjYuOTMsMjMuODI0IDY2LjkzLDYwLjUwNCAwLDQ4LjYwNiAtNDUuODQsNTYuODU2IC04My40NCw2Ni45NDEgLTg1LjI4LDIyLjAwNCAtMTc4LjgxLDQ4LjYwNiAtMTc4LjgxLDE1NS44NzkgMCw5My41MzYgNzguODYsMTQ3LjYzMyAxNjUuOTgsMTQ3LjYzMyA0NCwwIDgzLjQzLC05LjE3NiAxMTAuOTQsLTQ0LjAwOCBsIDAsMzMuOTIyIDgyLjUzLDAgMCwtMTMyLjk2NSAtMTA4LjIxLDAgYyAtMS44MywzNC44NTYgLTI4LjQyLDU3Ljc3NCAtNjMuMjYsNTcuNzc0IC0zMC4yNiwwIC02Mi4zNSwtMTcuNDIyIC02Mi4zNSwtNTEuMzQ4IDAsLTQ1Ljg0NyA0NC45MywtNTUuOTMgODAuNjksLTY0LjE4IDg4LjAyLC0yMC4xNzUgMTgyLjQ3LC00Ny42OTUgMTgyLjQ3LC0xNTcuNzM0IDAsLTk5LjAyNyAtODMuNDQsLTE1NC4wMzkgLTE3NS4xMywtMTU0LjAzOSAtNDkuNTMsMCAtOTQuNDYsMTUuNTgyIC0xMjYuNTUsNTMuMTggbCAwLC00MC4zNCAtODUuMjcsMCAwLDE0Mi4xMjkgMTE0LjYyLDAiCiAgICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICAgICAgaWQ9InBhdGgyNiIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiM4YTQxODI7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiIC8+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gMjk4OC4xOCw4MDAuMjU0IC02My4yNiwwIDAsMTA0LjUyNyAxNjUuMDUsMCAwLC03My4zNTUgYyAzMS4xOCw1MS4zNDcgNzguODYsODUuMjc3IDE0MS4yMSw4NS4yNzcgNjcuODUsMCAxMjQuNzEsLTQxLjI1OCAxNTIuMjEsLTEwMi42OTkgMjYuNiw2Mi4zNTEgOTIuNjIsMTAyLjY5OSAxNjAuNDcsMTAyLjY5OSA1My4xOSwwIDEwNS40NiwtMjIgMTQxLjIxLC02Mi4zNTEgMzguNTIsLTQ0LjkzOCAzOC41MiwtOTMuNTMyIDM4LjUyLC0xNDkuNDU3IGwgMCwtMTg1LjIzOSA2My4yNywwIDAsLTEwNC41MjcgLTIzOC40MiwwIDAsMTA0LjUyNyA2My4yOCwwIDAsMTU3LjcxNSBjIDAsMzIuMTAyIDAsNjAuNTI3IC0xNC42Nyw4OC45NTcgLTE4LjM0LDI2LjU4MiAtNDguNjEsNDAuMzQ0IC03OS43Nyw0MC4zNDQgLTMwLjI2LDAgLTYzLjI4LC0xMi44NDQgLTgyLjUzLC0zNi42NzIgLTIyLjkzLC0yOS4zNTUgLTIyLjkzLC01Ni44NjMgLTIyLjkzLC05Mi42MjkgbCAwLC0xNTcuNzE1IDYzLjI3LDAgMCwtMTA0LjUyNyAtMjM4LjQxLDAgMCwxMDQuNTI3IDYzLjI4LDAgMCwxNTAuMzgzIGMgMCwyOS4zNDggMCw2Ni4wMjMgLTE0LjY3LDkxLjY5OSAtMTUuNTksMjkuMzM2IC00Ny42OSw0NC45MzQgLTgwLjcsNDQuOTM0IC0zMS4xOCwwIC01Ny43NywtMTEuMDA4IC03Ny45NCwtMzUuNzc0IC0yNC43NywtMzAuMjUzIC0yNi42LC02Mi4zNDMgLTI2LjYsLTk5Ljk0MSBsIDAsLTE1MS4zMDEgNjMuMjcsMCAwLC0xMDQuNTI3IC0yMzguNCwwIDAsMTA0LjUyNyA2My4yNiwwIDAsMjgwLjU5OCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDI4IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSAzOTk4LjY2LDk1MS41NDcgLTExMS44NywwIDAsMTE4LjI5MyAxMTEuODcsMCAwLC0xMTguMjkzIHogbSAwLC00MzEuODkxIDYzLjI3LDAgMCwtMTA0LjUyNyAtMjM5LjMzLDAgMCwxMDQuNTI3IDY0LjE5LDAgMCwyODAuNTk4IC02My4yNywwIDAsMTA0LjUyNyAxNzUuMTQsMCAwLC0zODUuMTI1IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMzAiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDQxNTkuMTIsODAwLjI1NCAtNjMuMjcsMCAwLDEwNC41MjcgMTc1LjE0LDAgMCwtNjkuNjg3IGMgMjkuMzUsNTQuMTAxIDg0LjM2LDgwLjY5OSAxNDQuODcsODAuNjk5IDUzLjE5LDAgMTA1LjQ1LC0yMi4wMTYgMTQxLjIyLC02MC41MjcgNDAuMzQsLTQ0LjkzNCA0MS4yNiwtODguMDMyIDQxLjI2LC0xNDMuOTU3IGwgMCwtMTkxLjY1MyA2My4yNywwIDAsLTEwNC41MjcgLTIzOC40LDAgMCwxMDQuNTI3IDYzLjI2LDAgMCwxNTguNjM3IGMgMCwzMC4yNjIgMCw2MS40MzQgLTE5LjI2LDg4LjAzNSAtMjAuMTcsMjYuNTgyIC01My4xOCwzOS40MTQgLTg2LjE5LDM5LjQxNCAtMzMuOTMsMCAtNjguNzcsLTEzLjc1IC04OC45NCwtNDEuMjUgLTIxLjA5LC0yNy41IC0yMS4wOSwtNjkuNjg3IC0yMS4wOSwtMTAyLjcwNyBsIDAsLTE0Mi4xMjkgNjMuMjYsMCAwLC0xMDQuNTI3IC0yMzguNCwwIDAsMTA0LjUyNyA2My4yNywwIDAsMjgwLjU5OCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDMyIgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA1MDgyLjQ4LDcwMy45NjUgYyAtMTkuMjQsNzAuNjA1IC04MS42LDExNS41NDcgLTE1NC4wNCwxMTUuNTQ3IC02Ni4wNCwwIC0xMjkuMywtNTEuMzQ4IC0xNDMuMDUsLTExNS41NDcgbCAyOTcuMDksMCB6IG0gODUuMjcsLTE0NC44ODMgYyAtMzguNTEsLTkzLjUyMyAtMTI5LjI3LC0xNTYuNzkzIC0yMzEuMDUsLTE1Ni43OTMgLTE0My4wNywwIC0yNTcuNjgsMTExLjg3MSAtMjU3LjY4LDI1NS44MzYgMCwxNDQuODgzIDEwOS4xMiwyNjEuMzI4IDI1NC45MSwyNjEuMzI4IDY3Ljg3LDAgMTM1LjcyLC0zMC4yNTggMTgzLjM5LC03OC44NjMgNDguNjIsLTUxLjM0NCA2OC43OSwtMTEzLjY5NSA2OC43OSwtMTgzLjM4MyBsIC0zLjY3LC0zOS40MzQgLTM5Ni4xMywwIGMgMTQuNjcsLTY3Ljg2MyA3Ny4wMywtMTE3LjM2MyAxNDYuNzIsLTExNy4zNjMgNDguNTksMCA5MC43NiwxOC4zMjggMTE4LjI4LDU4LjY3MiBsIDExNi40NCwwIgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMzQiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDY5MC44OTUsODUwLjcwMyA5MC43NSwwIDIyLjU0MywzMS4wMzUgMCwyNDMuMTIyIC0xMzUuODI5LDAgMCwtMjQzLjE0MSAyMi41MzYsLTMxLjAxNiIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDM2IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA2MzIuMzk1LDc0Mi4yNTggMjguMDM5LDg2LjMwNCAtMjIuNTUxLDMxLjA0IC0yMzEuMjIzLDc1LjEyOCAtNDEuOTc2LC0xMjkuMTgzIDIzMS4yNTcsLTc1LjEzNyAzNi40NTQsMTEuODQ4IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMzgiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDcxNy40NDksNjUzLjEwNSAtNzMuNDEsNTMuMzYgLTM2LjQ4OCwtMTEuODc1IC0xNDIuOTAzLC0xOTYuNjkyIDEwOS44ODMsLTc5LjgyOCAxNDIuOTE4LDE5Ni43MDMgMCwzOC4zMzIiCiAgICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICAgICAgaWQ9InBhdGg0MCIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiM4YTQxODI7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiIC8+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gODI4LjUyLDcwNi40NjUgLTczLjQyNiwtNTMuMzQgMC4wMTEsLTM4LjM1OSBMIDg5OC4wMDQsNDE4LjA3IDEwMDcuOSw0OTcuODk4IDg2NC45NzMsNjk0LjYwOSA4MjguNTIsNzA2LjQ2NSIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDQyIgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA4MTIuMDg2LDgyOC41ODYgMjguMDU1LC04Ni4zMiAzNi40ODQsLTExLjgzNiAyMzEuMjI1LDc1LjExNyAtNDEuOTcsMTI5LjE4MyAtMjMxLjIzOSwtNzUuMTQgLTIyLjU1NSwtMzEuMDA0IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoNDQiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDczNi4zMDEsMTMzNS44OCBjIC0zMjMuMDQ3LDAgLTU4NS44NzUsLTI2Mi43OCAtNTg1Ljg3NSwtNTg1Ljc4MiAwLC0zMjMuMTE4IDI2Mi44MjgsLTU4NS45NzcgNTg1Ljg3NSwtNTg1Ljk3NyAzMjMuMDE5LDAgNTg1LjgwOSwyNjIuODU5IDU4NS44MDksNTg1Ljk3NyAwLDMyMy4wMDIgLTI2Mi43OSw1ODUuNzgyIC01ODUuODA5LDU4NS43ODIgbCAwLDAgeiBtIDAsLTExOC42MSBjIDI1Ny45NzIsMCA0NjcuMTg5LC0yMDkuMTMgNDY3LjE4OSwtNDY3LjE3MiAwLC0yNTguMTI5IC0yMDkuMjE3LC00NjcuMzQ4IC00NjcuMTg5LC00NjcuMzQ4IC0yNTguMDc0LDAgLTQ2Ny4yNTQsMjA5LjIxOSAtNDY3LjI1NCw0NjcuMzQ4IDAsMjU4LjA0MiAyMDkuMTgsNDY3LjE3MiA0NjcuMjU0LDQ2Ny4xNzIiCiAgICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICAgICAgaWQ9InBhdGg0NiIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiM4YTQxODI7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiIC8+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gMTA5MS4xMyw2MTkuODgzIC0xNzUuNzcxLDU3LjEyMSAxMS42MjksMzUuODA4IDE3NS43NjIsLTU3LjEyMSAtMTEuNjIsLTM1LjgwOCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDQ4IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0iTSA4NjYuOTU3LDkwMi4wNzQgODM2LjUsOTI0LjE5OSA5NDUuMTIxLDEwNzMuNzMgOTc1LjU4NiwxMDUxLjYxIDg2Ni45NTcsOTAyLjA3NCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDUwIgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0iTSA2MDcuNDY1LDkwMy40NDUgNDk4Ljg1NSwxMDUyLjk3IDUyOS4zMiwxMDc1LjEgNjM3LjkzLDkyNS41NjYgNjA3LjQ2NSw5MDMuNDQ1IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoNTIiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDM4MC42ODgsNjIyLjEyOSAtMTEuNjI2LDM1LjgwMSAxNzUuNzU4LDU3LjA5IDExLjYyMSwtMzUuODAxIC0xNzUuNzUzLC01Ny4wOSIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDU0IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA3MTYuMjg5LDM3Ni41OSAzNy42NDA2LDAgMCwxODQuODE2IC0zNy42NDA2LDAgMCwtMTg0LjgxNiB6IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoNTYiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjwvZz48L2c+PC9nPjwvZz48L3N2Zz4=') no-repeat, none; -moz-background-size: 100%; -o-background-size: 100%; -webkit-background-size: 100%; background-size: 100%; display: block; float: left; width: 90px; height: 25px; }
10 | .jasmine_html-reporter .banner .version { margin-left: 14px; position: relative; top: 6px; }
11 | .jasmine_html-reporter .banner .duration { position: absolute; right: 14px; top: 6px; }
12 | .jasmine_html-reporter #jasmine_content { position: fixed; right: 100%; }
13 | .jasmine_html-reporter .version { color: #aaa; }
14 | .jasmine_html-reporter .banner { margin-top: 14px; }
15 | .jasmine_html-reporter .duration { color: #aaa; float: right; }
16 | .jasmine_html-reporter .symbol-summary { overflow: hidden; *zoom: 1; margin: 14px 0; }
17 | .jasmine_html-reporter .symbol-summary li { display: inline-block; height: 8px; width: 14px; font-size: 16px; }
18 | .jasmine_html-reporter .symbol-summary li.passed { font-size: 14px; }
19 | .jasmine_html-reporter .symbol-summary li.passed:before { color: #007069; content: "\02022"; }
20 | .jasmine_html-reporter .symbol-summary li.failed { line-height: 9px; }
21 | .jasmine_html-reporter .symbol-summary li.failed:before { color: #ca3a11; content: "\d7"; font-weight: bold; margin-left: -1px; }
22 | .jasmine_html-reporter .symbol-summary li.disabled { font-size: 14px; }
23 | .jasmine_html-reporter .symbol-summary li.disabled:before { color: #bababa; content: "\02022"; }
24 | .jasmine_html-reporter .symbol-summary li.pending { line-height: 17px; }
25 | .jasmine_html-reporter .symbol-summary li.pending:before { color: #ba9d37; content: "*"; }
26 | .jasmine_html-reporter .symbol-summary li.empty { font-size: 14px; }
27 | .jasmine_html-reporter .symbol-summary li.empty:before { color: #ba9d37; content: "\02022"; }
28 | .jasmine_html-reporter .exceptions { color: #fff; float: right; margin-top: 5px; margin-right: 5px; }
29 | .jasmine_html-reporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; }
30 | .jasmine_html-reporter .bar.failed { background-color: #ca3a11; }
31 | .jasmine_html-reporter .bar.passed { background-color: #007069; }
32 | .jasmine_html-reporter .bar.skipped { background-color: #bababa; }
33 | .jasmine_html-reporter .bar.errored { background-color: #ca3a11; }
34 | .jasmine_html-reporter .bar.menu { background-color: #fff; color: #aaa; }
35 | .jasmine_html-reporter .bar.menu a { color: #333; }
36 | .jasmine_html-reporter .bar a { color: white; }
37 | .jasmine_html-reporter.spec-list .bar.menu.failure-list, .jasmine_html-reporter.spec-list .results .failures { display: none; }
38 | .jasmine_html-reporter.failure-list .bar.menu.spec-list, .jasmine_html-reporter.failure-list .summary { display: none; }
39 | .jasmine_html-reporter .running-alert { background-color: #666; }
40 | .jasmine_html-reporter .results { margin-top: 14px; }
41 | .jasmine_html-reporter.showDetails .summaryMenuItem { font-weight: normal; text-decoration: inherit; }
42 | .jasmine_html-reporter.showDetails .summaryMenuItem:hover { text-decoration: underline; }
43 | .jasmine_html-reporter.showDetails .detailsMenuItem { font-weight: bold; text-decoration: underline; }
44 | .jasmine_html-reporter.showDetails .summary { display: none; }
45 | .jasmine_html-reporter.showDetails #details { display: block; }
46 | .jasmine_html-reporter .summaryMenuItem { font-weight: bold; text-decoration: underline; }
47 | .jasmine_html-reporter .summary { margin-top: 14px; }
48 | .jasmine_html-reporter .summary ul { list-style-type: none; margin-left: 14px; padding-top: 0; padding-left: 0; }
49 | .jasmine_html-reporter .summary ul.suite { margin-top: 7px; margin-bottom: 7px; }
50 | .jasmine_html-reporter .summary li.passed a { color: #007069; }
51 | .jasmine_html-reporter .summary li.failed a { color: #ca3a11; }
52 | .jasmine_html-reporter .summary li.empty a { color: #ba9d37; }
53 | .jasmine_html-reporter .summary li.pending a { color: #ba9d37; }
54 | .jasmine_html-reporter .description + .suite { margin-top: 0; }
55 | .jasmine_html-reporter .suite { margin-top: 14px; }
56 | .jasmine_html-reporter .suite a { color: #333; }
57 | .jasmine_html-reporter .failures .spec-detail { margin-bottom: 28px; }
58 | .jasmine_html-reporter .failures .spec-detail .description { background-color: #ca3a11; }
59 | .jasmine_html-reporter .failures .spec-detail .description a { color: white; }
60 | .jasmine_html-reporter .result-message { padding-top: 14px; color: #333; white-space: pre; }
61 | .jasmine_html-reporter .result-message span.result { display: block; }
62 | .jasmine_html-reporter .stack-trace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666; border: 1px solid #ddd; background: white; white-space: pre; }
63 |
--------------------------------------------------------------------------------
/app/lib/jasmine/jasmine.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2008-2015 Pivotal Labs
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining
5 | a copy of this software and associated documentation files (the
6 | "Software"), to deal in the Software without restriction, including
7 | without limitation the rights to use, copy, modify, merge, publish,
8 | distribute, sublicense, and/or sell copies of the Software, and to
9 | permit persons to whom the Software is furnished to do so, subject to
10 | the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be
13 | included in all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 | */
23 | var getJasmineRequireObj = (function (jasmineGlobal) {
24 | var jasmineRequire;
25 |
26 | if (typeof window !== 'undefined' && typeof window.toString === 'function' && window.toString() === '[object GjsGlobal]') {
27 | jasmineGlobal = window;
28 | }
29 | jasmineRequire = jasmineGlobal.jasmineRequire = jasmineGlobal.jasmineRequire || {};
30 |
31 | function getJasmineRequire() {
32 | return jasmineRequire;
33 | }
34 |
35 | getJasmineRequire().core = function(jRequire) {
36 | var j$ = {};
37 |
38 | jRequire.base(j$, jasmineGlobal);
39 | j$.util = jRequire.util();
40 | j$.Any = jRequire.Any();
41 | j$.Anything = jRequire.Anything(j$);
42 | j$.CallTracker = jRequire.CallTracker();
43 | j$.MockDate = jRequire.MockDate();
44 | j$.Clock = jRequire.Clock();
45 | j$.DelayedFunctionScheduler = jRequire.DelayedFunctionScheduler();
46 | j$.Env = jRequire.Env(j$);
47 | j$.ExceptionFormatter = jRequire.ExceptionFormatter();
48 | j$.Expectation = jRequire.Expectation();
49 | j$.buildExpectationResult = jRequire.buildExpectationResult();
50 | j$.JsApiReporter = jRequire.JsApiReporter();
51 | j$.matchersUtil = jRequire.matchersUtil(j$);
52 | j$.ObjectContaining = jRequire.ObjectContaining(j$);
53 | j$.ArrayContaining = jRequire.ArrayContaining(j$);
54 | j$.pp = jRequire.pp(j$);
55 | j$.QueueRunner = jRequire.QueueRunner(j$);
56 | j$.ReportDispatcher = jRequire.ReportDispatcher();
57 | j$.Spec = jRequire.Spec(j$);
58 | j$.SpyRegistry = jRequire.SpyRegistry(j$);
59 | j$.SpyStrategy = jRequire.SpyStrategy();
60 | j$.StringMatching = jRequire.StringMatching(j$);
61 | j$.Suite = jRequire.Suite();
62 | j$.Timer = jRequire.Timer();
63 | j$.version = jRequire.version();
64 |
65 | j$.matchers = jRequire.requireMatchers(jRequire, j$);
66 |
67 | return j$;
68 | };
69 |
70 | return getJasmineRequire;
71 | })(this);
72 |
73 | getJasmineRequireObj().requireMatchers = function(jRequire, j$) {
74 | var availableMatchers = [
75 | 'toBe',
76 | 'toBeCloseTo',
77 | 'toBeDefined',
78 | 'toBeFalsy',
79 | 'toBeGreaterThan',
80 | 'toBeLessThan',
81 | 'toBeNaN',
82 | 'toBeNull',
83 | 'toBeTruthy',
84 | 'toBeUndefined',
85 | 'toContain',
86 | 'toEqual',
87 | 'toHaveBeenCalled',
88 | 'toHaveBeenCalledWith',
89 | 'toMatch',
90 | 'toThrow',
91 | 'toThrowError'
92 | ],
93 | matchers = {};
94 |
95 | for (var i = 0; i < availableMatchers.length; i++) {
96 | var name = availableMatchers[i];
97 | matchers[name] = jRequire[name](j$);
98 | }
99 |
100 | return matchers;
101 | };
102 |
103 | getJasmineRequireObj().base = function(j$, jasmineGlobal) {
104 | j$.unimplementedMethod_ = function() {
105 | throw new Error('unimplemented method');
106 | };
107 |
108 | j$.MAX_PRETTY_PRINT_DEPTH = 40;
109 | j$.MAX_PRETTY_PRINT_ARRAY_LENGTH = 100;
110 | j$.DEFAULT_TIMEOUT_INTERVAL = 5000;
111 |
112 | j$.getGlobal = function() {
113 | return jasmineGlobal;
114 | };
115 |
116 | j$.getEnv = function(options) {
117 | var env = j$.currentEnv_ = j$.currentEnv_ || new j$.Env(options);
118 | //jasmine. singletons in here (setTimeout blah blah).
119 | return env;
120 | };
121 |
122 | j$.isArray_ = function(value) {
123 | return j$.isA_('Array', value);
124 | };
125 |
126 | j$.isString_ = function(value) {
127 | return j$.isA_('String', value);
128 | };
129 |
130 | j$.isNumber_ = function(value) {
131 | return j$.isA_('Number', value);
132 | };
133 |
134 | j$.isA_ = function(typeName, value) {
135 | return Object.prototype.toString.apply(value) === '[object ' + typeName + ']';
136 | };
137 |
138 | j$.isDomNode = function(obj) {
139 | return obj.nodeType > 0;
140 | };
141 |
142 | j$.fnNameFor = function(func) {
143 | return func.name || func.toString().match(/^\s*function\s*(\w*)\s*\(/)[1];
144 | };
145 |
146 | j$.any = function(clazz) {
147 | return new j$.Any(clazz);
148 | };
149 |
150 | j$.anything = function() {
151 | return new j$.Anything();
152 | };
153 |
154 | j$.objectContaining = function(sample) {
155 | return new j$.ObjectContaining(sample);
156 | };
157 |
158 | j$.stringMatching = function(expected) {
159 | return new j$.StringMatching(expected);
160 | };
161 |
162 | j$.arrayContaining = function(sample) {
163 | return new j$.ArrayContaining(sample);
164 | };
165 |
166 | j$.createSpy = function(name, originalFn) {
167 |
168 | var spyStrategy = new j$.SpyStrategy({
169 | name: name,
170 | fn: originalFn,
171 | getSpy: function() { return spy; }
172 | }),
173 | callTracker = new j$.CallTracker(),
174 | spy = function() {
175 | var callData = {
176 | object: this,
177 | args: Array.prototype.slice.apply(arguments)
178 | };
179 |
180 | callTracker.track(callData);
181 | var returnValue = spyStrategy.exec.apply(this, arguments);
182 | callData.returnValue = returnValue;
183 |
184 | return returnValue;
185 | };
186 |
187 | for (var prop in originalFn) {
188 | if (prop === 'and' || prop === 'calls') {
189 | throw new Error('Jasmine spies would overwrite the \'and\' and \'calls\' properties on the object being spied upon');
190 | }
191 |
192 | spy[prop] = originalFn[prop];
193 | }
194 |
195 | spy.and = spyStrategy;
196 | spy.calls = callTracker;
197 |
198 | return spy;
199 | };
200 |
201 | j$.isSpy = function(putativeSpy) {
202 | if (!putativeSpy) {
203 | return false;
204 | }
205 | return putativeSpy.and instanceof j$.SpyStrategy &&
206 | putativeSpy.calls instanceof j$.CallTracker;
207 | };
208 |
209 | j$.createSpyObj = function(baseName, methodNames) {
210 | if (j$.isArray_(baseName) && j$.util.isUndefined(methodNames)) {
211 | methodNames = baseName;
212 | baseName = 'unknown';
213 | }
214 |
215 | if (!j$.isArray_(methodNames) || methodNames.length === 0) {
216 | throw 'createSpyObj requires a non-empty array of method names to create spies for';
217 | }
218 | var obj = {};
219 | for (var i = 0; i < methodNames.length; i++) {
220 | obj[methodNames[i]] = j$.createSpy(baseName + '.' + methodNames[i]);
221 | }
222 | return obj;
223 | };
224 | };
225 |
226 | getJasmineRequireObj().util = function() {
227 |
228 | var util = {};
229 |
230 | util.inherit = function(childClass, parentClass) {
231 | var Subclass = function() {
232 | };
233 | Subclass.prototype = parentClass.prototype;
234 | childClass.prototype = new Subclass();
235 | };
236 |
237 | util.htmlEscape = function(str) {
238 | if (!str) {
239 | return str;
240 | }
241 | return str.replace(/&/g, '&')
242 | .replace(//g, '>');
244 | };
245 |
246 | util.argsToArray = function(args) {
247 | var arrayOfArgs = [];
248 | for (var i = 0; i < args.length; i++) {
249 | arrayOfArgs.push(args[i]);
250 | }
251 | return arrayOfArgs;
252 | };
253 |
254 | util.isUndefined = function(obj) {
255 | return obj === void 0;
256 | };
257 |
258 | util.arrayContains = function(array, search) {
259 | var i = array.length;
260 | while (i--) {
261 | if (array[i] === search) {
262 | return true;
263 | }
264 | }
265 | return false;
266 | };
267 |
268 | util.clone = function(obj) {
269 | if (Object.prototype.toString.apply(obj) === '[object Array]') {
270 | return obj.slice();
271 | }
272 |
273 | var cloned = {};
274 | for (var prop in obj) {
275 | if (obj.hasOwnProperty(prop)) {
276 | cloned[prop] = obj[prop];
277 | }
278 | }
279 |
280 | return cloned;
281 | };
282 |
283 | return util;
284 | };
285 |
286 | getJasmineRequireObj().Spec = function(j$) {
287 | function Spec(attrs) {
288 | this.expectationFactory = attrs.expectationFactory;
289 | this.resultCallback = attrs.resultCallback || function() {};
290 | this.id = attrs.id;
291 | this.description = attrs.description || '';
292 | this.queueableFn = attrs.queueableFn;
293 | this.beforeAndAfterFns = attrs.beforeAndAfterFns || function() { return {befores: [], afters: []}; };
294 | this.userContext = attrs.userContext || function() { return {}; };
295 | this.onStart = attrs.onStart || function() {};
296 | this.getSpecName = attrs.getSpecName || function() { return ''; };
297 | this.expectationResultFactory = attrs.expectationResultFactory || function() { };
298 | this.queueRunnerFactory = attrs.queueRunnerFactory || function() {};
299 | this.catchingExceptions = attrs.catchingExceptions || function() { return true; };
300 |
301 | if (!this.queueableFn.fn) {
302 | this.pend();
303 | }
304 |
305 | this.result = {
306 | id: this.id,
307 | description: this.description,
308 | fullName: this.getFullName(),
309 | failedExpectations: [],
310 | passedExpectations: [],
311 | pendingReason: ''
312 | };
313 | }
314 |
315 | Spec.prototype.addExpectationResult = function(passed, data) {
316 | var expectationResult = this.expectationResultFactory(data);
317 | if (passed) {
318 | this.result.passedExpectations.push(expectationResult);
319 | } else {
320 | this.result.failedExpectations.push(expectationResult);
321 | }
322 | };
323 |
324 | Spec.prototype.expect = function(actual) {
325 | return this.expectationFactory(actual, this);
326 | };
327 |
328 | Spec.prototype.execute = function(onComplete) {
329 | var self = this;
330 |
331 | this.onStart(this);
332 |
333 | if (this.markedPending || this.disabled) {
334 | complete();
335 | return;
336 | }
337 |
338 | var fns = this.beforeAndAfterFns();
339 | var allFns = fns.befores.concat(this.queueableFn).concat(fns.afters);
340 |
341 | this.queueRunnerFactory({
342 | queueableFns: allFns,
343 | onException: function() { self.onException.apply(self, arguments); },
344 | onComplete: complete,
345 | userContext: this.userContext()
346 | });
347 |
348 | function complete() {
349 | self.result.status = self.status();
350 | self.resultCallback(self.result);
351 |
352 | if (onComplete) {
353 | onComplete();
354 | }
355 | }
356 | };
357 |
358 | Spec.prototype.onException = function onException(e) {
359 | if (Spec.isPendingSpecException(e)) {
360 | this.pend(extractCustomPendingMessage(e));
361 | return;
362 | }
363 |
364 | this.addExpectationResult(false, {
365 | matcherName: '',
366 | passed: false,
367 | expected: '',
368 | actual: '',
369 | error: e
370 | });
371 | };
372 |
373 | Spec.prototype.disable = function() {
374 | this.disabled = true;
375 | };
376 |
377 | Spec.prototype.pend = function(message) {
378 | this.markedPending = true;
379 | if (message) {
380 | this.result.pendingReason = message;
381 | }
382 | };
383 |
384 | Spec.prototype.status = function() {
385 | if (this.disabled) {
386 | return 'disabled';
387 | }
388 |
389 | if (this.markedPending) {
390 | return 'pending';
391 | }
392 |
393 | if (this.result.failedExpectations.length > 0) {
394 | return 'failed';
395 | } else {
396 | return 'passed';
397 | }
398 | };
399 |
400 | Spec.prototype.isExecutable = function() {
401 | return !this.disabled && !this.markedPending;
402 | };
403 |
404 | Spec.prototype.getFullName = function() {
405 | return this.getSpecName(this);
406 | };
407 |
408 | var extractCustomPendingMessage = function(e) {
409 | var fullMessage = e.toString(),
410 | boilerplateStart = fullMessage.indexOf(Spec.pendingSpecExceptionMessage),
411 | boilerplateEnd = boilerplateStart + Spec.pendingSpecExceptionMessage.length;
412 |
413 | return fullMessage.substr(boilerplateEnd);
414 | };
415 |
416 | Spec.pendingSpecExceptionMessage = '=> marked Pending';
417 |
418 | Spec.isPendingSpecException = function(e) {
419 | return !!(e && e.toString && e.toString().indexOf(Spec.pendingSpecExceptionMessage) !== -1);
420 | };
421 |
422 | return Spec;
423 | };
424 |
425 | if (typeof window == void 0 && typeof exports == 'object') {
426 | exports.Spec = jasmineRequire.Spec;
427 | }
428 |
429 | getJasmineRequireObj().Env = function(j$) {
430 | function Env(options) {
431 | options = options || {};
432 |
433 | var self = this;
434 | var global = options.global || j$.getGlobal();
435 |
436 | var totalSpecsDefined = 0;
437 |
438 | var catchExceptions = true;
439 |
440 | var realSetTimeout = j$.getGlobal().setTimeout;
441 | var realClearTimeout = j$.getGlobal().clearTimeout;
442 | this.clock = new j$.Clock(global, new j$.DelayedFunctionScheduler(), new j$.MockDate(global));
443 |
444 | var runnableLookupTable = {};
445 | var runnableResources = {};
446 |
447 | var currentSpec = null;
448 | var currentlyExecutingSuites = [];
449 | var currentDeclarationSuite = null;
450 |
451 | var currentSuite = function() {
452 | return currentlyExecutingSuites[currentlyExecutingSuites.length - 1];
453 | };
454 |
455 | var currentRunnable = function() {
456 | return currentSpec || currentSuite();
457 | };
458 |
459 | var reporter = new j$.ReportDispatcher([
460 | 'jasmineStarted',
461 | 'jasmineDone',
462 | 'suiteStarted',
463 | 'suiteDone',
464 | 'specStarted',
465 | 'specDone'
466 | ]);
467 |
468 | this.specFilter = function() {
469 | return true;
470 | };
471 |
472 | this.addCustomEqualityTester = function(tester) {
473 | if(!currentRunnable()) {
474 | throw new Error('Custom Equalities must be added in a before function or a spec');
475 | }
476 | runnableResources[currentRunnable().id].customEqualityTesters.push(tester);
477 | };
478 |
479 | this.addMatchers = function(matchersToAdd) {
480 | if(!currentRunnable()) {
481 | throw new Error('Matchers must be added in a before function or a spec');
482 | }
483 | var customMatchers = runnableResources[currentRunnable().id].customMatchers;
484 | for (var matcherName in matchersToAdd) {
485 | customMatchers[matcherName] = matchersToAdd[matcherName];
486 | }
487 | };
488 |
489 | j$.Expectation.addCoreMatchers(j$.matchers);
490 |
491 | var nextSpecId = 0;
492 | var getNextSpecId = function() {
493 | return 'spec' + nextSpecId++;
494 | };
495 |
496 | var nextSuiteId = 0;
497 | var getNextSuiteId = function() {
498 | return 'suite' + nextSuiteId++;
499 | };
500 |
501 | var expectationFactory = function(actual, spec) {
502 | return j$.Expectation.Factory({
503 | util: j$.matchersUtil,
504 | customEqualityTesters: runnableResources[spec.id].customEqualityTesters,
505 | customMatchers: runnableResources[spec.id].customMatchers,
506 | actual: actual,
507 | addExpectationResult: addExpectationResult
508 | });
509 |
510 | function addExpectationResult(passed, result) {
511 | return spec.addExpectationResult(passed, result);
512 | }
513 | };
514 |
515 | var defaultResourcesForRunnable = function(id, parentRunnableId) {
516 | var resources = {spies: [], customEqualityTesters: [], customMatchers: {}};
517 |
518 | if(runnableResources[parentRunnableId]){
519 | resources.customEqualityTesters = j$.util.clone(runnableResources[parentRunnableId].customEqualityTesters);
520 | resources.customMatchers = j$.util.clone(runnableResources[parentRunnableId].customMatchers);
521 | }
522 |
523 | runnableResources[id] = resources;
524 | };
525 |
526 | var clearResourcesForRunnable = function(id) {
527 | spyRegistry.clearSpies();
528 | delete runnableResources[id];
529 | };
530 |
531 | var beforeAndAfterFns = function(suite, runnablesExplictlySet) {
532 | return function() {
533 | var befores = [],
534 | afters = [],
535 | beforeAlls = [],
536 | afterAlls = [];
537 |
538 | while(suite) {
539 | befores = befores.concat(suite.beforeFns);
540 | afters = afters.concat(suite.afterFns);
541 |
542 | if (runnablesExplictlySet()) {
543 | beforeAlls = beforeAlls.concat(suite.beforeAllFns);
544 | afterAlls = afterAlls.concat(suite.afterAllFns);
545 | }
546 |
547 | suite = suite.parentSuite;
548 | }
549 | return {
550 | befores: beforeAlls.reverse().concat(befores.reverse()),
551 | afters: afters.concat(afterAlls)
552 | };
553 | };
554 | };
555 |
556 | var getSpecName = function(spec, suite) {
557 | return suite.getFullName() + ' ' + spec.description;
558 | };
559 |
560 | // TODO: we may just be able to pass in the fn instead of wrapping here
561 | var buildExpectationResult = j$.buildExpectationResult,
562 | exceptionFormatter = new j$.ExceptionFormatter(),
563 | expectationResultFactory = function(attrs) {
564 | attrs.messageFormatter = exceptionFormatter.message;
565 | attrs.stackFormatter = exceptionFormatter.stack;
566 |
567 | return buildExpectationResult(attrs);
568 | };
569 |
570 | // TODO: fix this naming, and here's where the value comes in
571 | this.catchExceptions = function(value) {
572 | catchExceptions = !!value;
573 | return catchExceptions;
574 | };
575 |
576 | this.catchingExceptions = function() {
577 | return catchExceptions;
578 | };
579 |
580 | var maximumSpecCallbackDepth = 20;
581 | var currentSpecCallbackDepth = 0;
582 |
583 | function clearStack(fn) {
584 | currentSpecCallbackDepth++;
585 | if (currentSpecCallbackDepth >= maximumSpecCallbackDepth) {
586 | currentSpecCallbackDepth = 0;
587 | realSetTimeout(fn, 0);
588 | } else {
589 | fn();
590 | }
591 | }
592 |
593 | var catchException = function(e) {
594 | return j$.Spec.isPendingSpecException(e) || catchExceptions;
595 | };
596 |
597 | var queueRunnerFactory = function(options) {
598 | options.catchException = catchException;
599 | options.clearStack = options.clearStack || clearStack;
600 | options.timer = {setTimeout: realSetTimeout, clearTimeout: realClearTimeout};
601 | options.fail = self.fail;
602 |
603 | new j$.QueueRunner(options).execute();
604 | };
605 |
606 | var topSuite = new j$.Suite({
607 | env: this,
608 | id: getNextSuiteId(),
609 | description: 'Jasmine__TopLevel__Suite',
610 | queueRunner: queueRunnerFactory
611 | });
612 | runnableLookupTable[topSuite.id] = topSuite;
613 | defaultResourcesForRunnable(topSuite.id);
614 | currentDeclarationSuite = topSuite;
615 |
616 | this.topSuite = function() {
617 | return topSuite;
618 | };
619 |
620 | this.execute = function(runnablesToRun) {
621 | if(runnablesToRun) {
622 | runnablesExplictlySet = true;
623 | } else if (focusedRunnables.length) {
624 | runnablesExplictlySet = true;
625 | runnablesToRun = focusedRunnables;
626 | } else {
627 | runnablesToRun = [topSuite.id];
628 | }
629 |
630 | var allFns = [];
631 | for(var i = 0; i < runnablesToRun.length; i++) {
632 | var runnable = runnableLookupTable[runnablesToRun[i]];
633 | allFns.push((function(runnable) { return { fn: function(done) { runnable.execute(done); } }; })(runnable));
634 | }
635 |
636 | reporter.jasmineStarted({
637 | totalSpecsDefined: totalSpecsDefined
638 | });
639 |
640 | queueRunnerFactory({queueableFns: allFns, onComplete: reporter.jasmineDone});
641 | };
642 |
643 | this.addReporter = function(reporterToAdd) {
644 | reporter.addReporter(reporterToAdd);
645 | };
646 |
647 | var spyRegistry = new j$.SpyRegistry({currentSpies: function() {
648 | if(!currentRunnable()) {
649 | throw new Error('Spies must be created in a before function or a spec');
650 | }
651 | return runnableResources[currentRunnable().id].spies;
652 | }});
653 |
654 | this.spyOn = function() {
655 | return spyRegistry.spyOn.apply(spyRegistry, arguments);
656 | };
657 |
658 | var suiteFactory = function(description) {
659 | var suite = new j$.Suite({
660 | env: self,
661 | id: getNextSuiteId(),
662 | description: description,
663 | parentSuite: currentDeclarationSuite,
664 | queueRunner: queueRunnerFactory,
665 | onStart: suiteStarted,
666 | expectationFactory: expectationFactory,
667 | expectationResultFactory: expectationResultFactory,
668 | runnablesExplictlySetGetter: runnablesExplictlySetGetter,
669 | resultCallback: function(attrs) {
670 | if (!suite.disabled) {
671 | clearResourcesForRunnable(suite.id);
672 | }
673 | currentlyExecutingSuites.pop();
674 | reporter.suiteDone(attrs);
675 | }
676 | });
677 |
678 | runnableLookupTable[suite.id] = suite;
679 | return suite;
680 |
681 | function suiteStarted(suite) {
682 | currentlyExecutingSuites.push(suite);
683 | defaultResourcesForRunnable(suite.id, suite.parentSuite.id);
684 | reporter.suiteStarted(suite.result);
685 | }
686 | };
687 |
688 | this.describe = function(description, specDefinitions) {
689 | var suite = suiteFactory(description);
690 | addSpecsToSuite(suite, specDefinitions);
691 | return suite;
692 | };
693 |
694 | this.xdescribe = function(description, specDefinitions) {
695 | var suite = this.describe(description, specDefinitions);
696 | suite.disable();
697 | return suite;
698 | };
699 |
700 | var focusedRunnables = [];
701 |
702 | this.fdescribe = function(description, specDefinitions) {
703 | var suite = suiteFactory(description);
704 | suite.isFocused = true;
705 |
706 | focusedRunnables.push(suite.id);
707 | unfocusAncestor();
708 | addSpecsToSuite(suite, specDefinitions);
709 |
710 | return suite;
711 | };
712 |
713 | function addSpecsToSuite(suite, specDefinitions) {
714 | var parentSuite = currentDeclarationSuite;
715 | parentSuite.addChild(suite);
716 | currentDeclarationSuite = suite;
717 |
718 | var declarationError = null;
719 | try {
720 | specDefinitions.call(suite);
721 | } catch (e) {
722 | declarationError = e;
723 | }
724 |
725 | if (declarationError) {
726 | self.it('encountered a declaration exception', function() {
727 | throw declarationError;
728 | });
729 | }
730 |
731 | currentDeclarationSuite = parentSuite;
732 | }
733 |
734 | function findFocusedAncestor(suite) {
735 | while (suite) {
736 | if (suite.isFocused) {
737 | return suite.id;
738 | }
739 | suite = suite.parentSuite;
740 | }
741 |
742 | return null;
743 | }
744 |
745 | function unfocusAncestor() {
746 | var focusedAncestor = findFocusedAncestor(currentDeclarationSuite);
747 | if (focusedAncestor) {
748 | for (var i = 0; i < focusedRunnables.length; i++) {
749 | if (focusedRunnables[i] === focusedAncestor) {
750 | focusedRunnables.splice(i, 1);
751 | break;
752 | }
753 | }
754 | }
755 | }
756 |
757 | var runnablesExplictlySet = false;
758 |
759 | var runnablesExplictlySetGetter = function(){
760 | return runnablesExplictlySet;
761 | };
762 |
763 | var specFactory = function(description, fn, suite, timeout) {
764 | totalSpecsDefined++;
765 | var spec = new j$.Spec({
766 | id: getNextSpecId(),
767 | beforeAndAfterFns: beforeAndAfterFns(suite, runnablesExplictlySetGetter),
768 | expectationFactory: expectationFactory,
769 | resultCallback: specResultCallback,
770 | getSpecName: function(spec) {
771 | return getSpecName(spec, suite);
772 | },
773 | onStart: specStarted,
774 | description: description,
775 | expectationResultFactory: expectationResultFactory,
776 | queueRunnerFactory: queueRunnerFactory,
777 | userContext: function() { return suite.clonedSharedUserContext(); },
778 | queueableFn: {
779 | fn: fn,
780 | timeout: function() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; }
781 | }
782 | });
783 |
784 | runnableLookupTable[spec.id] = spec;
785 |
786 | if (!self.specFilter(spec)) {
787 | spec.disable();
788 | }
789 |
790 | return spec;
791 |
792 | function specResultCallback(result) {
793 | clearResourcesForRunnable(spec.id);
794 | currentSpec = null;
795 | reporter.specDone(result);
796 | }
797 |
798 | function specStarted(spec) {
799 | currentSpec = spec;
800 | defaultResourcesForRunnable(spec.id, suite.id);
801 | reporter.specStarted(spec.result);
802 | }
803 | };
804 |
805 | this.it = function(description, fn, timeout) {
806 | var spec = specFactory(description, fn, currentDeclarationSuite, timeout);
807 | currentDeclarationSuite.addChild(spec);
808 | return spec;
809 | };
810 |
811 | this.xit = function() {
812 | var spec = this.it.apply(this, arguments);
813 | spec.pend();
814 | return spec;
815 | };
816 |
817 | this.fit = function(){
818 | var spec = this.it.apply(this, arguments);
819 |
820 | focusedRunnables.push(spec.id);
821 | unfocusAncestor();
822 | return spec;
823 | };
824 |
825 | this.expect = function(actual) {
826 | if (!currentRunnable()) {
827 | throw new Error('\'expect\' was used when there was no current spec, this could be because an asynchronous test timed out');
828 | }
829 |
830 | return currentRunnable().expect(actual);
831 | };
832 |
833 | this.beforeEach = function(beforeEachFunction, timeout) {
834 | currentDeclarationSuite.beforeEach({
835 | fn: beforeEachFunction,
836 | timeout: function() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; }
837 | });
838 | };
839 |
840 | this.beforeAll = function(beforeAllFunction, timeout) {
841 | currentDeclarationSuite.beforeAll({
842 | fn: beforeAllFunction,
843 | timeout: function() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; }
844 | });
845 | };
846 |
847 | this.afterEach = function(afterEachFunction, timeout) {
848 | currentDeclarationSuite.afterEach({
849 | fn: afterEachFunction,
850 | timeout: function() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; }
851 | });
852 | };
853 |
854 | this.afterAll = function(afterAllFunction, timeout) {
855 | currentDeclarationSuite.afterAll({
856 | fn: afterAllFunction,
857 | timeout: function() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; }
858 | });
859 | };
860 |
861 | this.pending = function(message) {
862 | var fullMessage = j$.Spec.pendingSpecExceptionMessage;
863 | if(message) {
864 | fullMessage += message;
865 | }
866 | throw fullMessage;
867 | };
868 |
869 | this.fail = function(error) {
870 | var message = 'Failed';
871 | if (error) {
872 | message += ': ';
873 | message += error.message || error;
874 | }
875 |
876 | currentRunnable().addExpectationResult(false, {
877 | matcherName: '',
878 | passed: false,
879 | expected: '',
880 | actual: '',
881 | message: message,
882 | error: error && error.message ? error : null
883 | });
884 | };
885 | }
886 |
887 | return Env;
888 | };
889 |
890 | getJasmineRequireObj().JsApiReporter = function() {
891 |
892 | var noopTimer = {
893 | start: function(){},
894 | elapsed: function(){ return 0; }
895 | };
896 |
897 | function JsApiReporter(options) {
898 | var timer = options.timer || noopTimer,
899 | status = 'loaded';
900 |
901 | this.started = false;
902 | this.finished = false;
903 |
904 | this.jasmineStarted = function() {
905 | this.started = true;
906 | status = 'started';
907 | timer.start();
908 | };
909 |
910 | var executionTime;
911 |
912 | this.jasmineDone = function() {
913 | this.finished = true;
914 | executionTime = timer.elapsed();
915 | status = 'done';
916 | };
917 |
918 | this.status = function() {
919 | return status;
920 | };
921 |
922 | var suites = [],
923 | suites_hash = {};
924 |
925 | this.suiteStarted = function(result) {
926 | suites_hash[result.id] = result;
927 | };
928 |
929 | this.suiteDone = function(result) {
930 | storeSuite(result);
931 | };
932 |
933 | this.suiteResults = function(index, length) {
934 | return suites.slice(index, index + length);
935 | };
936 |
937 | function storeSuite(result) {
938 | suites.push(result);
939 | suites_hash[result.id] = result;
940 | }
941 |
942 | this.suites = function() {
943 | return suites_hash;
944 | };
945 |
946 | var specs = [];
947 |
948 | this.specDone = function(result) {
949 | specs.push(result);
950 | };
951 |
952 | this.specResults = function(index, length) {
953 | return specs.slice(index, index + length);
954 | };
955 |
956 | this.specs = function() {
957 | return specs;
958 | };
959 |
960 | this.executionTime = function() {
961 | return executionTime;
962 | };
963 |
964 | }
965 |
966 | return JsApiReporter;
967 | };
968 |
969 | getJasmineRequireObj().CallTracker = function() {
970 |
971 | function CallTracker() {
972 | var calls = [];
973 |
974 | this.track = function(context) {
975 | calls.push(context);
976 | };
977 |
978 | this.any = function() {
979 | return !!calls.length;
980 | };
981 |
982 | this.count = function() {
983 | return calls.length;
984 | };
985 |
986 | this.argsFor = function(index) {
987 | var call = calls[index];
988 | return call ? call.args : [];
989 | };
990 |
991 | this.all = function() {
992 | return calls;
993 | };
994 |
995 | this.allArgs = function() {
996 | var callArgs = [];
997 | for(var i = 0; i < calls.length; i++){
998 | callArgs.push(calls[i].args);
999 | }
1000 |
1001 | return callArgs;
1002 | };
1003 |
1004 | this.first = function() {
1005 | return calls[0];
1006 | };
1007 |
1008 | this.mostRecent = function() {
1009 | return calls[calls.length - 1];
1010 | };
1011 |
1012 | this.reset = function() {
1013 | calls = [];
1014 | };
1015 | }
1016 |
1017 | return CallTracker;
1018 | };
1019 |
1020 | getJasmineRequireObj().Clock = function() {
1021 | function Clock(global, delayedFunctionScheduler, mockDate) {
1022 | var self = this,
1023 | realTimingFunctions = {
1024 | setTimeout: global.setTimeout,
1025 | clearTimeout: global.clearTimeout,
1026 | setInterval: global.setInterval,
1027 | clearInterval: global.clearInterval
1028 | },
1029 | fakeTimingFunctions = {
1030 | setTimeout: setTimeout,
1031 | clearTimeout: clearTimeout,
1032 | setInterval: setInterval,
1033 | clearInterval: clearInterval
1034 | },
1035 | installed = false,
1036 | timer;
1037 |
1038 |
1039 | self.install = function() {
1040 | replace(global, fakeTimingFunctions);
1041 | timer = fakeTimingFunctions;
1042 | installed = true;
1043 |
1044 | return self;
1045 | };
1046 |
1047 | self.uninstall = function() {
1048 | delayedFunctionScheduler.reset();
1049 | mockDate.uninstall();
1050 | replace(global, realTimingFunctions);
1051 |
1052 | timer = realTimingFunctions;
1053 | installed = false;
1054 | };
1055 |
1056 | self.mockDate = function(initialDate) {
1057 | mockDate.install(initialDate);
1058 | };
1059 |
1060 | self.setTimeout = function(fn, delay, params) {
1061 | if (legacyIE()) {
1062 | if (arguments.length > 2) {
1063 | throw new Error('IE < 9 cannot support extra params to setTimeout without a polyfill');
1064 | }
1065 | return timer.setTimeout(fn, delay);
1066 | }
1067 | return Function.prototype.apply.apply(timer.setTimeout, [global, arguments]);
1068 | };
1069 |
1070 | self.setInterval = function(fn, delay, params) {
1071 | if (legacyIE()) {
1072 | if (arguments.length > 2) {
1073 | throw new Error('IE < 9 cannot support extra params to setInterval without a polyfill');
1074 | }
1075 | return timer.setInterval(fn, delay);
1076 | }
1077 | return Function.prototype.apply.apply(timer.setInterval, [global, arguments]);
1078 | };
1079 |
1080 | self.clearTimeout = function(id) {
1081 | return Function.prototype.call.apply(timer.clearTimeout, [global, id]);
1082 | };
1083 |
1084 | self.clearInterval = function(id) {
1085 | return Function.prototype.call.apply(timer.clearInterval, [global, id]);
1086 | };
1087 |
1088 | self.tick = function(millis) {
1089 | if (installed) {
1090 | mockDate.tick(millis);
1091 | delayedFunctionScheduler.tick(millis);
1092 | } else {
1093 | throw new Error('Mock clock is not installed, use jasmine.clock().install()');
1094 | }
1095 | };
1096 |
1097 | return self;
1098 |
1099 | function legacyIE() {
1100 | //if these methods are polyfilled, apply will be present
1101 | return !(realTimingFunctions.setTimeout || realTimingFunctions.setInterval).apply;
1102 | }
1103 |
1104 | function replace(dest, source) {
1105 | for (var prop in source) {
1106 | dest[prop] = source[prop];
1107 | }
1108 | }
1109 |
1110 | function setTimeout(fn, delay) {
1111 | return delayedFunctionScheduler.scheduleFunction(fn, delay, argSlice(arguments, 2));
1112 | }
1113 |
1114 | function clearTimeout(id) {
1115 | return delayedFunctionScheduler.removeFunctionWithId(id);
1116 | }
1117 |
1118 | function setInterval(fn, interval) {
1119 | return delayedFunctionScheduler.scheduleFunction(fn, interval, argSlice(arguments, 2), true);
1120 | }
1121 |
1122 | function clearInterval(id) {
1123 | return delayedFunctionScheduler.removeFunctionWithId(id);
1124 | }
1125 |
1126 | function argSlice(argsObj, n) {
1127 | return Array.prototype.slice.call(argsObj, n);
1128 | }
1129 | }
1130 |
1131 | return Clock;
1132 | };
1133 |
1134 | getJasmineRequireObj().DelayedFunctionScheduler = function() {
1135 | function DelayedFunctionScheduler() {
1136 | var self = this;
1137 | var scheduledLookup = [];
1138 | var scheduledFunctions = {};
1139 | var currentTime = 0;
1140 | var delayedFnCount = 0;
1141 |
1142 | self.tick = function(millis) {
1143 | millis = millis || 0;
1144 | var endTime = currentTime + millis;
1145 |
1146 | runScheduledFunctions(endTime);
1147 | currentTime = endTime;
1148 | };
1149 |
1150 | self.scheduleFunction = function(funcToCall, millis, params, recurring, timeoutKey, runAtMillis) {
1151 | var f;
1152 | if (typeof(funcToCall) === 'string') {
1153 | /* jshint evil: true */
1154 | f = function() { return eval(funcToCall); };
1155 | /* jshint evil: false */
1156 | } else {
1157 | f = funcToCall;
1158 | }
1159 |
1160 | millis = millis || 0;
1161 | timeoutKey = timeoutKey || ++delayedFnCount;
1162 | runAtMillis = runAtMillis || (currentTime + millis);
1163 |
1164 | var funcToSchedule = {
1165 | runAtMillis: runAtMillis,
1166 | funcToCall: f,
1167 | recurring: recurring,
1168 | params: params,
1169 | timeoutKey: timeoutKey,
1170 | millis: millis
1171 | };
1172 |
1173 | if (runAtMillis in scheduledFunctions) {
1174 | scheduledFunctions[runAtMillis].push(funcToSchedule);
1175 | } else {
1176 | scheduledFunctions[runAtMillis] = [funcToSchedule];
1177 | scheduledLookup.push(runAtMillis);
1178 | scheduledLookup.sort(function (a, b) {
1179 | return a - b;
1180 | });
1181 | }
1182 |
1183 | return timeoutKey;
1184 | };
1185 |
1186 | self.removeFunctionWithId = function(timeoutKey) {
1187 | for (var runAtMillis in scheduledFunctions) {
1188 | var funcs = scheduledFunctions[runAtMillis];
1189 | var i = indexOfFirstToPass(funcs, function (func) {
1190 | return func.timeoutKey === timeoutKey;
1191 | });
1192 |
1193 | if (i > -1) {
1194 | if (funcs.length === 1) {
1195 | delete scheduledFunctions[runAtMillis];
1196 | deleteFromLookup(runAtMillis);
1197 | } else {
1198 | funcs.splice(i, 1);
1199 | }
1200 |
1201 | // intervals get rescheduled when executed, so there's never more
1202 | // than a single scheduled function with a given timeoutKey
1203 | break;
1204 | }
1205 | }
1206 | };
1207 |
1208 | self.reset = function() {
1209 | currentTime = 0;
1210 | scheduledLookup = [];
1211 | scheduledFunctions = {};
1212 | delayedFnCount = 0;
1213 | };
1214 |
1215 | return self;
1216 |
1217 | function indexOfFirstToPass(array, testFn) {
1218 | var index = -1;
1219 |
1220 | for (var i = 0; i < array.length; ++i) {
1221 | if (testFn(array[i])) {
1222 | index = i;
1223 | break;
1224 | }
1225 | }
1226 |
1227 | return index;
1228 | }
1229 |
1230 | function deleteFromLookup(key) {
1231 | var value = Number(key);
1232 | var i = indexOfFirstToPass(scheduledLookup, function (millis) {
1233 | return millis === value;
1234 | });
1235 |
1236 | if (i > -1) {
1237 | scheduledLookup.splice(i, 1);
1238 | }
1239 | }
1240 |
1241 | function reschedule(scheduledFn) {
1242 | self.scheduleFunction(scheduledFn.funcToCall,
1243 | scheduledFn.millis,
1244 | scheduledFn.params,
1245 | true,
1246 | scheduledFn.timeoutKey,
1247 | scheduledFn.runAtMillis + scheduledFn.millis);
1248 | }
1249 |
1250 | function forEachFunction(funcsToRun, callback) {
1251 | for (var i = 0; i < funcsToRun.length; ++i) {
1252 | callback(funcsToRun[i]);
1253 | }
1254 | }
1255 |
1256 | function runScheduledFunctions(endTime) {
1257 | if (scheduledLookup.length === 0 || scheduledLookup[0] > endTime) {
1258 | return;
1259 | }
1260 |
1261 | do {
1262 | currentTime = scheduledLookup.shift();
1263 |
1264 | var funcsToRun = scheduledFunctions[currentTime];
1265 | delete scheduledFunctions[currentTime];
1266 |
1267 | forEachFunction(funcsToRun, function(funcToRun) {
1268 | if (funcToRun.recurring) {
1269 | reschedule(funcToRun);
1270 | }
1271 | });
1272 |
1273 | forEachFunction(funcsToRun, function(funcToRun) {
1274 | funcToRun.funcToCall.apply(null, funcToRun.params || []);
1275 | });
1276 | } while (scheduledLookup.length > 0 &&
1277 | // checking first if we're out of time prevents setTimeout(0)
1278 | // scheduled in a funcToRun from forcing an extra iteration
1279 | currentTime !== endTime &&
1280 | scheduledLookup[0] <= endTime);
1281 | }
1282 | }
1283 |
1284 | return DelayedFunctionScheduler;
1285 | };
1286 |
1287 | getJasmineRequireObj().ExceptionFormatter = function() {
1288 | function ExceptionFormatter() {
1289 | this.message = function(error) {
1290 | var message = '';
1291 |
1292 | if (error.name && error.message) {
1293 | message += error.name + ': ' + error.message;
1294 | } else {
1295 | message += error.toString() + ' thrown';
1296 | }
1297 |
1298 | if (error.fileName || error.sourceURL) {
1299 | message += ' in ' + (error.fileName || error.sourceURL);
1300 | }
1301 |
1302 | if (error.line || error.lineNumber) {
1303 | message += ' (line ' + (error.line || error.lineNumber) + ')';
1304 | }
1305 |
1306 | return message;
1307 | };
1308 |
1309 | this.stack = function(error) {
1310 | return error ? error.stack : null;
1311 | };
1312 | }
1313 |
1314 | return ExceptionFormatter;
1315 | };
1316 |
1317 | getJasmineRequireObj().Expectation = function() {
1318 |
1319 | function Expectation(options) {
1320 | this.util = options.util || { buildFailureMessage: function() {} };
1321 | this.customEqualityTesters = options.customEqualityTesters || [];
1322 | this.actual = options.actual;
1323 | this.addExpectationResult = options.addExpectationResult || function(){};
1324 | this.isNot = options.isNot;
1325 |
1326 | var customMatchers = options.customMatchers || {};
1327 | for (var matcherName in customMatchers) {
1328 | this[matcherName] = Expectation.prototype.wrapCompare(matcherName, customMatchers[matcherName]);
1329 | }
1330 | }
1331 |
1332 | Expectation.prototype.wrapCompare = function(name, matcherFactory) {
1333 | return function() {
1334 | var args = Array.prototype.slice.call(arguments, 0),
1335 | expected = args.slice(0),
1336 | message = '';
1337 |
1338 | args.unshift(this.actual);
1339 |
1340 | var matcher = matcherFactory(this.util, this.customEqualityTesters),
1341 | matcherCompare = matcher.compare;
1342 |
1343 | function defaultNegativeCompare() {
1344 | var result = matcher.compare.apply(null, args);
1345 | result.pass = !result.pass;
1346 | return result;
1347 | }
1348 |
1349 | if (this.isNot) {
1350 | matcherCompare = matcher.negativeCompare || defaultNegativeCompare;
1351 | }
1352 |
1353 | var result = matcherCompare.apply(null, args);
1354 |
1355 | if (!result.pass) {
1356 | if (!result.message) {
1357 | args.unshift(this.isNot);
1358 | args.unshift(name);
1359 | message = this.util.buildFailureMessage.apply(null, args);
1360 | } else {
1361 | if (Object.prototype.toString.apply(result.message) === '[object Function]') {
1362 | message = result.message();
1363 | } else {
1364 | message = result.message;
1365 | }
1366 | }
1367 | }
1368 |
1369 | if (expected.length == 1) {
1370 | expected = expected[0];
1371 | }
1372 |
1373 | // TODO: how many of these params are needed?
1374 | this.addExpectationResult(
1375 | result.pass,
1376 | {
1377 | matcherName: name,
1378 | passed: result.pass,
1379 | message: message,
1380 | actual: this.actual,
1381 | expected: expected // TODO: this may need to be arrayified/sliced
1382 | }
1383 | );
1384 | };
1385 | };
1386 |
1387 | Expectation.addCoreMatchers = function(matchers) {
1388 | var prototype = Expectation.prototype;
1389 | for (var matcherName in matchers) {
1390 | var matcher = matchers[matcherName];
1391 | prototype[matcherName] = prototype.wrapCompare(matcherName, matcher);
1392 | }
1393 | };
1394 |
1395 | Expectation.Factory = function(options) {
1396 | options = options || {};
1397 |
1398 | var expect = new Expectation(options);
1399 |
1400 | // TODO: this would be nice as its own Object - NegativeExpectation
1401 | // TODO: copy instead of mutate options
1402 | options.isNot = true;
1403 | expect.not = new Expectation(options);
1404 |
1405 | return expect;
1406 | };
1407 |
1408 | return Expectation;
1409 | };
1410 |
1411 | //TODO: expectation result may make more sense as a presentation of an expectation.
1412 | getJasmineRequireObj().buildExpectationResult = function() {
1413 | function buildExpectationResult(options) {
1414 | var messageFormatter = options.messageFormatter || function() {},
1415 | stackFormatter = options.stackFormatter || function() {};
1416 |
1417 | var result = {
1418 | matcherName: options.matcherName,
1419 | message: message(),
1420 | stack: stack(),
1421 | passed: options.passed
1422 | };
1423 |
1424 | if(!result.passed) {
1425 | result.expected = options.expected;
1426 | result.actual = options.actual;
1427 | }
1428 |
1429 | return result;
1430 |
1431 | function message() {
1432 | if (options.passed) {
1433 | return 'Passed.';
1434 | } else if (options.message) {
1435 | return options.message;
1436 | } else if (options.error) {
1437 | return messageFormatter(options.error);
1438 | }
1439 | return '';
1440 | }
1441 |
1442 | function stack() {
1443 | if (options.passed) {
1444 | return '';
1445 | }
1446 |
1447 | var error = options.error;
1448 | if (!error) {
1449 | try {
1450 | throw new Error(message());
1451 | } catch (e) {
1452 | error = e;
1453 | }
1454 | }
1455 | return stackFormatter(error);
1456 | }
1457 | }
1458 |
1459 | return buildExpectationResult;
1460 | };
1461 |
1462 | getJasmineRequireObj().MockDate = function() {
1463 | function MockDate(global) {
1464 | var self = this;
1465 | var currentTime = 0;
1466 |
1467 | if (!global || !global.Date) {
1468 | self.install = function() {};
1469 | self.tick = function() {};
1470 | self.uninstall = function() {};
1471 | return self;
1472 | }
1473 |
1474 | var GlobalDate = global.Date;
1475 |
1476 | self.install = function(mockDate) {
1477 | if (mockDate instanceof GlobalDate) {
1478 | currentTime = mockDate.getTime();
1479 | } else {
1480 | currentTime = new GlobalDate().getTime();
1481 | }
1482 |
1483 | global.Date = FakeDate;
1484 | };
1485 |
1486 | self.tick = function(millis) {
1487 | millis = millis || 0;
1488 | currentTime = currentTime + millis;
1489 | };
1490 |
1491 | self.uninstall = function() {
1492 | currentTime = 0;
1493 | global.Date = GlobalDate;
1494 | };
1495 |
1496 | createDateProperties();
1497 |
1498 | return self;
1499 |
1500 | function FakeDate() {
1501 | switch(arguments.length) {
1502 | case 0:
1503 | return new GlobalDate(currentTime);
1504 | case 1:
1505 | return new GlobalDate(arguments[0]);
1506 | case 2:
1507 | return new GlobalDate(arguments[0], arguments[1]);
1508 | case 3:
1509 | return new GlobalDate(arguments[0], arguments[1], arguments[2]);
1510 | case 4:
1511 | return new GlobalDate(arguments[0], arguments[1], arguments[2], arguments[3]);
1512 | case 5:
1513 | return new GlobalDate(arguments[0], arguments[1], arguments[2], arguments[3],
1514 | arguments[4]);
1515 | case 6:
1516 | return new GlobalDate(arguments[0], arguments[1], arguments[2], arguments[3],
1517 | arguments[4], arguments[5]);
1518 | default:
1519 | return new GlobalDate(arguments[0], arguments[1], arguments[2], arguments[3],
1520 | arguments[4], arguments[5], arguments[6]);
1521 | }
1522 | }
1523 |
1524 | function createDateProperties() {
1525 | FakeDate.prototype = GlobalDate.prototype;
1526 |
1527 | FakeDate.now = function() {
1528 | if (GlobalDate.now) {
1529 | return currentTime;
1530 | } else {
1531 | throw new Error('Browser does not support Date.now()');
1532 | }
1533 | };
1534 |
1535 | FakeDate.toSource = GlobalDate.toSource;
1536 | FakeDate.toString = GlobalDate.toString;
1537 | FakeDate.parse = GlobalDate.parse;
1538 | FakeDate.UTC = GlobalDate.UTC;
1539 | }
1540 | }
1541 |
1542 | return MockDate;
1543 | };
1544 |
1545 | getJasmineRequireObj().pp = function(j$) {
1546 |
1547 | function PrettyPrinter() {
1548 | this.ppNestLevel_ = 0;
1549 | this.seen = [];
1550 | }
1551 |
1552 | PrettyPrinter.prototype.format = function(value) {
1553 | this.ppNestLevel_++;
1554 | try {
1555 | if (j$.util.isUndefined(value)) {
1556 | this.emitScalar('undefined');
1557 | } else if (value === null) {
1558 | this.emitScalar('null');
1559 | } else if (value === 0 && 1/value === -Infinity) {
1560 | this.emitScalar('-0');
1561 | } else if (value === j$.getGlobal()) {
1562 | this.emitScalar('');
1563 | } else if (value.jasmineToString) {
1564 | this.emitScalar(value.jasmineToString());
1565 | } else if (typeof value === 'string') {
1566 | this.emitString(value);
1567 | } else if (j$.isSpy(value)) {
1568 | this.emitScalar('spy on ' + value.and.identity());
1569 | } else if (value instanceof RegExp) {
1570 | this.emitScalar(value.toString());
1571 | } else if (typeof value === 'function') {
1572 | this.emitScalar('Function');
1573 | } else if (typeof value.nodeType === 'number') {
1574 | this.emitScalar('HTMLNode');
1575 | } else if (value instanceof Date) {
1576 | this.emitScalar('Date(' + value + ')');
1577 | } else if (j$.util.arrayContains(this.seen, value)) {
1578 | this.emitScalar('');
1579 | } else if (j$.isArray_(value) || j$.isA_('Object', value)) {
1580 | this.seen.push(value);
1581 | if (j$.isArray_(value)) {
1582 | this.emitArray(value);
1583 | } else {
1584 | this.emitObject(value);
1585 | }
1586 | this.seen.pop();
1587 | } else {
1588 | this.emitScalar(value.toString());
1589 | }
1590 | } finally {
1591 | this.ppNestLevel_--;
1592 | }
1593 | };
1594 |
1595 | PrettyPrinter.prototype.iterateObject = function(obj, fn) {
1596 | for (var property in obj) {
1597 | if (!Object.prototype.hasOwnProperty.call(obj, property)) { continue; }
1598 | fn(property, obj.__lookupGetter__ ? (!j$.util.isUndefined(obj.__lookupGetter__(property)) &&
1599 | obj.__lookupGetter__(property) !== null) : false);
1600 | }
1601 | };
1602 |
1603 | PrettyPrinter.prototype.emitArray = j$.unimplementedMethod_;
1604 | PrettyPrinter.prototype.emitObject = j$.unimplementedMethod_;
1605 | PrettyPrinter.prototype.emitScalar = j$.unimplementedMethod_;
1606 | PrettyPrinter.prototype.emitString = j$.unimplementedMethod_;
1607 |
1608 | function StringPrettyPrinter() {
1609 | PrettyPrinter.call(this);
1610 |
1611 | this.string = '';
1612 | }
1613 |
1614 | j$.util.inherit(StringPrettyPrinter, PrettyPrinter);
1615 |
1616 | StringPrettyPrinter.prototype.emitScalar = function(value) {
1617 | this.append(value);
1618 | };
1619 |
1620 | StringPrettyPrinter.prototype.emitString = function(value) {
1621 | this.append('\'' + value + '\'');
1622 | };
1623 |
1624 | StringPrettyPrinter.prototype.emitArray = function(array) {
1625 | if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) {
1626 | this.append('Array');
1627 | return;
1628 | }
1629 | var length = Math.min(array.length, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH);
1630 | this.append('[ ');
1631 | for (var i = 0; i < length; i++) {
1632 | if (i > 0) {
1633 | this.append(', ');
1634 | }
1635 | this.format(array[i]);
1636 | }
1637 | if(array.length > length){
1638 | this.append(', ...');
1639 | }
1640 | this.append(' ]');
1641 | };
1642 |
1643 | StringPrettyPrinter.prototype.emitObject = function(obj) {
1644 | var constructorName = obj.constructor ? j$.fnNameFor(obj.constructor) : 'null';
1645 | this.append(constructorName);
1646 |
1647 | if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) {
1648 | return;
1649 | }
1650 |
1651 | var self = this;
1652 | this.append('({ ');
1653 | var first = true;
1654 |
1655 | this.iterateObject(obj, function(property, isGetter) {
1656 | if (first) {
1657 | first = false;
1658 | } else {
1659 | self.append(', ');
1660 | }
1661 |
1662 | self.append(property);
1663 | self.append(': ');
1664 | if (isGetter) {
1665 | self.append('');
1666 | } else {
1667 | self.format(obj[property]);
1668 | }
1669 | });
1670 |
1671 | this.append(' })');
1672 | };
1673 |
1674 | StringPrettyPrinter.prototype.append = function(value) {
1675 | this.string += value;
1676 | };
1677 |
1678 | return function(value) {
1679 | var stringPrettyPrinter = new StringPrettyPrinter();
1680 | stringPrettyPrinter.format(value);
1681 | return stringPrettyPrinter.string;
1682 | };
1683 | };
1684 |
1685 | getJasmineRequireObj().QueueRunner = function(j$) {
1686 |
1687 | function once(fn) {
1688 | var called = false;
1689 | return function() {
1690 | if (!called) {
1691 | called = true;
1692 | fn();
1693 | }
1694 | };
1695 | }
1696 |
1697 | function QueueRunner(attrs) {
1698 | this.queueableFns = attrs.queueableFns || [];
1699 | this.onComplete = attrs.onComplete || function() {};
1700 | this.clearStack = attrs.clearStack || function(fn) {fn();};
1701 | this.onException = attrs.onException || function() {};
1702 | this.catchException = attrs.catchException || function() { return true; };
1703 | this.userContext = attrs.userContext || {};
1704 | this.timer = attrs.timeout || {setTimeout: setTimeout, clearTimeout: clearTimeout};
1705 | this.fail = attrs.fail || function() {};
1706 | }
1707 |
1708 | QueueRunner.prototype.execute = function() {
1709 | this.run(this.queueableFns, 0);
1710 | };
1711 |
1712 | QueueRunner.prototype.run = function(queueableFns, recursiveIndex) {
1713 | var length = queueableFns.length,
1714 | self = this,
1715 | iterativeIndex;
1716 |
1717 |
1718 | for(iterativeIndex = recursiveIndex; iterativeIndex < length; iterativeIndex++) {
1719 | var queueableFn = queueableFns[iterativeIndex];
1720 | if (queueableFn.fn.length > 0) {
1721 | attemptAsync(queueableFn);
1722 | return;
1723 | } else {
1724 | attemptSync(queueableFn);
1725 | }
1726 | }
1727 |
1728 | var runnerDone = iterativeIndex >= length;
1729 |
1730 | if (runnerDone) {
1731 | this.clearStack(this.onComplete);
1732 | }
1733 |
1734 | function attemptSync(queueableFn) {
1735 | try {
1736 | queueableFn.fn.call(self.userContext);
1737 | } catch (e) {
1738 | handleException(e, queueableFn);
1739 | }
1740 | }
1741 |
1742 | function attemptAsync(queueableFn) {
1743 | var clearTimeout = function () {
1744 | Function.prototype.apply.apply(self.timer.clearTimeout, [j$.getGlobal(), [timeoutId]]);
1745 | },
1746 | next = once(function () {
1747 | clearTimeout(timeoutId);
1748 | self.run(queueableFns, iterativeIndex + 1);
1749 | }),
1750 | timeoutId;
1751 |
1752 | next.fail = function() {
1753 | self.fail.apply(null, arguments);
1754 | next();
1755 | };
1756 |
1757 | if (queueableFn.timeout) {
1758 | timeoutId = Function.prototype.apply.apply(self.timer.setTimeout, [j$.getGlobal(), [function() {
1759 | var error = new Error('Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.');
1760 | onException(error, queueableFn);
1761 | next();
1762 | }, queueableFn.timeout()]]);
1763 | }
1764 |
1765 | try {
1766 | queueableFn.fn.call(self.userContext, next);
1767 | } catch (e) {
1768 | handleException(e, queueableFn);
1769 | next();
1770 | }
1771 | }
1772 |
1773 | function onException(e, queueableFn) {
1774 | self.onException(e);
1775 | }
1776 |
1777 | function handleException(e, queueableFn) {
1778 | onException(e, queueableFn);
1779 | if (!self.catchException(e)) {
1780 | //TODO: set a var when we catch an exception and
1781 | //use a finally block to close the loop in a nice way..
1782 | throw e;
1783 | }
1784 | }
1785 | };
1786 |
1787 | return QueueRunner;
1788 | };
1789 |
1790 | getJasmineRequireObj().ReportDispatcher = function() {
1791 | function ReportDispatcher(methods) {
1792 |
1793 | var dispatchedMethods = methods || [];
1794 |
1795 | for (var i = 0; i < dispatchedMethods.length; i++) {
1796 | var method = dispatchedMethods[i];
1797 | this[method] = (function(m) {
1798 | return function() {
1799 | dispatch(m, arguments);
1800 | };
1801 | }(method));
1802 | }
1803 |
1804 | var reporters = [];
1805 |
1806 | this.addReporter = function(reporter) {
1807 | reporters.push(reporter);
1808 | };
1809 |
1810 | return this;
1811 |
1812 | function dispatch(method, args) {
1813 | for (var i = 0; i < reporters.length; i++) {
1814 | var reporter = reporters[i];
1815 | if (reporter[method]) {
1816 | reporter[method].apply(reporter, args);
1817 | }
1818 | }
1819 | }
1820 | }
1821 |
1822 | return ReportDispatcher;
1823 | };
1824 |
1825 |
1826 | getJasmineRequireObj().SpyRegistry = function(j$) {
1827 |
1828 | function SpyRegistry(options) {
1829 | options = options || {};
1830 | var currentSpies = options.currentSpies || function() { return []; };
1831 |
1832 | this.spyOn = function(obj, methodName) {
1833 | if (j$.util.isUndefined(obj)) {
1834 | throw new Error('spyOn could not find an object to spy upon for ' + methodName + '()');
1835 | }
1836 |
1837 | if (j$.util.isUndefined(methodName)) {
1838 | throw new Error('No method name supplied');
1839 | }
1840 |
1841 | if (j$.util.isUndefined(obj[methodName])) {
1842 | throw new Error(methodName + '() method does not exist');
1843 | }
1844 |
1845 | if (obj[methodName] && j$.isSpy(obj[methodName])) {
1846 | //TODO?: should this return the current spy? Downside: may cause user confusion about spy state
1847 | throw new Error(methodName + ' has already been spied upon');
1848 | }
1849 |
1850 | var spy = j$.createSpy(methodName, obj[methodName]);
1851 |
1852 | currentSpies().push({
1853 | spy: spy,
1854 | baseObj: obj,
1855 | methodName: methodName,
1856 | originalValue: obj[methodName]
1857 | });
1858 |
1859 | obj[methodName] = spy;
1860 |
1861 | return spy;
1862 | };
1863 |
1864 | this.clearSpies = function() {
1865 | var spies = currentSpies();
1866 | for (var i = 0; i < spies.length; i++) {
1867 | var spyEntry = spies[i];
1868 | spyEntry.baseObj[spyEntry.methodName] = spyEntry.originalValue;
1869 | }
1870 | };
1871 | }
1872 |
1873 | return SpyRegistry;
1874 | };
1875 |
1876 | getJasmineRequireObj().SpyStrategy = function() {
1877 |
1878 | function SpyStrategy(options) {
1879 | options = options || {};
1880 |
1881 | var identity = options.name || 'unknown',
1882 | originalFn = options.fn || function() {},
1883 | getSpy = options.getSpy || function() {},
1884 | plan = function() {};
1885 |
1886 | this.identity = function() {
1887 | return identity;
1888 | };
1889 |
1890 | this.exec = function() {
1891 | return plan.apply(this, arguments);
1892 | };
1893 |
1894 | this.callThrough = function() {
1895 | plan = originalFn;
1896 | return getSpy();
1897 | };
1898 |
1899 | this.returnValue = function(value) {
1900 | plan = function() {
1901 | return value;
1902 | };
1903 | return getSpy();
1904 | };
1905 |
1906 | this.returnValues = function() {
1907 | var values = Array.prototype.slice.call(arguments);
1908 | plan = function () {
1909 | return values.shift();
1910 | };
1911 | return getSpy();
1912 | };
1913 |
1914 | this.throwError = function(something) {
1915 | var error = (something instanceof Error) ? something : new Error(something);
1916 | plan = function() {
1917 | throw error;
1918 | };
1919 | return getSpy();
1920 | };
1921 |
1922 | this.callFake = function(fn) {
1923 | plan = fn;
1924 | return getSpy();
1925 | };
1926 |
1927 | this.stub = function(fn) {
1928 | plan = function() {};
1929 | return getSpy();
1930 | };
1931 | }
1932 |
1933 | return SpyStrategy;
1934 | };
1935 |
1936 | getJasmineRequireObj().Suite = function() {
1937 | function Suite(attrs) {
1938 | this.env = attrs.env;
1939 | this.id = attrs.id;
1940 | this.parentSuite = attrs.parentSuite;
1941 | this.description = attrs.description;
1942 | this.onStart = attrs.onStart || function() {};
1943 | this.resultCallback = attrs.resultCallback || function() {};
1944 | this.clearStack = attrs.clearStack || function(fn) {fn();};
1945 | this.expectationFactory = attrs.expectationFactory;
1946 | this.expectationResultFactory = attrs.expectationResultFactory;
1947 | this.runnablesExplictlySetGetter = attrs.runnablesExplictlySetGetter || function() {};
1948 |
1949 | this.beforeFns = [];
1950 | this.afterFns = [];
1951 | this.beforeAllFns = [];
1952 | this.afterAllFns = [];
1953 | this.queueRunner = attrs.queueRunner || function() {};
1954 | this.disabled = false;
1955 |
1956 | this.children = [];
1957 |
1958 | this.result = {
1959 | id: this.id,
1960 | description: this.description,
1961 | fullName: this.getFullName(),
1962 | failedExpectations: []
1963 | };
1964 | }
1965 |
1966 | Suite.prototype.expect = function(actual) {
1967 | return this.expectationFactory(actual, this);
1968 | };
1969 |
1970 | Suite.prototype.getFullName = function() {
1971 | var fullName = this.description;
1972 | for (var parentSuite = this.parentSuite; parentSuite; parentSuite = parentSuite.parentSuite) {
1973 | if (parentSuite.parentSuite) {
1974 | fullName = parentSuite.description + ' ' + fullName;
1975 | }
1976 | }
1977 | return fullName;
1978 | };
1979 |
1980 | Suite.prototype.disable = function() {
1981 | this.disabled = true;
1982 | };
1983 |
1984 | Suite.prototype.beforeEach = function(fn) {
1985 | this.beforeFns.unshift(fn);
1986 | };
1987 |
1988 | Suite.prototype.beforeAll = function(fn) {
1989 | this.beforeAllFns.push(fn);
1990 | };
1991 |
1992 | Suite.prototype.afterEach = function(fn) {
1993 | this.afterFns.unshift(fn);
1994 | };
1995 |
1996 | Suite.prototype.afterAll = function(fn) {
1997 | this.afterAllFns.push(fn);
1998 | };
1999 |
2000 | Suite.prototype.addChild = function(child) {
2001 | this.children.push(child);
2002 | };
2003 |
2004 | Suite.prototype.status = function() {
2005 | if (this.disabled) {
2006 | return 'disabled';
2007 | }
2008 |
2009 | if (this.result.failedExpectations.length > 0) {
2010 | return 'failed';
2011 | } else {
2012 | return 'finished';
2013 | }
2014 | };
2015 |
2016 | Suite.prototype.execute = function(onComplete) {
2017 | var self = this;
2018 |
2019 | this.onStart(this);
2020 |
2021 | if (this.disabled) {
2022 | complete();
2023 | return;
2024 | }
2025 |
2026 | var allFns = [];
2027 |
2028 | for (var i = 0; i < this.children.length; i++) {
2029 | allFns.push(wrapChildAsAsync(this.children[i]));
2030 | }
2031 |
2032 | if (this.isExecutable()) {
2033 | allFns = this.beforeAllFns.concat(allFns);
2034 | allFns = allFns.concat(this.afterAllFns);
2035 | }
2036 |
2037 | this.queueRunner({
2038 | queueableFns: allFns,
2039 | onComplete: complete,
2040 | userContext: this.sharedUserContext(),
2041 | onException: function() { self.onException.apply(self, arguments); }
2042 | });
2043 |
2044 | function complete() {
2045 | self.result.status = self.status();
2046 | self.resultCallback(self.result);
2047 |
2048 | if (onComplete) {
2049 | onComplete();
2050 | }
2051 | }
2052 |
2053 | function wrapChildAsAsync(child) {
2054 | return { fn: function(done) { child.execute(done); } };
2055 | }
2056 | };
2057 |
2058 | Suite.prototype.isExecutable = function() {
2059 | var runnablesExplicitlySet = this.runnablesExplictlySetGetter();
2060 | return !runnablesExplicitlySet && hasExecutableChild(this.children);
2061 | };
2062 |
2063 | Suite.prototype.sharedUserContext = function() {
2064 | if (!this.sharedContext) {
2065 | this.sharedContext = this.parentSuite ? clone(this.parentSuite.sharedUserContext()) : {};
2066 | }
2067 |
2068 | return this.sharedContext;
2069 | };
2070 |
2071 | Suite.prototype.clonedSharedUserContext = function() {
2072 | return clone(this.sharedUserContext());
2073 | };
2074 |
2075 | Suite.prototype.onException = function() {
2076 | if(isAfterAll(this.children)) {
2077 | var data = {
2078 | matcherName: '',
2079 | passed: false,
2080 | expected: '',
2081 | actual: '',
2082 | error: arguments[0]
2083 | };
2084 | this.result.failedExpectations.push(this.expectationResultFactory(data));
2085 | } else {
2086 | for (var i = 0; i < this.children.length; i++) {
2087 | var child = this.children[i];
2088 | child.onException.apply(child, arguments);
2089 | }
2090 | }
2091 | };
2092 |
2093 | Suite.prototype.addExpectationResult = function () {
2094 | if(isAfterAll(this.children) && isFailure(arguments)){
2095 | var data = arguments[1];
2096 | this.result.failedExpectations.push(this.expectationResultFactory(data));
2097 | } else {
2098 | for (var i = 0; i < this.children.length; i++) {
2099 | var child = this.children[i];
2100 | child.addExpectationResult.apply(child, arguments);
2101 | }
2102 | }
2103 | };
2104 |
2105 | function isAfterAll(children) {
2106 | return children && children[0].result.status;
2107 | }
2108 |
2109 | function isFailure(args) {
2110 | return !args[0];
2111 | }
2112 |
2113 | function hasExecutableChild(children) {
2114 | var foundActive = false;
2115 | for (var i = 0; i < children.length; i++) {
2116 | if (children[i].isExecutable()) {
2117 | foundActive = true;
2118 | break;
2119 | }
2120 | }
2121 | return foundActive;
2122 | }
2123 |
2124 | function clone(obj) {
2125 | var clonedObj = {};
2126 | for (var prop in obj) {
2127 | if (obj.hasOwnProperty(prop)) {
2128 | clonedObj[prop] = obj[prop];
2129 | }
2130 | }
2131 |
2132 | return clonedObj;
2133 | }
2134 |
2135 | return Suite;
2136 | };
2137 |
2138 | if (typeof window == void 0 && typeof exports == 'object') {
2139 | exports.Suite = jasmineRequire.Suite;
2140 | }
2141 |
2142 | getJasmineRequireObj().Timer = function() {
2143 | var defaultNow = (function(Date) {
2144 | return function() { return new Date().getTime(); };
2145 | })(Date);
2146 |
2147 | function Timer(options) {
2148 | options = options || {};
2149 |
2150 | var now = options.now || defaultNow,
2151 | startTime;
2152 |
2153 | this.start = function() {
2154 | startTime = now();
2155 | };
2156 |
2157 | this.elapsed = function() {
2158 | return now() - startTime;
2159 | };
2160 | }
2161 |
2162 | return Timer;
2163 | };
2164 |
2165 | getJasmineRequireObj().Any = function() {
2166 |
2167 | function Any(expectedObject) {
2168 | this.expectedObject = expectedObject;
2169 | }
2170 |
2171 | Any.prototype.asymmetricMatch = function(other) {
2172 | if (this.expectedObject == String) {
2173 | return typeof other == 'string' || other instanceof String;
2174 | }
2175 |
2176 | if (this.expectedObject == Number) {
2177 | return typeof other == 'number' || other instanceof Number;
2178 | }
2179 |
2180 | if (this.expectedObject == Function) {
2181 | return typeof other == 'function' || other instanceof Function;
2182 | }
2183 |
2184 | if (this.expectedObject == Object) {
2185 | return typeof other == 'object';
2186 | }
2187 |
2188 | if (this.expectedObject == Boolean) {
2189 | return typeof other == 'boolean';
2190 | }
2191 |
2192 | return other instanceof this.expectedObject;
2193 | };
2194 |
2195 | Any.prototype.jasmineToString = function() {
2196 | return '';
2197 | };
2198 |
2199 | return Any;
2200 | };
2201 |
2202 | getJasmineRequireObj().Anything = function(j$) {
2203 |
2204 | function Anything() {}
2205 |
2206 | Anything.prototype.asymmetricMatch = function(other) {
2207 | return !j$.util.isUndefined(other) && other !== null;
2208 | };
2209 |
2210 | Anything.prototype.jasmineToString = function() {
2211 | return '';
2212 | };
2213 |
2214 | return Anything;
2215 | };
2216 |
2217 | getJasmineRequireObj().ArrayContaining = function(j$) {
2218 | function ArrayContaining(sample) {
2219 | this.sample = sample;
2220 | }
2221 |
2222 | ArrayContaining.prototype.asymmetricMatch = function(other) {
2223 | var className = Object.prototype.toString.call(this.sample);
2224 | if (className !== '[object Array]') { throw new Error('You must provide an array to arrayContaining, not \'' + this.sample + '\'.'); }
2225 |
2226 | for (var i = 0; i < this.sample.length; i++) {
2227 | var item = this.sample[i];
2228 | if (!j$.matchersUtil.contains(other, item)) {
2229 | return false;
2230 | }
2231 | }
2232 |
2233 | return true;
2234 | };
2235 |
2236 | ArrayContaining.prototype.jasmineToString = function () {
2237 | return '';
2238 | };
2239 |
2240 | return ArrayContaining;
2241 | };
2242 |
2243 | getJasmineRequireObj().ObjectContaining = function(j$) {
2244 |
2245 | function ObjectContaining(sample) {
2246 | this.sample = sample;
2247 | }
2248 |
2249 | ObjectContaining.prototype.asymmetricMatch = function(other) {
2250 | if (typeof(this.sample) !== 'object') { throw new Error('You must provide an object to objectContaining, not \''+this.sample+'\'.'); }
2251 |
2252 | for (var property in this.sample) {
2253 | if (!Object.prototype.hasOwnProperty.call(other, property) ||
2254 | !j$.matchersUtil.equals(this.sample[property], other[property])) {
2255 | return false;
2256 | }
2257 | }
2258 |
2259 | return true;
2260 | };
2261 |
2262 | ObjectContaining.prototype.jasmineToString = function() {
2263 | return '';
2264 | };
2265 |
2266 | return ObjectContaining;
2267 | };
2268 |
2269 | getJasmineRequireObj().StringMatching = function(j$) {
2270 |
2271 | function StringMatching(expected) {
2272 | if (!j$.isString_(expected) && !j$.isA_('RegExp', expected)) {
2273 | throw new Error('Expected is not a String or a RegExp');
2274 | }
2275 |
2276 | this.regexp = new RegExp(expected);
2277 | }
2278 |
2279 | StringMatching.prototype.asymmetricMatch = function(other) {
2280 | return this.regexp.test(other);
2281 | };
2282 |
2283 | StringMatching.prototype.jasmineToString = function() {
2284 | return '';
2285 | };
2286 |
2287 | return StringMatching;
2288 | };
2289 |
2290 | getJasmineRequireObj().matchersUtil = function(j$) {
2291 | // TODO: what to do about jasmine.pp not being inject? move to JSON.stringify? gut PrettyPrinter?
2292 |
2293 | return {
2294 | equals: function(a, b, customTesters) {
2295 | customTesters = customTesters || [];
2296 |
2297 | return eq(a, b, [], [], customTesters);
2298 | },
2299 |
2300 | contains: function(haystack, needle, customTesters) {
2301 | customTesters = customTesters || [];
2302 |
2303 | if ((Object.prototype.toString.apply(haystack) === '[object Array]') ||
2304 | (!!haystack && !haystack.indexOf))
2305 | {
2306 | for (var i = 0; i < haystack.length; i++) {
2307 | if (eq(haystack[i], needle, [], [], customTesters)) {
2308 | return true;
2309 | }
2310 | }
2311 | return false;
2312 | }
2313 |
2314 | return !!haystack && haystack.indexOf(needle) >= 0;
2315 | },
2316 |
2317 | buildFailureMessage: function() {
2318 | var args = Array.prototype.slice.call(arguments, 0),
2319 | matcherName = args[0],
2320 | isNot = args[1],
2321 | actual = args[2],
2322 | expected = args.slice(3),
2323 | englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); });
2324 |
2325 | var message = 'Expected ' +
2326 | j$.pp(actual) +
2327 | (isNot ? ' not ' : ' ') +
2328 | englishyPredicate;
2329 |
2330 | if (expected.length > 0) {
2331 | for (var i = 0; i < expected.length; i++) {
2332 | if (i > 0) {
2333 | message += ',';
2334 | }
2335 | message += ' ' + j$.pp(expected[i]);
2336 | }
2337 | }
2338 |
2339 | return message + '.';
2340 | }
2341 | };
2342 |
2343 | function isAsymmetric(obj) {
2344 | return obj && j$.isA_('Function', obj.asymmetricMatch);
2345 | }
2346 |
2347 | function asymmetricMatch(a, b) {
2348 | var asymmetricA = isAsymmetric(a),
2349 | asymmetricB = isAsymmetric(b);
2350 |
2351 | if (asymmetricA && asymmetricB) {
2352 | return undefined;
2353 | }
2354 |
2355 | if (asymmetricA) {
2356 | return a.asymmetricMatch(b);
2357 | }
2358 |
2359 | if (asymmetricB) {
2360 | return b.asymmetricMatch(a);
2361 | }
2362 | }
2363 |
2364 | // Equality function lovingly adapted from isEqual in
2365 | // [Underscore](http://underscorejs.org)
2366 | function eq(a, b, aStack, bStack, customTesters) {
2367 | var result = true;
2368 |
2369 | var asymmetricResult = asymmetricMatch(a, b);
2370 | if (!j$.util.isUndefined(asymmetricResult)) {
2371 | return asymmetricResult;
2372 | }
2373 |
2374 | for (var i = 0; i < customTesters.length; i++) {
2375 | var customTesterResult = customTesters[i](a, b);
2376 | if (!j$.util.isUndefined(customTesterResult)) {
2377 | return customTesterResult;
2378 | }
2379 | }
2380 |
2381 | if (a instanceof Error && b instanceof Error) {
2382 | return a.message == b.message;
2383 | }
2384 |
2385 | // Identical objects are equal. `0 === -0`, but they aren't identical.
2386 | // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal).
2387 | if (a === b) { return a !== 0 || 1 / a == 1 / b; }
2388 | // A strict comparison is necessary because `null == undefined`.
2389 | if (a === null || b === null) { return a === b; }
2390 | var className = Object.prototype.toString.call(a);
2391 | if (className != Object.prototype.toString.call(b)) { return false; }
2392 | switch (className) {
2393 | // Strings, numbers, dates, and booleans are compared by value.
2394 | case '[object String]':
2395 | // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
2396 | // equivalent to `new String("5")`.
2397 | return a == String(b);
2398 | case '[object Number]':
2399 | // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for
2400 | // other numeric values.
2401 | return a != +a ? b != +b : (a === 0 ? 1 / a == 1 / b : a == +b);
2402 | case '[object Date]':
2403 | case '[object Boolean]':
2404 | // Coerce dates and booleans to numeric primitive values. Dates are compared by their
2405 | // millisecond representations. Note that invalid dates with millisecond representations
2406 | // of `NaN` are not equivalent.
2407 | return +a == +b;
2408 | // RegExps are compared by their source patterns and flags.
2409 | case '[object RegExp]':
2410 | return a.source == b.source &&
2411 | a.global == b.global &&
2412 | a.multiline == b.multiline &&
2413 | a.ignoreCase == b.ignoreCase;
2414 | }
2415 | if (typeof a != 'object' || typeof b != 'object') { return false; }
2416 |
2417 | var aIsDomNode = j$.isDomNode(a);
2418 | var bIsDomNode = j$.isDomNode(b);
2419 | if (aIsDomNode && bIsDomNode) {
2420 | // At first try to use DOM3 method isEqualNode
2421 | if (a.isEqualNode) {
2422 | return a.isEqualNode(b);
2423 | }
2424 | // IE8 doesn't support isEqualNode, try to use outerHTML && innerText
2425 | var aIsElement = a instanceof Element;
2426 | var bIsElement = b instanceof Element;
2427 | if (aIsElement && bIsElement) {
2428 | return a.outerHTML == b.outerHTML;
2429 | }
2430 | if (aIsElement || bIsElement) {
2431 | return false;
2432 | }
2433 | return a.innerText == b.innerText && a.textContent == b.textContent;
2434 | }
2435 | if (aIsDomNode || bIsDomNode) {
2436 | return false;
2437 | }
2438 |
2439 | // Assume equality for cyclic structures. The algorithm for detecting cyclic
2440 | // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
2441 | var length = aStack.length;
2442 | while (length--) {
2443 | // Linear search. Performance is inversely proportional to the number of
2444 | // unique nested structures.
2445 | if (aStack[length] == a) { return bStack[length] == b; }
2446 | }
2447 | // Add the first object to the stack of traversed objects.
2448 | aStack.push(a);
2449 | bStack.push(b);
2450 | var size = 0;
2451 | // Recursively compare objects and arrays.
2452 | // Compare array lengths to determine if a deep comparison is necessary.
2453 | if (className == '[object Array]' && a.length !== b.length) {
2454 | result = false;
2455 | }
2456 |
2457 | if (result) {
2458 | // Objects with different constructors are not equivalent, but `Object`s
2459 | // from different frames are.
2460 | var aCtor = a.constructor, bCtor = b.constructor;
2461 | if (aCtor !== bCtor && !(isFunction(aCtor) && (aCtor instanceof aCtor) &&
2462 | isFunction(bCtor) && (bCtor instanceof bCtor))) {
2463 | return false;
2464 | }
2465 | // Deep compare objects.
2466 | for (var key in a) {
2467 | if (has(a, key)) {
2468 | // Count the expected number of properties.
2469 | size++;
2470 | // Deep compare each member.
2471 | if (!(result = has(b, key) && eq(a[key], b[key], aStack, bStack, customTesters))) { break; }
2472 | }
2473 | }
2474 | // Ensure that both objects contain the same number of properties.
2475 | if (result) {
2476 | for (key in b) {
2477 | if (has(b, key) && !(size--)) { break; }
2478 | }
2479 | result = !size;
2480 | }
2481 | }
2482 | // Remove the first object from the stack of traversed objects.
2483 | aStack.pop();
2484 | bStack.pop();
2485 |
2486 | return result;
2487 |
2488 | function has(obj, key) {
2489 | return Object.prototype.hasOwnProperty.call(obj, key);
2490 | }
2491 |
2492 | function isFunction(obj) {
2493 | return typeof obj === 'function';
2494 | }
2495 | }
2496 | };
2497 |
2498 | getJasmineRequireObj().toBe = function() {
2499 | function toBe() {
2500 | return {
2501 | compare: function(actual, expected) {
2502 | return {
2503 | pass: actual === expected
2504 | };
2505 | }
2506 | };
2507 | }
2508 |
2509 | return toBe;
2510 | };
2511 |
2512 | getJasmineRequireObj().toBeCloseTo = function() {
2513 |
2514 | function toBeCloseTo() {
2515 | return {
2516 | compare: function(actual, expected, precision) {
2517 | if (precision !== 0) {
2518 | precision = precision || 2;
2519 | }
2520 |
2521 | return {
2522 | pass: Math.abs(expected - actual) < (Math.pow(10, -precision) / 2)
2523 | };
2524 | }
2525 | };
2526 | }
2527 |
2528 | return toBeCloseTo;
2529 | };
2530 |
2531 | getJasmineRequireObj().toBeDefined = function() {
2532 | function toBeDefined() {
2533 | return {
2534 | compare: function(actual) {
2535 | return {
2536 | pass: (void 0 !== actual)
2537 | };
2538 | }
2539 | };
2540 | }
2541 |
2542 | return toBeDefined;
2543 | };
2544 |
2545 | getJasmineRequireObj().toBeFalsy = function() {
2546 | function toBeFalsy() {
2547 | return {
2548 | compare: function(actual) {
2549 | return {
2550 | pass: !!!actual
2551 | };
2552 | }
2553 | };
2554 | }
2555 |
2556 | return toBeFalsy;
2557 | };
2558 |
2559 | getJasmineRequireObj().toBeGreaterThan = function() {
2560 |
2561 | function toBeGreaterThan() {
2562 | return {
2563 | compare: function(actual, expected) {
2564 | return {
2565 | pass: actual > expected
2566 | };
2567 | }
2568 | };
2569 | }
2570 |
2571 | return toBeGreaterThan;
2572 | };
2573 |
2574 |
2575 | getJasmineRequireObj().toBeLessThan = function() {
2576 | function toBeLessThan() {
2577 | return {
2578 |
2579 | compare: function(actual, expected) {
2580 | return {
2581 | pass: actual < expected
2582 | };
2583 | }
2584 | };
2585 | }
2586 |
2587 | return toBeLessThan;
2588 | };
2589 | getJasmineRequireObj().toBeNaN = function(j$) {
2590 |
2591 | function toBeNaN() {
2592 | return {
2593 | compare: function(actual) {
2594 | var result = {
2595 | pass: (actual !== actual)
2596 | };
2597 |
2598 | if (result.pass) {
2599 | result.message = 'Expected actual not to be NaN.';
2600 | } else {
2601 | result.message = function() { return 'Expected ' + j$.pp(actual) + ' to be NaN.'; };
2602 | }
2603 |
2604 | return result;
2605 | }
2606 | };
2607 | }
2608 |
2609 | return toBeNaN;
2610 | };
2611 |
2612 | getJasmineRequireObj().toBeNull = function() {
2613 |
2614 | function toBeNull() {
2615 | return {
2616 | compare: function(actual) {
2617 | return {
2618 | pass: actual === null
2619 | };
2620 | }
2621 | };
2622 | }
2623 |
2624 | return toBeNull;
2625 | };
2626 |
2627 | getJasmineRequireObj().toBeTruthy = function() {
2628 |
2629 | function toBeTruthy() {
2630 | return {
2631 | compare: function(actual) {
2632 | return {
2633 | pass: !!actual
2634 | };
2635 | }
2636 | };
2637 | }
2638 |
2639 | return toBeTruthy;
2640 | };
2641 |
2642 | getJasmineRequireObj().toBeUndefined = function() {
2643 |
2644 | function toBeUndefined() {
2645 | return {
2646 | compare: function(actual) {
2647 | return {
2648 | pass: void 0 === actual
2649 | };
2650 | }
2651 | };
2652 | }
2653 |
2654 | return toBeUndefined;
2655 | };
2656 |
2657 | getJasmineRequireObj().toContain = function() {
2658 | function toContain(util, customEqualityTesters) {
2659 | customEqualityTesters = customEqualityTesters || [];
2660 |
2661 | return {
2662 | compare: function(actual, expected) {
2663 |
2664 | return {
2665 | pass: util.contains(actual, expected, customEqualityTesters)
2666 | };
2667 | }
2668 | };
2669 | }
2670 |
2671 | return toContain;
2672 | };
2673 |
2674 | getJasmineRequireObj().toEqual = function() {
2675 |
2676 | function toEqual(util, customEqualityTesters) {
2677 | customEqualityTesters = customEqualityTesters || [];
2678 |
2679 | return {
2680 | compare: function(actual, expected) {
2681 | var result = {
2682 | pass: false
2683 | };
2684 |
2685 | result.pass = util.equals(actual, expected, customEqualityTesters);
2686 |
2687 | return result;
2688 | }
2689 | };
2690 | }
2691 |
2692 | return toEqual;
2693 | };
2694 |
2695 | getJasmineRequireObj().toHaveBeenCalled = function(j$) {
2696 |
2697 | function toHaveBeenCalled() {
2698 | return {
2699 | compare: function(actual) {
2700 | var result = {};
2701 |
2702 | if (!j$.isSpy(actual)) {
2703 | throw new Error('Expected a spy, but got ' + j$.pp(actual) + '.');
2704 | }
2705 |
2706 | if (arguments.length > 1) {
2707 | throw new Error('toHaveBeenCalled does not take arguments, use toHaveBeenCalledWith');
2708 | }
2709 |
2710 | result.pass = actual.calls.any();
2711 |
2712 | result.message = result.pass ?
2713 | 'Expected spy ' + actual.and.identity() + ' not to have been called.' :
2714 | 'Expected spy ' + actual.and.identity() + ' to have been called.';
2715 |
2716 | return result;
2717 | }
2718 | };
2719 | }
2720 |
2721 | return toHaveBeenCalled;
2722 | };
2723 |
2724 | getJasmineRequireObj().toHaveBeenCalledWith = function(j$) {
2725 |
2726 | function toHaveBeenCalledWith(util, customEqualityTesters) {
2727 | return {
2728 | compare: function() {
2729 | var args = Array.prototype.slice.call(arguments, 0),
2730 | actual = args[0],
2731 | expectedArgs = args.slice(1),
2732 | result = { pass: false };
2733 |
2734 | if (!j$.isSpy(actual)) {
2735 | throw new Error('Expected a spy, but got ' + j$.pp(actual) + '.');
2736 | }
2737 |
2738 | if (!actual.calls.any()) {
2739 | result.message = function() { return 'Expected spy ' + actual.and.identity() + ' to have been called with ' + j$.pp(expectedArgs) + ' but it was never called.'; };
2740 | return result;
2741 | }
2742 |
2743 | if (util.contains(actual.calls.allArgs(), expectedArgs, customEqualityTesters)) {
2744 | result.pass = true;
2745 | result.message = function() { return 'Expected spy ' + actual.and.identity() + ' not to have been called with ' + j$.pp(expectedArgs) + ' but it was.'; };
2746 | } else {
2747 | result.message = function() { return 'Expected spy ' + actual.and.identity() + ' to have been called with ' + j$.pp(expectedArgs) + ' but actual calls were ' + j$.pp(actual.calls.allArgs()).replace(/^\[ | \]$/g, '') + '.'; };
2748 | }
2749 |
2750 | return result;
2751 | }
2752 | };
2753 | }
2754 |
2755 | return toHaveBeenCalledWith;
2756 | };
2757 |
2758 | getJasmineRequireObj().toMatch = function(j$) {
2759 |
2760 | function toMatch() {
2761 | return {
2762 | compare: function(actual, expected) {
2763 | if (!j$.isString_(expected) && !j$.isA_('RegExp', expected)) {
2764 | throw new Error('Expected is not a String or a RegExp');
2765 | }
2766 |
2767 | var regexp = new RegExp(expected);
2768 |
2769 | return {
2770 | pass: regexp.test(actual)
2771 | };
2772 | }
2773 | };
2774 | }
2775 |
2776 | return toMatch;
2777 | };
2778 |
2779 | getJasmineRequireObj().toThrow = function(j$) {
2780 |
2781 | function toThrow(util) {
2782 | return {
2783 | compare: function(actual, expected) {
2784 | var result = { pass: false },
2785 | threw = false,
2786 | thrown;
2787 |
2788 | if (typeof actual != 'function') {
2789 | throw new Error('Actual is not a Function');
2790 | }
2791 |
2792 | try {
2793 | actual();
2794 | } catch (e) {
2795 | threw = true;
2796 | thrown = e;
2797 | }
2798 |
2799 | if (!threw) {
2800 | result.message = 'Expected function to throw an exception.';
2801 | return result;
2802 | }
2803 |
2804 | if (arguments.length == 1) {
2805 | result.pass = true;
2806 | result.message = function() { return 'Expected function not to throw, but it threw ' + j$.pp(thrown) + '.'; };
2807 |
2808 | return result;
2809 | }
2810 |
2811 | if (util.equals(thrown, expected)) {
2812 | result.pass = true;
2813 | result.message = function() { return 'Expected function not to throw ' + j$.pp(expected) + '.'; };
2814 | } else {
2815 | result.message = function() { return 'Expected function to throw ' + j$.pp(expected) + ', but it threw ' + j$.pp(thrown) + '.'; };
2816 | }
2817 |
2818 | return result;
2819 | }
2820 | };
2821 | }
2822 |
2823 | return toThrow;
2824 | };
2825 |
2826 | getJasmineRequireObj().toThrowError = function(j$) {
2827 | function toThrowError (util) {
2828 | return {
2829 | compare: function(actual) {
2830 | var threw = false,
2831 | pass = {pass: true},
2832 | fail = {pass: false},
2833 | thrown;
2834 |
2835 | if (typeof actual != 'function') {
2836 | throw new Error('Actual is not a Function');
2837 | }
2838 |
2839 | var errorMatcher = getMatcher.apply(null, arguments);
2840 |
2841 | try {
2842 | actual();
2843 | } catch (e) {
2844 | threw = true;
2845 | thrown = e;
2846 | }
2847 |
2848 | if (!threw) {
2849 | fail.message = 'Expected function to throw an Error.';
2850 | return fail;
2851 | }
2852 |
2853 | if (!(thrown instanceof Error)) {
2854 | fail.message = function() { return 'Expected function to throw an Error, but it threw ' + j$.pp(thrown) + '.'; };
2855 | return fail;
2856 | }
2857 |
2858 | if (errorMatcher.hasNoSpecifics()) {
2859 | pass.message = 'Expected function not to throw an Error, but it threw ' + j$.fnNameFor(thrown) + '.';
2860 | return pass;
2861 | }
2862 |
2863 | if (errorMatcher.matches(thrown)) {
2864 | pass.message = function() {
2865 | return 'Expected function not to throw ' + errorMatcher.errorTypeDescription + errorMatcher.messageDescription() + '.';
2866 | };
2867 | return pass;
2868 | } else {
2869 | fail.message = function() {
2870 | return 'Expected function to throw ' + errorMatcher.errorTypeDescription + errorMatcher.messageDescription() +
2871 | ', but it threw ' + errorMatcher.thrownDescription(thrown) + '.';
2872 | };
2873 | return fail;
2874 | }
2875 | }
2876 | };
2877 |
2878 | function getMatcher() {
2879 | var expected = null,
2880 | errorType = null;
2881 |
2882 | if (arguments.length == 2) {
2883 | expected = arguments[1];
2884 | if (isAnErrorType(expected)) {
2885 | errorType = expected;
2886 | expected = null;
2887 | }
2888 | } else if (arguments.length > 2) {
2889 | errorType = arguments[1];
2890 | expected = arguments[2];
2891 | if (!isAnErrorType(errorType)) {
2892 | throw new Error('Expected error type is not an Error.');
2893 | }
2894 | }
2895 |
2896 | if (expected && !isStringOrRegExp(expected)) {
2897 | if (errorType) {
2898 | throw new Error('Expected error message is not a string or RegExp.');
2899 | } else {
2900 | throw new Error('Expected is not an Error, string, or RegExp.');
2901 | }
2902 | }
2903 |
2904 | function messageMatch(message) {
2905 | if (typeof expected == 'string') {
2906 | return expected == message;
2907 | } else {
2908 | return expected.test(message);
2909 | }
2910 | }
2911 |
2912 | return {
2913 | errorTypeDescription: errorType ? j$.fnNameFor(errorType) : 'an exception',
2914 | thrownDescription: function(thrown) {
2915 | var thrownName = errorType ? j$.fnNameFor(thrown.constructor) : 'an exception',
2916 | thrownMessage = '';
2917 |
2918 | if (expected) {
2919 | thrownMessage = ' with message ' + j$.pp(thrown.message);
2920 | }
2921 |
2922 | return thrownName + thrownMessage;
2923 | },
2924 | messageDescription: function() {
2925 | if (expected === null) {
2926 | return '';
2927 | } else if (expected instanceof RegExp) {
2928 | return ' with a message matching ' + j$.pp(expected);
2929 | } else {
2930 | return ' with message ' + j$.pp(expected);
2931 | }
2932 | },
2933 | hasNoSpecifics: function() {
2934 | return expected === null && errorType === null;
2935 | },
2936 | matches: function(error) {
2937 | return (errorType === null || error.constructor === errorType) &&
2938 | (expected === null || messageMatch(error.message));
2939 | }
2940 | };
2941 | }
2942 |
2943 | function isStringOrRegExp(potential) {
2944 | return potential instanceof RegExp || (typeof potential == 'string');
2945 | }
2946 |
2947 | function isAnErrorType(type) {
2948 | if (typeof type !== 'function') {
2949 | return false;
2950 | }
2951 |
2952 | var Surrogate = function() {};
2953 | Surrogate.prototype = type.prototype;
2954 | return (new Surrogate()) instanceof Error;
2955 | }
2956 | }
2957 |
2958 | return toThrowError;
2959 | };
2960 |
2961 | getJasmineRequireObj().interface = function(jasmine, env) {
2962 | var jasmineInterface = {
2963 | describe: function(description, specDefinitions) {
2964 | return env.describe(description, specDefinitions);
2965 | },
2966 |
2967 | xdescribe: function(description, specDefinitions) {
2968 | return env.xdescribe(description, specDefinitions);
2969 | },
2970 |
2971 | fdescribe: function(description, specDefinitions) {
2972 | return env.fdescribe(description, specDefinitions);
2973 | },
2974 |
2975 | it: function() {
2976 | return env.it.apply(env, arguments);
2977 | },
2978 |
2979 | xit: function() {
2980 | return env.xit.apply(env, arguments);
2981 | },
2982 |
2983 | fit: function() {
2984 | return env.fit.apply(env, arguments);
2985 | },
2986 |
2987 | beforeEach: function() {
2988 | return env.beforeEach.apply(env, arguments);
2989 | },
2990 |
2991 | afterEach: function() {
2992 | return env.afterEach.apply(env, arguments);
2993 | },
2994 |
2995 | beforeAll: function() {
2996 | return env.beforeAll.apply(env, arguments);
2997 | },
2998 |
2999 | afterAll: function() {
3000 | return env.afterAll.apply(env, arguments);
3001 | },
3002 |
3003 | expect: function(actual) {
3004 | return env.expect(actual);
3005 | },
3006 |
3007 | pending: function() {
3008 | return env.pending.apply(env, arguments);
3009 | },
3010 |
3011 | fail: function() {
3012 | return env.fail.apply(env, arguments);
3013 | },
3014 |
3015 | spyOn: function(obj, methodName) {
3016 | return env.spyOn(obj, methodName);
3017 | },
3018 |
3019 | jsApiReporter: new jasmine.JsApiReporter({
3020 | timer: new jasmine.Timer()
3021 | }),
3022 |
3023 | jasmine: jasmine
3024 | };
3025 |
3026 | jasmine.addCustomEqualityTester = function(tester) {
3027 | env.addCustomEqualityTester(tester);
3028 | };
3029 |
3030 | jasmine.addMatchers = function(matchers) {
3031 | return env.addMatchers(matchers);
3032 | };
3033 |
3034 | jasmine.clock = function() {
3035 | return env.clock;
3036 | };
3037 |
3038 | return jasmineInterface;
3039 | };
3040 |
3041 | getJasmineRequireObj().version = function() {
3042 | return '2.2.0';
3043 | };
3044 |
--------------------------------------------------------------------------------