├── app
├── model
│ ├── Readme.md
│ ├── Base.js
│ └── Issues.js
├── store
│ ├── Readme.md
│ ├── Servers.js
│ └── Issues.js
├── view
│ ├── main
│ │ ├── MainModel.js
│ │ ├── Main.js
│ │ └── MainController.js
│ ├── issuesGrid
│ │ ├── IssuesGridController.js
│ │ └── IssuesGrid.js
│ └── serversGrid
│ │ ├── ServersGridController.js
│ │ └── ServersGrid.js
├── singleton
│ ├── Functions.js
│ ├── Cache.js
│ └── Socket.js
├── Application.js
└── Readme.md
├── sass
├── config.rb
├── example
│ ├── example.css
│ ├── custom.js
│ └── theme.html
├── Readme.md
├── src
│ └── view
│ │ ├── issuesGrid
│ │ └── IssuesGrid.scss
│ │ └── main
│ │ └── Main.scss
└── etc
│ └── all.scss
├── resources
├── images
│ ├── demo.png
│ └── quadrata_logo.png
├── icons
│ └── favicon.ico
├── fonts
│ └── font-awesome-4.4.0
│ │ └── fonts
│ │ ├── FontAwesome.otf
│ │ ├── fontawesome-webfont.eot
│ │ ├── fontawesome-webfont.ttf
│ │ ├── fontawesome-webfont.woff
│ │ └── fontawesome-webfont.woff2
├── Readme.md
└── js
│ ├── highcharts-4.1.7-no-data-to-display.min.js
│ ├── highcharts-4.1.7-heatmap.min.js
│ ├── highcharts-4.1.7-treemap.min.js
│ └── underscore-1.8.3.min.js
├── config.js
├── .sencha
├── app
│ ├── native.properties
│ ├── testing.properties
│ ├── development.properties
│ ├── production.properties
│ ├── package.properties
│ ├── build.properties
│ ├── testing.defaults.properties
│ ├── resources-impl.xml
│ ├── development.defaults.properties
│ ├── native.defaults.properties
│ ├── package.defaults.properties
│ ├── production.defaults.properties
│ ├── sencha.cfg
│ ├── plugin.xml
│ ├── ext.properties
│ ├── find-cmd-impl.xml
│ ├── watch-impl.xml
│ ├── refresh-impl.xml
│ ├── js-impl.xml
│ ├── resolve-impl.xml
│ ├── packager-impl.xml
│ ├── microloader
│ │ ├── development.js
│ │ └── testing.js
│ ├── init-impl.xml
│ ├── sass-impl.xml
│ ├── page-impl.xml
│ ├── slice-impl.xml
│ ├── bootstrap-impl.xml
│ └── Microloader.js
└── workspace
│ ├── plugin.xml
│ └── sencha.cfg
├── index.html
├── app.js
├── backend
├── package.json
└── server.js
├── Application.md
├── Readme.md
└── app.json
/app/model/Readme.md:
--------------------------------------------------------------------------------
1 | This folder contains the Models for this application.
2 |
--------------------------------------------------------------------------------
/sass/config.rb:
--------------------------------------------------------------------------------
1 | cur_dir = File.dirname(__FILE__)
2 | output_style = :nested
3 |
--------------------------------------------------------------------------------
/resources/images/demo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SKUndef/octo-zab/HEAD/resources/images/demo.png
--------------------------------------------------------------------------------
/resources/icons/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SKUndef/octo-zab/HEAD/resources/icons/favicon.ico
--------------------------------------------------------------------------------
/resources/images/quadrata_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SKUndef/octo-zab/HEAD/resources/images/quadrata_logo.png
--------------------------------------------------------------------------------
/app/store/Readme.md:
--------------------------------------------------------------------------------
1 | This folder contains store instances (identified by storeId) and store types
2 | (with "store.foo" aliases).
3 |
--------------------------------------------------------------------------------
/config.js:
--------------------------------------------------------------------------------
1 | /**
2 | * OctoZab configuration file
3 | */
4 | var config = {
5 | backendUrl: "localhost",
6 | backendPort: "8080"
7 | };
--------------------------------------------------------------------------------
/resources/fonts/font-awesome-4.4.0/fonts/FontAwesome.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SKUndef/octo-zab/HEAD/resources/fonts/font-awesome-4.4.0/fonts/FontAwesome.otf
--------------------------------------------------------------------------------
/resources/Readme.md:
--------------------------------------------------------------------------------
1 | # OctoZab/resources
2 |
3 | This folder contains resources (such as images) needed by the application. This file can
4 | be removed if not needed.
5 |
--------------------------------------------------------------------------------
/resources/fonts/font-awesome-4.4.0/fonts/fontawesome-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SKUndef/octo-zab/HEAD/resources/fonts/font-awesome-4.4.0/fonts/fontawesome-webfont.eot
--------------------------------------------------------------------------------
/resources/fonts/font-awesome-4.4.0/fonts/fontawesome-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SKUndef/octo-zab/HEAD/resources/fonts/font-awesome-4.4.0/fonts/fontawesome-webfont.ttf
--------------------------------------------------------------------------------
/resources/fonts/font-awesome-4.4.0/fonts/fontawesome-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SKUndef/octo-zab/HEAD/resources/fonts/font-awesome-4.4.0/fonts/fontawesome-webfont.woff
--------------------------------------------------------------------------------
/resources/fonts/font-awesome-4.4.0/fonts/fontawesome-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SKUndef/octo-zab/HEAD/resources/fonts/font-awesome-4.4.0/fonts/fontawesome-webfont.woff2
--------------------------------------------------------------------------------
/app/store/Servers.js:
--------------------------------------------------------------------------------
1 | /**
2 | * ZABBIX SERVERS store class.
3 | */
4 |
5 | Ext.define('OctoZab.store.Servers', {
6 | extend: 'Ext.data.Store',
7 |
8 | fields: [
9 | 'server',
10 | 'user',
11 | 'psw'
12 | ],
13 |
14 | batchUpdateMode: 'complete'
15 | });
--------------------------------------------------------------------------------
/app/model/Base.js:
--------------------------------------------------------------------------------
1 | /**
2 | * BASE MODEL on which other models extend their functionality.
3 | */
4 |
5 | Ext.define('OctoZab.model.Base', {
6 | extend: 'Ext.data.Model',
7 |
8 | fields: [{
9 | name: 'id',
10 | type: 'int'
11 | }],
12 |
13 | schema: {
14 | namespace: 'OctoZab.model'
15 | }
16 | });
--------------------------------------------------------------------------------
/app/view/main/MainModel.js:
--------------------------------------------------------------------------------
1 | /**
2 | * This class is the view model for the Main view of the application.
3 | */
4 | Ext.define('OctoZab.view.main.MainModel', {
5 | extend: 'Ext.app.ViewModel',
6 |
7 | alias: 'viewmodel.main',
8 |
9 | data: {
10 | appName: 'OctoZab',
11 | issuesMapView: 'ALL'
12 | }
13 | });
--------------------------------------------------------------------------------
/sass/example/example.css:
--------------------------------------------------------------------------------
1 |
2 |
3 | /*
4 | * This file is generated by Sencha Cmd and should NOT be edited. It redirects
5 | * to the most recently built CSS file for the application to allow theme.html
6 | * to load properly for image slicing (required to support non-CSS3 browsers
7 | * such as IE9 and below).
8 | */
9 | @import '../../build/temp/production/OctoZab/slicer-temp/OctoZab-example.css';
10 |
11 |
--------------------------------------------------------------------------------
/sass/example/custom.js:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is generated as a starting point by Sencha Cmd - it will not be replaced or
3 | * updated by "sencha package upgrade".
4 | *
5 | * This file can be removed and the script tag in theme.html removed if this theme does
6 | * not need custom additional manifest or shortcut entries. These are documented in
7 | * ./packages/ext-theme-base/sass/example/render.js.
8 | */
9 |
10 | //Ext.theme.addManifest();
11 |
12 | //Ext.theme.addShortcuts();
13 |
--------------------------------------------------------------------------------
/.sencha/app/native.properties:
--------------------------------------------------------------------------------
1 | # =============================================================================
2 | # This file provides an override point for default variables defined in
3 | # native.defaults.properties. These properties are only imported when building
4 | # for the "native" environment.
5 | #
6 | # Properties defined in this file take priority over build.properties but are
7 | # only loaded for "native" builds.
8 | # =============================================================================
9 |
--------------------------------------------------------------------------------
/.sencha/app/testing.properties:
--------------------------------------------------------------------------------
1 | # =============================================================================
2 | # This file provides an override point for default variables defined in
3 | # testing.defaults.properties. These properties are only imported when building
4 | # for the "testing" environment.
5 | #
6 | # Properties defined in this file take priority over build.properties but are
7 | # only loaded for "testing" builds.
8 | # =============================================================================
9 |
--------------------------------------------------------------------------------
/.sencha/app/development.properties:
--------------------------------------------------------------------------------
1 | # =============================================================================
2 | # This file provides an override point for default variables defined in
3 | # testing.defaults.properties. These properties are only imported when building
4 | # for the "development" environment.
5 | #
6 | # Properties defined in this file take priority over build.properties but are
7 | # only loaded for "development" builds.
8 | # =============================================================================
9 |
--------------------------------------------------------------------------------
/.sencha/app/production.properties:
--------------------------------------------------------------------------------
1 | # =============================================================================
2 | # This file provides an override point for default variables defined in
3 | # production.defaults.properties. These properties are only imported when building
4 | # for the "production" environment.
5 | #
6 | # Properties defined in this file take priority over build.properties but are
7 | # only loaded for "production" builds.
8 | # =============================================================================
9 |
--------------------------------------------------------------------------------
/.sencha/workspace/plugin.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | OctoZab
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/app/store/Issues.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Last ISSUES GRID store class.
3 | */
4 |
5 | Ext.define('OctoZab.store.Issues', {
6 | extend: 'Ext.data.Store',
7 |
8 | model: 'OctoZab.model.Issues',
9 |
10 | // autoLoad: true,
11 | // autoSync: true,
12 | batchUpdateMode: 'complete',
13 |
14 | groupField: 'server',
15 |
16 | sorters: [{
17 | property: 'acknowledged'
18 | },{
19 | property: 'priority',
20 | direction: 'DESC'
21 | },{
22 | property: 'lastchange',
23 | direction: 'DESC'
24 | }],
25 |
26 | filters: [
27 | Cache.getIssuesServerFilter(),
28 | Cache.getIssuesPriorityFilter()
29 | ]
30 | });
--------------------------------------------------------------------------------
/.sencha/app/package.properties:
--------------------------------------------------------------------------------
1 | # =============================================================================
2 | # This file provides an override point for default variables defined in
3 | # package.defaults.properties. These properties are only imported when building
4 | # for the "package" environment.
5 | #
6 | # Properties defined in this file take priority over build.properties but are
7 | # only loaded for "package" builds.
8 | #
9 | # NOTE: This use of "package" applies to native packaged application, not a
10 | # Package in the general since of code libraries.
11 | # =============================================================================
12 |
--------------------------------------------------------------------------------
/app/singleton/Functions.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Singleton class with useful functions.
3 | */
4 |
5 | Ext.define('OctoZab.singleton.Functions', {
6 | singleton: true,
7 | alternateClassName: ['Functions'],
8 |
9 | requires: [
10 | 'Ext.window.Toast'
11 | ],
12 |
13 | constructor: function(config) {
14 | this.initConfig(config);
15 | },
16 |
17 | toastShow: function(text, bgColor, textColor) {
18 | Ext.toast({
19 | html: text,
20 | closable: false,
21 | stickOnClick: false,
22 | align: 't',
23 | slideInDuration: 100,
24 | autoCloseDelay: 2500,
25 | cls: 'x-toast',
26 | bodyStyle: { background: bgColor, color: textColor }
27 | });
28 | }
29 | });
--------------------------------------------------------------------------------
/app.js:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is generated and updated by Sencha Cmd. You can edit this file as
3 | * needed for your application, but these edits will have to be merged by
4 | * Sencha Cmd when upgrading.
5 | */
6 | Ext.application({
7 | name: 'OctoZab',
8 |
9 | extend: 'OctoZab.Application',
10 |
11 | // autoCreateViewport: 'OctoZab.view.main.Main'
12 |
13 | //-------------------------------------------------------------------------
14 | // Most customizations should be made to OctoZab.Application. If you need to
15 | // customize this file, doing so below this section reduces the likelihood
16 | // of merge conflicts when upgrading to new versions of Sencha Cmd.
17 | //-------------------------------------------------------------------------
18 | });
19 |
--------------------------------------------------------------------------------
/.sencha/app/build.properties:
--------------------------------------------------------------------------------
1 | # =============================================================================
2 | # This file provides an override point for default variables defined in these
3 | # lower priority files:
4 | #
5 | # - ext.properties
6 | # - *.defaults.properties
7 | # - defaults.properties
8 | #
9 | # To override a property based on build.environment instead add properties to
10 | # one of these higher priority files:
11 | #
12 | # - production.properties
13 | # - testing.properties
14 | # - native.properties
15 | # - package.properties
16 | #
17 | # IMPORTANT - Sencha Cmd will merge your changes with its own during upgrades.
18 | # To avoid potential merge conflicts avoid making large, sweeping changes to
19 | # this file.
20 | # =============================================================================
21 |
--------------------------------------------------------------------------------
/backend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "octo-zab",
3 | "version": "0.2.0",
4 | "description": "Backend application to remotely control multiple Zabbix servers",
5 | "main": "server.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "start": "node server.js"
9 | },
10 | "repository": {
11 | "type": "git",
12 | "url": "git+ssh://git@github.com/Quadrata-it/octo-zab.git#master"
13 | },
14 | "keywords": [
15 | "zabbix"
16 | ],
17 | "author": "Pietro Antonacci (SKUndef)",
18 | "license": "GPL-2.0",
19 | "bugs": {
20 | "url": "https://github.com/Quadrata-it/octo-zab/issues"
21 | },
22 | "homepage": "https://github.com/Quadrata-it/octo-zab/tree/master#readme",
23 | "dependencies": {
24 | "ioredis": "^1.7.4",
25 | "request": "^2.60.0",
26 | "socket.io": "^1.3.6",
27 | "underscore": "^1.8.3"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/app/Application.js:
--------------------------------------------------------------------------------
1 | /**
2 | * The main application class. An instance of this class is created by app.js when it calls
3 | * Ext.application(). This is the ideal place to handle application launch and initialization
4 | * details.
5 | */
6 | Ext.define('OctoZab.Application', {
7 |
8 | extend: 'Ext.app.Application',
9 |
10 | name: 'OctoZab',
11 |
12 | requires: [
13 | 'OctoZab.singleton.Socket',
14 | 'OctoZab.singleton.Cache',
15 | 'OctoZab.singleton.Functions'
16 | ],
17 |
18 | views: [
19 | 'main.Main',
20 | 'issuesGrid.IssuesGrid',
21 | 'serversGrid.ServersGrid'
22 | ],
23 |
24 | stores: [
25 | 'Issues',
26 | 'Servers'
27 | ],
28 |
29 | launch: function () {
30 | Highcharts.setOptions({
31 | global : { useUTC: false },
32 | chart : { style: { fontFamily: "helvetica,arial,verdana,sans-serif" } }
33 | });
34 |
35 | Cache.connectServer(Ext.createByAlias('widget.app-main'));
36 | }
37 | });
--------------------------------------------------------------------------------
/.sencha/app/testing.defaults.properties:
--------------------------------------------------------------------------------
1 | # =============================================================================
2 | # This file defines default property values that apply to the "testing" build
3 | # environment.
4 | #
5 | # Please use testing.properties to customize these properties unless you want
6 | # your customizations to be for all environments. In that case, you can instead
7 | # override these properties in build.properties.
8 | #
9 | # The properties defined in this file take priority over defaults.properties
10 | # but are lower priority than build.properties which in turn is lower priority
11 | # than testing.properties.
12 | #
13 | # IMPORTANT - This file should not be modified by an app as it is overwritten
14 | # during each app upgrade.
15 | # =============================================================================
16 |
17 | build.options.logger=yes
18 |
19 | build.options.debug=true
20 |
21 | build.css.compress=false
22 |
--------------------------------------------------------------------------------
/.sencha/app/resources-impl.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/app/view/issuesGrid/IssuesGridController.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Last ISSUES GRID viewcontroller class
3 | */
4 | Ext.define('OctoZab.view.main.IssuesGridController', {
5 | extend: 'Ext.app.ViewController',
6 |
7 | alias: 'controller.issuesGrid',
8 |
9 | control: {
10 | '#': {
11 | cellclick: 'onIssueClick'
12 | }
13 | },
14 |
15 | onIssueClick: function(table, td, index, rec) {
16 | var server = rec.data.server,
17 | hostid = rec.data.hostid,
18 | triggerid = rec.data.triggerid,
19 | eventid = rec.data.eventid;
20 |
21 | switch (index) {
22 | case 0:
23 | case 4:
24 | window.open('//' + server + '/zabbix/tr_events.php?triggerid=' + triggerid + '&eventid=' + eventid, '_blank'); break;
25 | case 2:
26 | window.open('//' + server + '/zabbix/dashboard.php', '_blank'); break;
27 | case 3:
28 | window.open('//' + server + '/zabbix/latest.php?hostids%5B%5D=' + hostid + '&show_without_data=1&filter_set=Filter', '_blank'); break;
29 | }
30 | }
31 | });
--------------------------------------------------------------------------------
/.sencha/app/development.defaults.properties:
--------------------------------------------------------------------------------
1 | # =============================================================================
2 | # This file defines default property values that apply to the "development" build
3 | # environment.
4 | #
5 | # Please use testing.properties to customize these properties unless you want
6 | # your customizations to be for all environments. In that case, you can instead
7 | # override these properties in build.properties.
8 | #
9 | # The properties defined in this file take priority over defaults.properties
10 | # but are lower priority than build.properties which in turn is lower priority
11 | # than development.properties.
12 | #
13 | # IMPORTANT - This file should not be modified by an app as it is overwritten
14 | # during each app upgrade.
15 | # =============================================================================
16 |
17 | build.options.logger=yes
18 |
19 | build.options.debug=true
20 |
21 | build.css.compress=false
22 |
23 | build.include.all.css=true
24 |
--------------------------------------------------------------------------------
/sass/example/theme.html:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 | Ext JS Theme Harness
8 |
9 |
10 |
13 |
14 |
15 |
16 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/.sencha/app/native.defaults.properties:
--------------------------------------------------------------------------------
1 | # =============================================================================
2 | # This file defines default property values that apply to the "native" build
3 | # environment.
4 | #
5 | # Please use native.properties to customize these properties unless you want
6 | # your customizations to be for all environments. In that case, you can instead
7 | # override these properties in build.properties.
8 | #
9 | # The properties defined in this file take priority over defaults.properties
10 | # but are lower priority than build.properties which in turn is lower priority
11 | # than native.properties.
12 | #
13 | # IMPORTANT - This file should not be modified by an app as it is overwritten
14 | # during each app upgrade.
15 | # =============================================================================
16 |
17 | build.options.logger=no
18 |
19 | build.options.debug=false
20 |
21 | # enable yui compression
22 | build.compression.yui=1
23 |
24 | enable.standalone.manifest=true
25 |
26 | app.microloader.name=testing.js
27 |
28 | skip.native-package=false
29 |
--------------------------------------------------------------------------------
/.sencha/app/package.defaults.properties:
--------------------------------------------------------------------------------
1 | # =============================================================================
2 | # This file defines default property values that apply to the "package" build
3 | # environment.
4 | #
5 | # Please use package.properties to customize these properties unless you want
6 | # your customizations to be for all environments. In that case, you can instead
7 | # override these properties in build.properties.
8 | #
9 | # The properties defined in this file take priority over defaults.properties
10 | # but are lower priority than build.properties which in turn is lower priority
11 | # than package.properties.
12 | #
13 | # IMPORTANT - This file should not be modified by an app as it is overwritten
14 | # during each app upgrade.
15 | #
16 | # NOTE: This use of "package" applies to native packaged application, not a
17 | # Package in the general since of code libraries.
18 | # =============================================================================
19 |
20 | build.options.logger=no
21 |
22 | build.options.debug=false
23 |
24 | # enable yui compression
25 | build.compression.yui=1
26 |
27 | app.microloader.name=testing.js
28 |
--------------------------------------------------------------------------------
/app/Readme.md:
--------------------------------------------------------------------------------
1 | # ./controller
2 |
3 | This folder contains the application's global controllers. ViewControllers are located
4 | alongside their respective view class in `"./view"`. These controllers are used for routing
5 | and other activities that span all views.
6 |
7 | # ./model
8 |
9 | This folder contains the application's (data) Model classes.
10 |
11 | # ./view
12 |
13 | This folder contains the views as well as ViewModels and ViewControllers depending on the
14 | application's architecture. Pure MVC applications may not have ViewModels, for example. For
15 | MVCVM applications or MVC applications that use ViewControllers, the following directory
16 | structure is recommended:
17 |
18 | ./view/
19 | foo/ # Some meaningful grouping of one or more views
20 | Foo.js # The view class
21 | FooController.js # The controller for Foo (a ViewController)
22 | FooModel.js # The ViewModel for Foo
23 |
24 | This structure helps keep these closely related classes together and easily identifiable in
25 | most tabbed IDE's or text editors.
26 |
27 | # ./store
28 |
29 | This folder contains any number of store instances or types that can then be reused in the
30 | application.
31 |
--------------------------------------------------------------------------------
/.sencha/app/production.defaults.properties:
--------------------------------------------------------------------------------
1 | # =============================================================================
2 | # This file defines default property values that apply to the "production" build
3 | # environment.
4 | #
5 | # Please use production.properties to customize these properties unless you want
6 | # your customizations to be for all environments. In that case, you can instead
7 | # override these properties in build.properties.
8 | #
9 | # The properties defined in this file take priority over defaults.properties
10 | # but are lower priority than build.properties which in turn is lower priority
11 | # than production.properties.
12 | #
13 | # IMPORTANT - This file should not be modified by an app as it is overwritten
14 | # during each app upgrade.
15 | # =============================================================================
16 |
17 | build.options.logger=no
18 |
19 | build.options.debug=false
20 |
21 | # enable yui compression
22 | build.compression.yui=1
23 |
24 | # enable the full class system optimizer
25 | build.optimize=${build.optimize.disable}
26 |
27 | enable.cache.manifest=true
28 |
29 | enable.resource.compression=true
30 |
31 | build.enable.embedded.manifest=false
32 |
33 | build.embedded.microloader.compressor=-closure
34 |
--------------------------------------------------------------------------------
/app/singleton/Cache.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Singleton class to retain values retrieved from backend.
3 | */
4 |
5 | Ext.define('OctoZab.singleton.Cache', {
6 | singleton: true,
7 | alternateClassName: ['Cache'],
8 |
9 | constructor: function(config) {
10 | this.initConfig(config);
11 | },
12 |
13 | config: {
14 | backendUrl: config.backendUrl + ':' + config.backendPort,
15 | nodeTask: null,
16 | servers: null,
17 | issues: {},
18 | issuesServerFilter: new Ext.util.Filter({
19 | id: 'server-filter',
20 | property: 'server',
21 | value: ""
22 | }),
23 | issuesPriorityFilter: new Ext.util.Filter({
24 | id: 'priority-filter',
25 | property: 'priority',
26 | operator: 'in',
27 | value: ['0','1','2','3','4','5']
28 | }),
29 | issuesMapData: {},
30 | issuesMap: null
31 | },
32 |
33 | connectServer: function(view) {
34 | this.setNodeTask(Ext.TaskManager.start({
35 | interval: 10*1000,
36 |
37 | run: function() {
38 | if (Socket.getSocket() === null) {
39 | $.getScript('//' + Cache.getBackendUrl() + '/socket.io/socket.io.js')
40 | .done(function() {
41 | Socket.connect(view);
42 | Ext.TaskManager.stop(Cache.getNodeTask());
43 | });
44 | }
45 | }
46 | }));
47 | }
48 | });
--------------------------------------------------------------------------------
/resources/js/highcharts-4.1.7-no-data-to-display.min.js:
--------------------------------------------------------------------------------
1 | /*
2 | Highcharts JS v4.1.7 (2015-06-26)
3 | Plugin for displaying a message when there is no data visible in chart.
4 |
5 | (c) 2010-2014 Highsoft AS
6 | Author: Oystein Moseng
7 |
8 | License: www.highcharts.com/license
9 | */
10 | (function(c){function i(){return!!this.points.length}function e(){this.hasData()?this.hideNoData():this.showNoData()}var f=c.seriesTypes,d=c.Chart.prototype,g=c.getOptions(),h=c.extend,j=c.each;h(g.lang,{noData:"No data to display"});g.noData={position:{x:0,y:0,align:"center",verticalAlign:"middle"},attr:{},style:{fontWeight:"bold",fontSize:"12px",color:"#60606a"}};j(["pie","gauge","waterfall","bubble"],function(a){if(f[a])f[a].prototype.hasData=i});c.Series.prototype.hasData=function(){return this.visible&&
11 | this.dataMax!==void 0&&this.dataMin!==void 0};d.showNoData=function(a){var b=this.options,a=a||b.lang.noData,b=b.noData;if(!this.noDataLabel)this.noDataLabel=this.renderer.label(a,0,0,null,null,null,b.useHTML,null,"no-data").attr(b.attr).css(b.style).add(),this.noDataLabel.align(h(this.noDataLabel.getBBox(),b.position),!1,"plotBox")};d.hideNoData=function(){if(this.noDataLabel)this.noDataLabel=this.noDataLabel.destroy()};d.hasData=function(){for(var a=this.series,b=a.length;b--;)if(a[b].hasData()&&
12 | !a[b].options.isInternal)return!0;return!1};d.callbacks.push(function(a){c.addEvent(a,"load",e);c.addEvent(a,"redraw",e)})})(Highcharts);
13 |
--------------------------------------------------------------------------------
/.sencha/app/sencha.cfg:
--------------------------------------------------------------------------------
1 | # This value has been replaced by "build.dir" set in defaults.properties but is
2 | # preserved for compatibility.
3 | app.build.dir=${workspace.build.dir}/${app.name}
4 |
5 | # Path to sass rule definition files corresponding to JavaScript classes.
6 | app.sass.srcpath=${app.dir}/sass/src
7 |
8 | # Path to sass variable definition files corresponding to JavaScript classes.
9 | app.sass.varpath=${app.dir}/sass/var
10 |
11 | # Path to sass function and mixin files.
12 | app.sass.etcpath=${app.dir}/sass/etc/all.scss
13 |
14 | # Path to extra ruby files to include into the generated sass config.rb,
15 | # /sass/config.rb will be included automatically if present and does
16 | # not need to be specified.
17 | # app.sass.rubypath=
18 |
19 | # This property can be modified to change the input and output page file
20 | # used in the compile command. (eg: index.aspx, index.jsp ... )
21 | app.page.name=index.html
22 |
23 | # the input page file
24 | app.page.file=${app.dir}/${app.page.name}
25 |
26 | # this property specifies a comma separated list of paths containing
27 | # resources to copy to the build directory
28 | app.resource.paths=${app.dir}/resources
29 |
30 | #==============================================================================
31 | # Custom Properties - Place customizations below this line to avoid merge
32 | # conflicts with newer versions
33 |
34 | app.sass.namespace=${app.name}
35 | app.theme=ext-theme-classic
36 |
37 | app.framework.version=5.0.0.970
38 | app.cmd.version=5.0.0.160
39 |
--------------------------------------------------------------------------------
/app/model/Issues.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Last ISSUES GRID elements MODEL class.
3 | * It displays TRIGGER.GET api output, filtered to display issues only.
4 | */
5 |
6 | Ext.define('OctoZab.model.Issues', {
7 | extend: 'OctoZab.model.Base',
8 |
9 | fields: [{
10 | name: 'lastchange',
11 | type: 'date',
12 | convert: function(value) {
13 | if (value instanceof Date === false) {
14 | return new Date(parseInt(value, 10)*1000);
15 | } else {
16 | return value;
17 | }
18 | }
19 | },{
20 | name: 'age',
21 | calculate: function(data) {
22 | var lastChange = new Date(data.lastchange),
23 | now = new Date(),
24 | days, hours, mins;
25 |
26 | days = Ext.Date.diff(
27 | lastChange,
28 | now,
29 | Ext.Date.DAY
30 | );
31 | hours = Ext.Date.diff(
32 | lastChange,
33 | Ext.Date.subtract(now, Ext.Date.DAY, days),
34 | Ext.Date.HOUR
35 | );
36 | mins = Ext.Date.diff(
37 | lastChange,
38 | Ext.Date.subtract(
39 | Ext.Date.subtract(now, Ext.Date.DAY, days),
40 | Ext.Date.HOUR,
41 | hours
42 | ),
43 | Ext.Date.MINUTE
44 | );
45 |
46 | return days + 'd ' + hours + 'h ' + mins + 'm';
47 | }
48 | },
49 | 'server',
50 | 'hostid',
51 | 'hostname',
52 | 'description',
53 | 'comments',
54 | 'priority',
55 | 'triggerid',
56 | {
57 | name: 'eventid',
58 | mapping: 'lastEvent.eventid'
59 | },{
60 | name: 'acknowledged',
61 | mapping: 'lastEvent.acknowledged',
62 | type: 'boolean'
63 | }],
64 |
65 | proxy: { type: 'memory' }
66 | });
--------------------------------------------------------------------------------
/.sencha/app/plugin.xml:
--------------------------------------------------------------------------------
1 |
2 |
22 |
23 |
24 |
32 |
33 |
--------------------------------------------------------------------------------
/sass/Readme.md:
--------------------------------------------------------------------------------
1 | # ./sass
2 |
3 | This folder contains the styling for the application's views. The primary pi
4 |
5 | ## Styling
6 |
7 | Sencha Cmd supports styling using Sass and integrates the styling from the theme
8 | and required packages (specified in `"app.json"`) with application-defined views.
9 |
10 | ### ./sass/etc
11 |
12 | This folder contains misc. support code for Sass builds (global functions,
13 | mixins, etc.).
14 |
15 | ### ./sass/src
16 |
17 | This folder contains Sass files defining CSS rules corresponding to classes
18 | included in the application's JavaScript code build. By default, files in this
19 | folder are mapped to the application's root namespace, 'OctoZab'. This is set in
20 | `"app.json"`:
21 |
22 | "sass": {
23 | "namespace": "OctoZab"
24 | }
25 |
26 | ### ./sass/var
27 |
28 | This folder contains Sass files defining Sass variables corresponding to classes
29 | included in the application's JavaScript code build. By default, files in this
30 | folder are mapped to the application's root namespace, 'OctoZab' in the same way
31 | as `"OctoZab/sass/src"`.
32 |
33 | ## Slicing
34 |
35 | Internet Explorer 8 and 9 do not support linear gradients and IE8 does not support
36 | border-radius. To compensate for this, Sencha Cmd provides "image slicing" using an
37 | internal WebKit based renderer. To enable this, there is a special web page that
38 | renders all components and states so they can be captured and turned into image
39 | sprites.
40 |
41 | ### ./sass/example
42 |
43 | This folder contains the web page used to present all components and states so they
44 | can be captured as an image and used to produce images for IE8 and 9.
45 |
--------------------------------------------------------------------------------
/.sencha/workspace/sencha.cfg:
--------------------------------------------------------------------------------
1 | #Wed, 10 Jun 2015 14:40:31 +0200
2 | # -----------------------------------------------------------------------------
3 | # This file contains configuration options that apply to all applications in
4 | # the workspace. By convention, these options start with "workspace." but any
5 | # option can be set here. Options specified in an application's sencha.cfg will
6 | # take priority over those values contained in this file. These options will
7 | # take priority over configuration values in Sencha Cmd or a framework plugin.
8 |
9 | # -----------------------------------------------------------------------------
10 | # This configuration property (if set) is included by default in all compile
11 | # commands executed according to this formulation:
12 | #
13 | # sencha compile -classpath=...,${framework.classpath},${workspace.classpath},${app.classpath}
14 |
15 | #workspace.classpath=
16 |
17 | #------------------------------------------------------------------------------
18 | # This is the folder for build outputs in the workspace
19 |
20 | workspace.build.dir=${workspace.dir}/build
21 |
22 | #------------------------------------------------------------------------------
23 | # This folder contains all generated and extracted packages.
24 |
25 | workspace.packages.dir=${workspace.dir}/packages
26 |
27 | workspace.theme.dir=${workspace.packages.dir}/${args.themeName}
28 |
29 | # =============================================================================
30 | # Customizations go below this divider to avoid merge conflicts on upgrade
31 | # =============================================================================
32 |
33 | workspace.cmd.version=5.0.0.160
34 |
35 | ext.dir=${workspace.dir}/ext
36 |
--------------------------------------------------------------------------------
/.sencha/app/ext.properties:
--------------------------------------------------------------------------------
1 | # =============================================================================
2 | # This file defines default property values that apply to all builds based on
3 | # Ext JS 5.x framework.
4 | #
5 | # Please use build.properties to customize these properties.
6 | #
7 | # To override a property based on build.environment instead add properties to
8 | # one of these higher priority files:
9 | #
10 | # - production.properties
11 | # - testing.properties
12 | # - native.properties
13 | # - package.properties
14 | #
15 | # The properties defined in this file take priority over defaults.properties
16 | # and *.defaults.properties.
17 | #
18 | # IMPORTANT - This file should not be modified by an app as it is overwritten
19 | # during each app upgrade.
20 | # =============================================================================
21 |
22 | enable.ext42.themes=true
23 |
24 | build.output.markuponly=false
25 |
26 | enable.sencha-core.filter=true
27 |
28 | build.options.product=ext
29 |
30 | build.options.minVersion=5
31 |
32 | bootstrap.include.boot=true
33 | bootstrap.override.tpl=Ext.Loader.loadScriptsSync
34 | bootstrap.override.tpltype=jsonp
35 |
36 | app.microloader.name=Microloader.js
37 | app.microloader.dir=${app.config.dir}
38 | app.microloader.bootstrap=${app.microloader.dir}/${app.microloader.name}
39 | app.microloader.path=${app.microloader.dir}/${app.microloader.name}
40 |
41 | build.microloader.json.tpl.embedded=var Ext = Ext || '{' '}'; Ext.manifest = {0};
42 | build.microloader.manifest.name=app
43 | build.microloader.json.tpl.external=var Ext = Ext || '{' '}'; Ext.manifest = "${build.microloader.manifest.name}";
44 |
45 | build.skip.versions.file=true
46 | build.enable.appmanifest=true
47 | build.optimize.defines=false
48 | build.optimize.callparent=false
49 | build.optimize.string.references=false
50 | compass.compile.force=false
51 |
--------------------------------------------------------------------------------
/app/view/serversGrid/ServersGridController.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Last ISSUES GRID viewcontroller class
3 | */
4 | Ext.define('OctoZab.view.main.ServersGridController', {
5 | extend: 'Ext.app.ViewController',
6 |
7 | alias: 'controller.serversGrid',
8 |
9 | onAddServer: function() {
10 | var grid = this.getView(),
11 | store = grid.getStore();
12 |
13 | grid.getPlugin('rowEdit').startEdit(store.add({
14 | server: '',
15 | user: '',
16 | psw: ''
17 | })[0]);
18 | },
19 |
20 | onServerBeforeEdit: function() {
21 | this.lookupReference('addBtn').disable();
22 | },
23 |
24 | onServerEdit: function() {
25 | this.lookupReference('addBtn').enable();
26 | },
27 |
28 | onServerEditCanc: function(editor, context) {
29 | if (!context.value) {
30 | this.getView().getStore().remove(context.record);
31 | }
32 |
33 | this.onServerEdit();
34 | },
35 |
36 | onDeleteServer: function(btn) {
37 | this.getView().getStore().remove(btn.getWidgetRecord());
38 | },
39 |
40 | onResetServers: function() {
41 | var store = this.getView().getStore();
42 |
43 | store.rejectChanges();
44 | store.reload();
45 | },
46 |
47 | onSaveServers: function() {
48 | var store = this.getView().getStore(),
49 | recs = store.getRange(),
50 | modRecs = store.getModifiedRecords(),
51 | delRecs = store.getRemovedRecords();
52 |
53 | if (modRecs.length || delRecs.length) {
54 | Ext.Msg.show({
55 | title: 'SAVE',
56 | message: 'Do you want to update servers configuration?',
57 | closable: false,
58 | buttons: Ext.Msg.OKCANCEL,
59 | fn: function(btn) {
60 | if (btn == 'ok') {
61 | Socket.updateServers(recs);
62 | }
63 | }
64 | });
65 | } else {
66 | Ext.Msg.show({
67 | title: 'ERROR!',
68 | message: 'No modification to the actual servers configuration',
69 | closable: false,
70 | buttons: Ext.Msg.OK
71 | });
72 | }
73 | }
74 | });
--------------------------------------------------------------------------------
/app/view/main/Main.js:
--------------------------------------------------------------------------------
1 | /**
2 | * This class is the main view for the application. It is specified in app.js as the
3 | * "autoCreateViewport" property. That setting automatically applies the "viewport"
4 | * plugin to promote that instance of this class to the body element.
5 | */
6 | Ext.define('OctoZab.view.main.Main', {
7 | extend: 'Ext.tab.Panel',
8 |
9 | requires: [
10 | 'Ext.grid.filters.Filters',
11 | 'Ext.grid.column.Date'
12 | ],
13 |
14 | xtype: 'app-main',
15 | controller: 'main',
16 | viewModel: { type: 'main' },
17 |
18 | plugins: 'viewport',
19 |
20 | header: { cls: 'main-bar' },
21 | tabBar: { cls: 'main-bar' },
22 |
23 | headerPosition: 'left',
24 | tabBarHeaderPosition: 0,
25 | tabPosition: 'left',
26 | tabRotation: 0,
27 |
28 | tools: [{
29 | xtype: 'tbtext',
30 | itemId: 'logo-text',
31 | text: 'OCTOZAB',
32 | cls: 'logo-text'
33 | }],
34 |
35 | defaults: {
36 | bodyPadding: '20 50 20 50',
37 | tabConfig: { tooltipType: 'title' }
38 | },
39 | items: [{
40 | glyph: 'xf0e4@FontAwesome',
41 | tooltip: 'Dashboard',
42 |
43 | layout: { type: 'vbox', align: 'stretch' },
44 |
45 | items: [{
46 | xtype: 'tbtext',
47 | text: "DASHBOARD",
48 | height: 26,
49 | margin: '0 0 20 0',
50 | cls: 'tab-panel-title'
51 | },{
52 | xtype: 'panel',
53 | itemId: 'panel-issues-map',
54 | margin: '0 0 10 0',
55 | flex: 3,
56 | bind: {
57 | title: 'ISSUES MAP / server: {issuesMapView}'
58 | }
59 | },{
60 | xtype: 'grid-issues',
61 | margin: '10 0 0 0',
62 | flex: 4
63 | }]
64 | },{
65 | glyph: 'xf013@FontAwesome',
66 | tooltip: 'Settings',
67 |
68 | layout: { type: 'vbox', align: 'stretch' },
69 |
70 | items: [{
71 | xtype: 'tbtext',
72 | text: "SETTINGS",
73 | height: 26,
74 | margin: '0 0 20 0',
75 | cls: 'tab-panel-title'
76 | },{
77 | xtype: 'grid-servers',
78 | margin: '0 0 10 0',
79 | height: 300
80 | }]
81 | }]
82 | });
--------------------------------------------------------------------------------
/Application.md:
--------------------------------------------------------------------------------
1 | # OctoZab
2 |
3 | This folder is primarily a container for the top-level pieces of the application.
4 | While you can remove some files and folders that this application does not use,
5 | be sure to read below before deciding what can be deleted and what needs to be
6 | kept in source control.
7 |
8 | The following files are all needed to build and load the application.
9 |
10 | - `"app.json"` - The application descriptor which controls how the application is
11 | built and loaded.
12 | - `"app.js"` - The file that launches the application. This is primarily used to
13 | launch an instance of the `MyApp.Application` class.
14 | - `"index.html"` - The default web page for this application. This can be customized
15 | in `"app.json"`.
16 | - `"build.xml"` - The entry point for Sencha Cmd to access the generated build
17 | script. This file is a place where you can hook into these processes and tune
18 | them. See the comments in that file for more information.
19 | - `".sencha"` - This (typically hidden) folder contains the generated build scripts
20 | and configuration files for the application. This folder is required in order to
21 | build the application but its content should not need to be edited in most cases.
22 | The content of this folder is updated by "sencha app upgrade".
23 |
24 | These files can be ignored from source control as they are regenerated by the build
25 | process.
26 |
27 | - `"build"` - This folder contain the output of the build. The generated CSS file,
28 | consolidated resources and concatenated JavaScript file are all stored in this
29 | folder.
30 | - `"bootstrap.*"` - These files are generated by the build and watch commands to
31 | enable the application to load in "development mode".
32 |
33 | # Other Folders
34 |
35 | ## OctoZab/app
36 |
37 | This folder contains the JavaScript files for the application.
38 |
39 | ## OctoZab/resources
40 |
41 | This folder contains static resources (typically an `"images"` folder as well).
42 |
43 | ## OctoZab/overrides
44 |
45 | This folder contains override classes. All overrides in this folder will be
46 | automatically included in application builds if the target class of the override
47 | is loaded.
48 |
49 | ## OctoZab/sass
50 |
51 | This folder contains the styling for the application's views. See OctoZab/sass/Readme.md
52 | for details.
53 |
--------------------------------------------------------------------------------
/sass/src/view/issuesGrid/IssuesGrid.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * ISSUES GRID CSS rules
3 | */
4 |
5 | /** EVENT SEVERITY CELL styling */
6 | .disaster-cell { background-color: #FF1E1E !important; }
7 | .high-cell { background-color: #FF7373 !important; }
8 | .average-cell { background-color: #FFA866 !important; }
9 | .warning-cell { background-color: #FFFF80 !important; }
10 | .info-cell { background-color: #BFFFFF !important; }
11 | .not-classified-cell { background-color: #D9D9D9 !important; }
12 | .ok-cell { background-color: #8CFF8C !important; }
13 | .no-ack-cell { background-color: #FFF380 !important; }
14 |
15 | /** EVENT SEVERITY ROWS styling */
16 | .x-grid-item .disaster-row .x-grid-cell { background-color: #FF1E1E; }
17 | .x-grid-item-selected .disaster-row .x-grid-cell { background-color: #D91919; }
18 | .x-grid-item-over .disaster-row .x-grid-cell { background-color: #D91919; }
19 |
20 | .x-grid-item .high-row .x-grid-cell { background-color: #FF7373; }
21 | .x-grid-item-selected .high-row .x-grid-cell { background-color: #D96262; }
22 | .x-grid-item-over .high-row .x-grid-cell { background-color: #D96262; }
23 |
24 | .x-grid-item .average-row .x-grid-cell { background-color: #FFA866; }
25 | .x-grid-item-selected .average-row .x-grid-cell { background-color: #D98F57; }
26 | .x-grid-item-over .average-row .x-grid-cell { background-color: #D98F57; }
27 |
28 | .x-grid-item .warning-row .x-grid-cell { background-color: #FFFF80; }
29 | .x-grid-item-selected .warning-row .x-grid-cell { background-color: #D9D96D; }
30 | .x-grid-item-over .warning-row .x-grid-cell { background-color: #D9D96D; }
31 |
32 | .x-grid-item .info-row .x-grid-cell { background-color: #BFFFFF; }
33 | .x-grid-item-selected .info-row .x-grid-cell { background-color: #A2D9D9; }
34 | .x-grid-item-over .info-row .x-grid-cell { background-color: #A2D9D9; }
35 |
36 | .x-grid-item .not-classified-row .x-grid-cell { background-color: #D9D9D9; }
37 | .x-grid-item-selected .not-classified-row .x-grid-cell { background-color: #B3B3B3; }
38 | .x-grid-item-over .not-classified-row .x-grid-cell { background-color: #B3B3B3; }
39 |
40 | .x-grid-item .acknowledged-row .x-grid-cell { background-color: #8CFF8C; }
41 | .x-grid-item-selected .acknowledged-row .x-grid-cell { background-color: #77D977; }
42 | .x-grid-item-over .acknowledged-row .x-grid-cell { background-color: #77D977; }
--------------------------------------------------------------------------------
/sass/etc/all.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * GLOBAL SASS VARIABLES
3 | */
4 |
5 | /** FONT */
6 | $font-size: 12px;
7 | $font-family: helvetica,arial,verdana,sans-serif;
8 |
9 | /** BODY */
10 | $body-background-color: #E6E6E6;
11 |
12 | /** BUTTON */
13 | $button-small-font-size: 12px;
14 | $button-default-background-color: #E6E6E6;
15 | $button-default-border-color: #E6E6E6;
16 | $button-default-color: #000;
17 | $button-default-glyph-opacity: 0.6;
18 | $button-default-background-color-over: #D9D9D9;
19 | $button-default-background-color-pressed: #D4D4D4;
20 | $button-default-background-color-focus: #D4D4D4;
21 | $button-default-background-color-disabled: #CCCCCC;
22 | $button-small-border-radius: 2px;
23 |
24 | /** TOOLBAR */
25 | $toolbar-footer-background-color: #505359;
26 |
27 | /** PANEL */
28 | $panel-header-color: #FFF;
29 | $panel-header-background-color: #505359;
30 | $panel-header-font-size: 14px;
31 | $panel-header-font-weight: bold;
32 | $panel-header-text-margin: 0 0 0 6px;
33 | $panel-body-background-color: transparent;
34 |
35 | /** GRID */
36 | $grid-body-border-width: 0;
37 | $grid-header-background-color: #505359;
38 | $grid-header-border-width: 0;
39 | $grid-header-over-background-color: transparent;
40 | $grid-column-header-color: #FFF;
41 | $grid-column-header-font-size: 11px;
42 | $grid-column-header-font-weight: bold;
43 | $grid-column-header-border-width: 0;
44 | $grid-grouped-header-background-color: #505359;
45 | $grid-grouped-header-border-width: 0;
46 | $grid-grouped-title-color: #FFF;
47 | $grid-grouped-title-font-weight: bold;
48 | $grid-grouped-title-line-height: 11px;
49 | $grid-row-cell-line-height: 16px;
50 | $grid-row-cell-over-background-color: #E6E6E6;
51 | $grid-row-cell-selected-background-color: #E6E6E6;
52 | $grid-row-editor-background-color: #E6E6E6;
53 | $grid-row-editor-border-color: #E6E6E6;
54 |
55 | /** TAB */
56 | $tab-glyph-opacity: 1;
57 |
58 | /** WINDOW */
59 | $window-header-background-color: #505359;
60 | $window-border-width: 0;
61 | $window-body-color: #FFF;
62 |
63 | /** MESSAGEBOX */
64 | $messagebox-body-background-color: #505359;
65 |
66 | /** LOADMASK */
67 | $loadmask-opacity: 0.5;
68 | $loadmask-background-color: #E6E6E6;
69 | $loadmask-msg-background-color: #505359;
70 | $loadmask-msg-border-radius: 0;
71 | $loadmask-msg-text-padding: 5px;
72 | $loadmask-msg-inner-color: #FFF;
73 | $loadmask-msg-inner-font-weight: bold;
74 | $loadmask-msg-inner-icon: none;
--------------------------------------------------------------------------------
/.sencha/app/find-cmd-impl.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
27 |
28 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
50 |
51 |
52 | source ~/.bash_profile; sencha which -p cmd.dir -o '$cmddir$'
53 |
54 |
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/.sencha/app/watch-impl.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
23 |
24 |
25 |
26 |
27 |
28 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/app/view/serversGrid/ServersGrid.js:
--------------------------------------------------------------------------------
1 | /**
2 | * ZABBIX SERVERS GRID class
3 | */
4 |
5 | Ext.define('OctoZab.view.serversGrid.ServersGrid', {
6 | extend: 'Ext.grid.Panel',
7 |
8 | xtype: 'grid-servers',
9 | controller: 'serversGrid',
10 |
11 | store: 'Servers',
12 |
13 | title: 'SERVERS',
14 | columnLines: false,
15 | rowLines: false,
16 | reserveScrollbar: true,
17 | sortableColumns: false,
18 | bufferedRenderer: false,
19 |
20 | bodyStyle: { backgroundColor: "#676B73" },
21 |
22 | tools: [{
23 | xtype: 'button',
24 | reference: 'addBtn',
25 | glyph: 'xf067@FontAwesome',
26 | tooltip: 'Add server',
27 | tooltipType: 'title',
28 | cls: 'dark-tool-button',
29 | handler: 'onAddServer'
30 | }],
31 |
32 | plugins: [{
33 | ptype: 'rowediting',
34 | pluginId: 'rowEdit',
35 | autoCancel: false,
36 | errorSummary: false,
37 | listeners: {
38 | beforeedit: 'onServerBeforeEdit',
39 | edit: 'onServerEdit',
40 | canceledit: 'onServerEditCanc',
41 | scope: 'controller'
42 | }
43 | }],
44 |
45 | viewConfig: {
46 | getRowClass: function(record) { return 'dark-big-row'; }
47 | },
48 |
49 | columns: {
50 | defaults: {
51 | align: 'center'
52 | },
53 | items: [{
54 | text: 'HOST',
55 | dataIndex: 'server',
56 | editor: { xtype: 'textfield', allowBlank: false },
57 | renderer: function(value, metaData) {
58 | metaData.style = "font-weight: bold";
59 | return value;
60 | },
61 | flex: 1
62 | },{
63 | text: 'USER',
64 | dataIndex: 'user',
65 | sortable: false,
66 | editor: { xtype: 'textfield', allowBlank: false },
67 | flex: 1
68 | },{
69 | text: 'PASSWORD',
70 | dataIndex: 'psw',
71 | sortable: false,
72 | editor: { xtype: 'textfield', allowBlank: false },
73 | renderer: function(value, metaData) {
74 | return Array(value.length+1).join("\u2022");
75 | },
76 | flex: 1
77 | },{
78 | xtype: 'widgetcolumn',
79 | sortable: false,
80 | menuDisabled: true,
81 | width: 52,
82 | widget: {
83 | xtype: 'button',
84 | glyph: 'xf014@FontAwesome',
85 | tooltip: 'Delete server',
86 | tooltipType: 'title',
87 | cls: 'dark-grid-icon',
88 | handler: 'onDeleteServer'
89 | }
90 | }]
91 | },
92 |
93 | buttons: [{
94 | text: 'Reset',
95 | tooltip: 'Reset configuration',
96 | tooltipType: 'title',
97 | handler: 'onResetServers'
98 | },{
99 | text: 'Save',
100 | tooltip: 'Save configuration',
101 | tooltipType: 'title',
102 | handler: 'onSaveServers'
103 | }]
104 | });
--------------------------------------------------------------------------------
/sass/src/view/main/Main.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * MAIN APP CSS rules.
3 | */
4 |
5 | /** PANEL DARK TOOL */
6 | .dark-tool-button.x-btn-default-small { background-color: #505359; border-color: #505359; border-radius: 0; }
7 | .dark-tool-button.x-btn-over.x-btn-default-small { background-color: #676B73; border-color: #676B73; }
8 | .dark-tool-button .x-btn-icon-el-default-small.x-btn-glyph { color: #FFF; opacity: 1; }
9 |
10 | /** GRID */
11 | .x-grid-filters-filtered-column { color: #66B4FF; }
12 | .wrap-text-cell div { white-space: normal; }
13 |
14 | /** GRID ROWEDIT PLUGIN */
15 | .x-grid-row-editor .x-panel-body { margin-top: 5px; }
16 | .x-grid-row-editor-buttons-default-top { margin-bottom: -5px; }
17 | .x-grid-row-editor-buttons-default-bottom { padding: 10px 5px 5px 5px; }
18 |
19 | /** GRID DARK BIG ROWS */
20 | .x-grid-item .dark-big-row .x-grid-cell {
21 | background-color: #676B73;
22 | color: #FFF;
23 | font-family: verdana,helvetica,arial,sans-serif;
24 | line-height: 28px;
25 | vertical-align: middle;
26 | }
27 | .x-grid-item-over .dark-big-row .x-grid-cell {
28 | background-color: #727780;
29 | }
30 | .x-grid-item-selected .dark-big-row .x-grid-cell {
31 | background-color: #727780;
32 | }
33 |
34 | /** GRID DARK ICON */
35 | .dark-grid-icon.x-btn-default-small {
36 | background-color: transparent;
37 | border-color: transparent;
38 | width: 34px !important;
39 | vertical-align: middle;
40 | }
41 | .dark-grid-icon .x-btn-icon-el-default-small.x-btn-glyph {
42 | color: #FFF;
43 | font-size: 18px;
44 | opacity: 1;
45 | }
46 |
47 | /** WINDOW */
48 | .x-window-default { border-radius: 0; }
49 | .x-window-header-default { border-radius: 0; }
50 |
51 | /** MESSAGEBOX */
52 | .x-message-box .x-form-display-field { color: #FFF; }
53 |
54 | /** TOAST */
55 | .x-toast { height: 38px !important; }
56 | .x-toast .x-window-body-default { font-weight: bold; }
57 |
58 | /** MAIN TAB BAR */
59 | .main-bar.x-panel-header-default-vertical { padding: 10px 0; }
60 | .main-bar .x-tab-bar-default { background-color: #505359; }
61 | .main-bar .x-tab-bar-default-left > .x-tab-bar-body-default { padding: 0; }
62 | .main-bar .x-tab-default { border-color: transparent; }
63 | .main-bar .x-tab-default-left { padding: 9px 22px; margin: 7px 0; background-color: transparent; }
64 | .main-bar .x-tab-active.x-tab-default { background-color: #E6E6E6; border-color: #E6E6E6; }
65 | .main-bar .x-tab-active.x-tab-default .x-tab-glyph { color: #505359; }
66 | .main-bar .x-tab-icon-el-default.x-tab-glyph { font-size: 26px; line-height: 24px; width: 26px; height: 26px; color: #E6E6E6; }
67 |
68 | /** LOGO */
69 | .logo-text {
70 | -webkit-transform: rotate(-90deg);
71 | -moz-transform: rotate(-90deg);
72 | -ms-transform: rotate(-90deg);
73 | -o-transform: rotate(-90deg);
74 | font-weight: bold;
75 | font-size: 24px;
76 | font-family: verdana,sans-serif;
77 | }
78 |
79 | /** TAB PANEL TITLE */
80 | .tab-panel-title {
81 | color: #7A818C;
82 | font-size: 26px;
83 | font-weight: bold;
84 | line-height: 100%;
85 | font-variant: small-caps;
86 | }
--------------------------------------------------------------------------------
/app/singleton/Socket.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Singleton class to manage Socket.IO connection
3 | */
4 |
5 | Ext.define('OctoZab.singleton.Socket', {
6 | singleton: true,
7 | alternateClassName: ['Socket'],
8 |
9 | constructor: function(config) {
10 | this.initConfig(config);
11 | },
12 |
13 | config: {
14 | socket: null
15 | },
16 |
17 | connect: function(view) {
18 | this.setSocket(io.connect(Cache.getBackendUrl()));
19 |
20 | this.getSocket().on('connect', function() {
21 | view.unmask();
22 | });
23 |
24 | this.getSocket().on('reconnect', function() {
25 | view.unmask();
26 | });
27 |
28 | this.getSocket().on('disconnect', function() {
29 | view.mask('UNREACHABLE BACKEND');
30 | });
31 |
32 | this.getSocket().on('servers.update', function(servers) {
33 | var store = Ext.StoreMgr.lookup('Servers'),
34 | serversData = [],
35 | serversToRemove = [],
36 | issuesStore = Ext.StoreMgr.lookup('Issues'),
37 | issuesMapSeries = Cache.getIssuesMap().get('issues');
38 |
39 | if (store.isLoaded()) {
40 | serversToRemove = _.difference(_.keys(Cache.getServers()), _.keys(servers));
41 |
42 | _.each(serversToRemove, function(server) {
43 | delete Cache.config.issues[server];
44 | delete Cache.config.issuesMapData[server];
45 | });
46 |
47 | Functions.toastShow('SERVERS CONFIGURATION UPDATED!', '#D94141', '#FFF');
48 | }
49 |
50 | Cache.setServers(servers);
51 |
52 | _.each(servers, function(v,k) {
53 | serversData.push({
54 | server : k,
55 | user : v.user,
56 | psw : v.psw
57 | });
58 | });
59 |
60 | store.getProxy().setData(serversData);
61 | store.reload();
62 |
63 | if (_.isEmpty(servers)) {
64 | issuesStore.getProxy().setData([]);
65 | issuesStore.reload();
66 |
67 | issuesMapSeries.setData([]);
68 | }
69 | });
70 |
71 | this.getSocket().on('trigger.get.issues', function(data) {
72 | var store = Ext.StoreMgr.lookup('Issues'),
73 | issues = [];
74 |
75 | // console.log('[UPDATE] ' + data.server + ': issues');
76 | Cache.config.issues[data.server] = JSON.parse(data.val);
77 |
78 | _.each(Cache.getIssues(), function(v,k) {
79 | issues = issues.concat(v);
80 | });
81 |
82 | store.getProxy().setData(issues);
83 | store.reload();
84 | });
85 |
86 | this.getSocket().on('trigger.get.issues.map', function(data) {
87 | var issuesMapData = [];
88 |
89 | // console.log('[UPDATE] ' + data.server + ': issues.map');
90 | Cache.config.issuesMapData[data.server] = JSON.parse(data.val);
91 |
92 | _.each(Cache.getIssuesMapData(), function(v,k) {
93 | issuesMapData = issuesMapData.concat(v);
94 | });
95 |
96 | Cache.getIssuesMap().get('issues').setData(issuesMapData);
97 | });
98 | },
99 |
100 | updateServers: function(recs) {
101 | var servers = {};
102 |
103 | _.each(recs, function(rec) {
104 | servers[rec.data.server] = {
105 | server : rec.data.server,
106 | user : rec.data.user,
107 | psw : rec.data.psw
108 | };
109 | });
110 |
111 | this.getSocket().emit('servers.set', servers);
112 | }
113 | });
--------------------------------------------------------------------------------
/.sencha/app/refresh-impl.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
31 |
32 |
33 |
34 |
35 |
36 |
40 |
41 |
48 |
49 |
50 |
51 |
52 |
56 |
57 |
58 | /**
59 | * This file is generated by Sencha Cmd and should NOT be edited. It is a
60 | * combination of content from app.json, and all required package's package.json
61 | * files. Customizations should be placed in app.json.
62 | */
63 |
64 |
65 |
73 |
74 |
75 |
76 |
80 |
84 |
85 |
88 |
89 |
90 |
91 |
--------------------------------------------------------------------------------
/.sencha/app/js-impl.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
8 |
12 |
13 |
14 |
17 |
18 |
19 |
20 |
21 |
22 |
56 |
57 |
58 |
59 |
60 |
76 |
77 |
78 |
79 |
80 |
81 |
84 |
85 |
86 |
87 |
--------------------------------------------------------------------------------
/app/view/issuesGrid/IssuesGrid.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Last ISSUES GRID class
3 | */
4 |
5 | Ext.define('OctoZab.view.issuesGrid.IssuesGrid', {
6 | extend: 'Ext.grid.Panel',
7 |
8 | xtype: 'grid-issues',
9 | controller: 'issuesGrid',
10 |
11 | store: 'Issues',
12 |
13 | title: 'ISSUES DETAILS',
14 | columnLines: false,
15 | rowLines: false,
16 | reserveScrollbar: true,
17 | sortableColumns: false,
18 |
19 | plugins: 'gridfilters',
20 |
21 | features: [{
22 | ftype: 'grouping',
23 | disabled: true,
24 | groupHeaderTpl: '{name} ({children.length} issues)'
25 | }],
26 |
27 | viewConfig: {
28 | getRowClass: function(record) {
29 | if (record.data.acknowledged === true) {
30 | return 'acknowledged-row';
31 | }
32 |
33 | switch (record.data.priority) {
34 | case "0": return 'not-classified-row';
35 | case "1": return 'info-row';
36 | case "2": return 'warning-row';
37 | case "3": return 'average-row';
38 | case "4": return 'high-row';
39 | case "5": return 'disaster-row';
40 | default : return 'not-classified-row';
41 | }
42 | }
43 | },
44 |
45 | columns: {
46 | defaults: {
47 | sortable: false
48 | },
49 | items: [{
50 | text: 'LAST CHANGE',
51 | dataIndex: 'lastchange',
52 | align: 'center',
53 | width: 145,
54 | xtype: 'datecolumn',
55 | // format: 'd/m/y H:i:s',
56 | filter: true,
57 | renderer: function(value, metaData) {
58 | metaData.style = 'cursor: pointer;';
59 | metaData.tdAttr = 'title="Click for issue details"';
60 |
61 | return Ext.Date.format(value, "d/m/y H:i:s");
62 | }
63 | },{
64 | text: 'AGE',
65 | dataIndex: 'age',
66 | width: 100
67 | },{
68 | text: 'ZABBIX SERVER',
69 | dataIndex: 'server',
70 | align: 'center',
71 | flex: 1,
72 | filter: 'string',
73 | renderer: function(value, metaData) {
74 | metaData.style = 'cursor: pointer;';
75 | metaData.tdAttr = 'title="Click for Zabbix server overview"';
76 |
77 | return value;
78 | }
79 | },{
80 | text: 'HOSTNAME',
81 | dataIndex: 'hostname',
82 | flex: 2,
83 | filter: 'string',
84 | renderer: function(value, metaData) {
85 | metaData.style = 'cursor: pointer;';
86 | metaData.tdAttr = 'title="Click for latest host data"';
87 |
88 | return value;
89 | }
90 | },{
91 | text: 'TRIGGER',
92 | dataIndex: 'description',
93 | flex: 3,
94 | renderer: function(value, metaData) {
95 | metaData.style = 'cursor: pointer;';
96 | metaData.tdAttr = 'title="Click for issue details"';
97 |
98 | return value;
99 | }
100 | },{
101 | text: 'ACK',
102 | dataIndex: 'acknowledged',
103 | align: 'center',
104 | width: 65,
105 | xtype: 'booleancolumn',
106 | renderer: function(value, metaData) {
107 | return (value) ? 'Yes' : 'No';
108 | }
109 | },{
110 | text: 'SEVERITY',
111 | dataIndex: 'priority',
112 | align: 'center',
113 | width: 100,
114 | renderer: function(value, metaData, record) {
115 | switch (value) {
116 | case "0":
117 | metaData.tdCls = 'not-classified-cell'; return "Not classified";
118 | case "1":
119 | metaData.tdCls = 'info-cell'; return "Info";
120 | case "2":
121 | metaData.tdCls = 'warning-cell'; return "Warning";
122 | case "3":
123 | metaData.tdCls = 'average-cell'; return "Average";
124 | case "4":
125 | metaData.tdCls = 'high-cell'; return "High";
126 | case "5":
127 | metaData.tdCls = 'disaster-cell'; return "Disaster";
128 | default:
129 | return value;
130 | }
131 | },
132 | filter: {
133 | type: 'list',
134 | options: [
135 | [ '0', "Not classified" ],
136 | [ '1', "Info" ],
137 | [ '2', "Warning" ],
138 | [ '3', "Average" ],
139 | [ '4', "High" ],
140 | [ '5', "Disaster" ]
141 | ]
142 | }
143 | }]
144 | }
145 | });
--------------------------------------------------------------------------------
/.sencha/app/resolve-impl.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
25 |
26 |
27 |
28 |
32 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
57 |
58 |
60 |
61 |
62 |
70 |
71 |
72 |
73 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
104 |
105 |
106 |
107 |
108 |
109 |
--------------------------------------------------------------------------------
/app/view/main/MainController.js:
--------------------------------------------------------------------------------
1 | /**
2 | * This class is the main view for the application. It is specified in app.js as the
3 | * "autoCreateViewport" property. That setting automatically applies the "viewport"
4 | * plugin to promote that instance of this class to the body element.
5 | *
6 | * TODO - Replace this content of this view to suite the needs of your application.
7 | */
8 | Ext.define('OctoZab.view.main.MainController', {
9 | extend: 'Ext.app.ViewController',
10 |
11 | alias: 'controller.main',
12 |
13 | control: {
14 | '#': {
15 | afterrender: 'onAfterRender'
16 | },
17 | "#logo-text": {
18 | move: 'onLogoMove'
19 | },
20 | "#panel-issues-map": {
21 | boxready: 'onIssuesMapRender',
22 | resize: 'onIssuesMapResize'
23 | }
24 | },
25 |
26 | onAfterRender: function(view) {
27 | setTimeout(function() {
28 | if (Socket.getSocket() === null) {
29 | view.mask('UNREACHABLE BACKEND');
30 | }
31 | }, 1000);
32 | },
33 |
34 | onLogoMove: function(view) {
35 | view.alignTo(this.getView().getEl(), 'bl', [19,-155]);
36 | },
37 |
38 | onIssuesMapRender: function(panel) {
39 | Cache.setIssuesMap(new Highcharts.Chart({
40 | chart: {
41 | renderTo: panel.body.dom, backgroundColor: '#505359', spacing: [18, 60, 18, 60]
42 | },
43 | title: { text: null },
44 | credits: { enabled: false },
45 | noData: {
46 | style: { fontSize: '12px', color: '#FFF' }
47 | },
48 | // navigation: { buttonOptions: { symbolStroke: '#F3F3F3', // symbolY: 45, theme: { fill: "#7E878C", states: { hover: { stroke: '#40484C', fill: '#40484C' }, select: { stroke: '#40484C', fill: '#40484C' } } } } },
49 | legend: {
50 | layout: 'vertical', align: 'right', verticalAlign: 'bottom',
51 | itemMarginTop: 1, itemMarginBottom: 1, itemHoverStyle: null, // itemHiddenStyle: null,
52 | itemStyle: { color: "#FFF", fontWeight: 'bold', fontSize: '11px' }
53 | },
54 | colorAxis: {
55 | dataClasses: [
56 | { color: '#DDD', name: 'NOT CLASSIFIED' , from: 0, to: 0},
57 | { color: '#BFF', name: 'INFORMATION' , from: 1, to: 1},
58 | { color: '#FF8', name: 'WARNING' , from: 2, to: 2},
59 | { color: '#FA6', name: 'AVERAGE' , from: 3, to: 3},
60 | { color: '#F77', name: 'HIGH' , from: 4, to: 4},
61 | { color: '#F11', name: 'DISASTER' , from: 5, to: 5}
62 | ]
63 | },
64 | xAxis: {
65 | events: {
66 | afterSetExtremes: function(e) {
67 | if ((e.min === 0) && (e.max === 100)) {
68 | var mainPanel = Ext.ComponentQuery.query('app-main')[0];
69 |
70 | Cache.getIssuesServerFilter().setValue("");
71 | Cache.getIssuesPriorityFilter().setValue(['0','1','2','3','4','5']);
72 |
73 | mainPanel.getViewModel().set('issuesMapView', 'ALL');
74 | mainPanel.down('grid-issues').filters.clearFilters();
75 |
76 | Ext.StoreMgr.lookup('Issues').reload();
77 | }
78 | }
79 | }
80 | },
81 | tooltip: {
82 | pointFormat: "{point.node.val} issues",
83 | backgroundColor: "#505459",
84 | borderColor: "#505459",
85 | borderRadius: 0,
86 | style: {color: "#FFF" }
87 | },
88 | series: [{
89 | id: 'issues', type: "treemap", layoutAlgorithm: 'sliceAndDice',
90 | allowDrillToNode: true, levelIsConstant: false,
91 | borderColor: '#505459', cursor: 'pointer',
92 | dataLabels: { enabled: false },
93 | drillUpButton: {
94 | position: { align: 'right', x: 65, y: 5 },
95 | theme: {
96 | fill: "#E6E6E6", stroke: "#E6E6E6", r: 1,
97 | states: {
98 | hover: { fill: "#CCC", stroke: "#CCC" }
99 | }
100 | }
101 | },
102 | levels: [{
103 | level: 1, borderWidth: 10,
104 | dataLabels: { enabled: true, style: { fontSize: '16px' } }
105 | },{
106 | level: 2, borderColor: 'transparent'
107 | }],
108 | events: {
109 | click: function(e) {
110 | var mainPanel = Ext.ComponentQuery.query('app-main')[0];
111 |
112 | if (!this.rootNode) {
113 | Cache.getIssuesServerFilter().setValue(e.point.id);
114 | Cache.getIssuesPriorityFilter().setValue(['0','1','2','3','4','5']);
115 |
116 | mainPanel.getViewModel().set('issuesMapView', e.point.name);
117 | mainPanel.down('grid-issues').filters.clearFilters();
118 |
119 | Ext.StoreMgr.lookup('Issues').reload();
120 | } else {
121 | Cache.getIssuesPriorityFilter().setValue([e.point.colorValue.toString()]);
122 |
123 | mainPanel.getViewModel().set('issuesMapView', e.point.parent.toUpperCase() + ' / severity: ' + e.point.name.toUpperCase());
124 | mainPanel.down('grid-issues').filters.clearFilters();
125 |
126 | Ext.StoreMgr.lookup('Issues').reload();
127 | }
128 | }
129 | }
130 | }]
131 | }));
132 | },
133 |
134 | onIssuesMapResize: function(panel, width, height) {
135 | Cache.getIssuesMap().setSize(width, height-36);
136 | }
137 | });
138 |
--------------------------------------------------------------------------------
/.sencha/app/packager-impl.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Running mobile packager action @{action} on file @{configFile}
7 |
8 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
32 |
36 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
51 |
52 |
54 |
55 |
59 |
61 |
63 |
65 |
66 |
67 |
73 |
74 |
75 |
76 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 | Running default mobile packager config.
107 |
109 |
110 |
111 | Building all specified mobile packager configs.
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
124 |
125 |
126 |
127 |
--------------------------------------------------------------------------------
/Readme.md:
--------------------------------------------------------------------------------
1 | # OctoZab
2 |
3 | 
4 |
5 | This application is a backend/frontend system to remotely control multiple Zabbix servers (http://www.zabbix.com/).
6 |
7 | Demo: [here](http://demo.quadrata.it/octozab/)
8 |
9 |
10 | ## Features
11 |
12 | - Issues list sorted by Severity -> Last Change
13 | - Issues treemap overview, with drilldown per server
14 | - Dynamic filtering on issues list through interaction with treemap objects
15 | - Redirect to issue details from issues list
16 | - NEW! Zabbix servers configuration management, ability to add, remove and choose credentials!
17 | - NEW! Notification to all connected users on configuration changes (like the servers one)
18 |
19 |
20 | ## Dependencies
21 |
22 | - [NodeJS](http://nodejs.org/) tested >= 0.12.6
23 | - [Redis](http://redis.io/) tested >= 3.0.2
24 |
25 |
26 | ## How does it work
27 |
28 | Backend is made by a NodeJS server, that operates in order to authenticate Zabbix servers and communicate with APIs they expose. Collected data is then stored on Redis DB, that caches it. Redis keys are monitored by the server in order to send changes back to client frontends whenever they occur. Clients are connected to server through WebSockets.
29 |
30 | ## Docker Container
31 |
32 | An easy and fast way to test the application is using our Docker container. Install [docker](https://docs.docker.com/) on your machine, pull the image and run it:
33 |
34 | ```shell
35 | docker pull quadrata/octozab
36 | docker run -it -d -p $frontendHostPort:80 -p $backendHostPort:8080 --name="octoZab" quadrata/octozab
37 | ```
38 |
39 | where $frontendHostPort and $backendHostPort are ports on your host where you desire that frontend and backend respectively should run. Now you have to configure the container. Attach to it and start necessary services:
40 |
41 | ```shell
42 | docker attach octoZab
43 | ```
44 |
45 | ```shell
46 | service httpd start
47 | service octozab-redis start
48 | service octozab-node start
49 | ```
50 |
51 | Modify frontend `"/var/www/html/octozab/config.js"` with your host url and host backend port you defined above at container run.
52 |
53 | IMPORTANT! When we speak about host, it is meant host, not the container. So above with $frontendHostPort and $backendHostPort we were referring to host ports on which are mapped container ports. And with host url in "config.js" we mean the url on which you access your host, not the container.
54 |
55 | Then detach from container with Ctrl+P-Ctrl+Q.
56 |
57 |
58 | That's it! Now you should be able to connect to application browsing at `http://yourHostUrl:yourHostFrontendPort/octozab` (if you mapped container port 80, webserver one, with port 80 on your host, you can omit the `:yourHostFrontendPort` url part).
59 |
60 |
61 | ## Installation
62 |
63 | In order to test the application, first step is to install dependencies listed above. Once `node`, `npm`, `redis-server` and `redis-cli` executables are installed on server, you can start deploying both backend and frontend by cloning repository on webserver root.
64 |
65 | If you prefer to deploy backend and frontend on different machines, copy only `"backend"` folder on backend machine, and the rest of the repository on frontend machine. Then modify in the file `"config.js"` the `backendUrl` parameter with backend domain name. Be sure port `8080` is exposed to frontend from backend.
66 |
67 | Install NodeJS module dependencies. Move from command-line to backend folder and run following command:
68 |
69 | ```shell
70 | npm install
71 | ```
72 |
73 | Frontend is based on ExtJS framework. In order to make the source code work, you have first to install SenchaCmd tool version [5.0.0.160](http://cdn.sencha.com/cmd/5.0.0.160/SenchaCmd-5.0.0.160-linux-x64.run.zip) (link is for Linux 64bit version). Check SenchaCmd dependencies [here](http://docs.sencha.com/cmd/5.x/intro_to_cmd.html#System_Setup) (Java >=1.7, Ruby >=2.0.0, Compass).
74 | Then from command-line move to frontend folder, and give the following commands:
75 |
76 | ```shell
77 | sencha app upgrade
78 | sencha app build
79 | ```
80 |
81 | Now run backend services. The simplest way is to open separate shells for Redis DB:
82 |
83 | ```shell
84 | redis-server $backend/redis.conf
85 | ```
86 |
87 | and NodeJS server:
88 |
89 | ```shell
90 | node $backend/server.js
91 | ```
92 |
93 | where `$backend` is the path to `"backend"` folder deployed before.
94 |
95 |
96 | ## Usage
97 |
98 | To see OctoZab in action, it's enough to connect to frontend, based on where you deployed frontend source code. To edit a Zabbix server details (url, user, psw, ...) from related settings, __double click on corresponding row__ and you'll have a row editor right on the line.
99 |
100 | If you're not using Docker container and you want to have a production release of it, use the SenchaCmd tool. Move from command-line to frontend folder, and run:
101 |
102 | ```shell
103 | sencha app build
104 | ```
105 |
106 | You will find the production release under `"$frontend/build/production/OctoZab"` (`$frontend` is the path where frontend is deployed).
107 |
108 |
109 | ## Roadmap
110 |
111 | - ~~Allow to specify credentials for each Zabbix server~~
112 | - ~~Add, remove, modify Zabbix servers configuration from frontend~~
113 | - Replace treemap overview with some other way/chart that gives a better understanding of each Zabbix server healthy (give us your feedback on this)
114 | - Many more Zabbix features based on audience feedback
115 |
116 |
117 | ## License
118 |
119 | GPL v2
--------------------------------------------------------------------------------
/.sencha/app/microloader/development.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Sencha Blink - Development
3 | * @author Jacky Nguyen
4 | */
5 | (function() {
6 | var head = document.head || document.getElementsByTagName('head')[0];
7 |
8 | function write(content) {
9 | document.write(content);
10 | }
11 |
12 | function addMeta(name, content) {
13 | var meta = document.createElement('meta');
14 |
15 | meta.setAttribute('name', name);
16 | meta.setAttribute('content', content);
17 | head.appendChild(meta);
18 | }
19 |
20 | var xhr = new XMLHttpRequest();
21 | xhr.open('GET', 'bootstrap.json', false);
22 | xhr.send(null);
23 |
24 | var options = eval("(" + xhr.responseText + ")"),
25 | scripts = options.js || [],
26 | styleSheets = options.css || [],
27 | i, ln, path, platform, theme, exclude;
28 |
29 | if(options.platform && options.platforms && options.platforms[options.platform] && options.platforms[options.platform].js) {
30 | scripts = options.platforms[options.platform].js.concat(scripts);
31 | }
32 |
33 | if (navigator.userAgent.match(/IEMobile\/10\.0/)) {
34 | var msViewportStyle = document.createElement("style");
35 | msViewportStyle.appendChild(
36 | document.createTextNode(
37 | "@media screen and (orientation: portrait) {" +
38 | "@-ms-viewport {width: 320px !important;}" +
39 | "}" +
40 | "@media screen and (orientation: landscape) {" +
41 | "@-ms-viewport {width: 560px !important;}" +
42 | "}"
43 | )
44 | );
45 | document.getElementsByTagName("head")[0].appendChild(msViewportStyle);
46 | }
47 |
48 | addMeta('viewport', 'width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no');
49 | addMeta('apple-mobile-web-app-capable', 'yes');
50 | addMeta('apple-touch-fullscreen', 'yes');
51 |
52 | if (!window.Ext) {
53 | window.Ext = {};
54 | }
55 | Ext.microloaded = true;
56 |
57 | var filterPlatform = window.Ext.filterPlatform = function(platform) {
58 | var profileMatch = false,
59 | ua = navigator.userAgent,
60 | j, jln;
61 |
62 | platform = [].concat(platform);
63 |
64 | function isPhone(ua) {
65 | var isMobile = /Mobile(\/|\s)/.test(ua);
66 |
67 | // Either:
68 | // - iOS but not iPad
69 | // - Android 2
70 | // - Android with "Mobile" in the UA
71 |
72 | return /(iPhone|iPod)/.test(ua) ||
73 | (!/(Silk)/.test(ua) && (/(Android)/.test(ua) && (/(Android 2)/.test(ua) || isMobile))) ||
74 | (/(BlackBerry|BB)/.test(ua) && isMobile) ||
75 | /(Windows Phone)/.test(ua);
76 | }
77 |
78 | function isTablet(ua) {
79 | return !isPhone(ua) && (/iPad/.test(ua) || /Android|Silk/.test(ua) || /(RIM Tablet OS)/.test(ua) ||
80 | (/MSIE 10/.test(ua) && /; Touch/.test(ua)));
81 | }
82 |
83 | // Check if the ?platform parameter is set in the URL
84 | var paramsString = window.location.search.substr(1),
85 | paramsArray = paramsString.split("&"),
86 | params = {},
87 | testPlatform, i;
88 |
89 | for (i = 0; i < paramsArray.length; i++) {
90 | var tmpArray = paramsArray[i].split("=");
91 | params[tmpArray[0]] = tmpArray[1];
92 | }
93 |
94 | testPlatform = params.platform;
95 | if (testPlatform) {
96 | return platform.indexOf(testPlatform) != -1;
97 | }
98 |
99 | for (j = 0, jln = platform.length; j < jln; j++) {
100 | switch (platform[j]) {
101 | case 'phone':
102 | profileMatch = isPhone(ua);
103 | break;
104 | case 'tablet':
105 | profileMatch = isTablet(ua);
106 | break;
107 | case 'desktop':
108 | profileMatch = !isPhone(ua) && !isTablet(ua);
109 | break;
110 | case 'ios':
111 | profileMatch = /(iPad|iPhone|iPod)/.test(ua);
112 | break;
113 | case 'android':
114 | profileMatch = /(Android|Silk)/.test(ua);
115 | break;
116 | case 'blackberry':
117 | profileMatch = /(BlackBerry|BB)/.test(ua);
118 | break;
119 | case 'safari':
120 | profileMatch = /Safari/.test(ua) && !(/(BlackBerry|BB)/.test(ua));
121 | break;
122 | case 'chrome':
123 | profileMatch = /Chrome/.test(ua);
124 | break;
125 | case 'ie10':
126 | profileMatch = /MSIE 10/.test(ua);
127 | break;
128 | case 'windows':
129 | profileMatch = /MSIE 10/.test(ua) || /Trident/.test(ua);
130 | break;
131 | case 'tizen':
132 | profileMatch = /Tizen/.test(ua);
133 | break;
134 | case 'firefox':
135 | profileMatch = /Firefox/.test(ua);
136 | }
137 | if (profileMatch) {
138 | return true;
139 | }
140 | }
141 | return false;
142 | };
143 |
144 |
145 | for (i = 0,ln = styleSheets.length; i < ln; i++) {
146 | path = styleSheets[i];
147 |
148 | if (typeof path != 'string') {
149 | platform = path.platform;
150 | exclude = path.exclude;
151 | theme = path.theme;
152 | path = path.path;
153 | }
154 |
155 | if (platform) {
156 | if (!filterPlatform(platform) || filterPlatform(exclude)) {
157 | continue;
158 | }
159 |
160 | if(!Ext.theme) {
161 | Ext.theme = {};
162 | }
163 | if(!Ext.theme.name) {
164 | Ext.theme.name = theme || 'Default';
165 | }
166 | }
167 |
168 | write('');
169 | }
170 |
171 | for (i = 0,ln = scripts.length; i < ln; i++) {
172 | path = scripts[i];
173 |
174 | if (typeof path != 'string') {
175 | platform = path.platform;
176 | exclude = path.exclude;
177 | path = path.path;
178 | }
179 |
180 | if (platform) {
181 | if (!filterPlatform(platform) || filterPlatform(exclude)) {
182 | continue;
183 | }
184 | }
185 |
186 | write('
41 |
42 |
43 |
44 |
46 |
47 |
48 |
49 |
54 |
55 |
56 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
101 |
102 |
103 |
110 |
111 |
112 |
116 |
119 |
120 |
121 |
122 |
123 |
124 |
134 |
135 |
136 |
150 |
151 |
152 |
161 |
162 |
163 |
164 |
165 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
208 |
236 |
237 |
238 |
239 |
240 |
241 |
254 |
255 |
256 |
257 |
--------------------------------------------------------------------------------
/.sencha/app/sass-impl.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
11 |
12 |
13 |
16 |
17 |
18 |
19 |
20 |
21 | include
22 | -all
23 |
24 |
25 |
26 |
27 | restore
28 | page
29 |
30 |
31 |
32 |
33 |
34 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
81 |
86 |
87 |
88 |
89 |
97 |
98 |
99 |
100 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
127 |
128 |
129 |
130 | Preprocessing @{cssfile} to ${css.output.name}
131 |
135 |
136 |
137 |
138 |
139 |
140 | Compressing @{cssfile} to ${css.output.name}
141 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
169 |
170 |
174 |
175 |
176 |
177 |
178 |
179 |
184 |
185 |
186 |
187 |
188 |
189 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
202 |
203 |
204 |
205 |
206 | Compiling sass directory : @{theme}/sass
207 |
213 |
214 |
216 |
217 |
218 |
219 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
240 |
241 |
242 |
248 |
249 |
250 |
251 |
255 |
259 |
260 |
263 |
264 |
265 |
266 |
--------------------------------------------------------------------------------
/.sencha/app/page-impl.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
16 |
17 |
18 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
65 |
66 |
67 |
68 |
69 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
120 |
121 |
122 |
123 |
124 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
176 |
177 |
178 |
179 |
180 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
242 |
243 |
246 |
247 |
248 |
249 |
250 |
251 |
256 |
257 |
--------------------------------------------------------------------------------
/resources/js/highcharts-4.1.7-treemap.min.js:
--------------------------------------------------------------------------------
1 | /*
2 | Highcharts JS v4.1.7 (2015-06-26)
3 |
4 | (c) 2014 Highsoft AS
5 | Authors: Jon Arild Nygard / Oystein Moseng
6 |
7 | License: www.highcharts.com/license
8 | */
9 | (function(g){var i=g.seriesTypes,m=g.merge,s=g.extend,t=g.extendClass,u=g.getOptions().plotOptions,v=function(){},j=g.each,x=HighchartsAdapter.grep,n=g.pick,q=g.Series,r=g.Color;u.treemap=m(u.scatter,{showInLegend:!1,marker:!1,borderColor:"#E0E0E0",borderWidth:1,dataLabels:{enabled:!0,defer:!1,verticalAlign:"middle",formatter:function(){return this.point.name||this.point.id},inside:!0},tooltip:{headerFormat:"",pointFormat:"{point.name}: {point.node.val}
"},layoutAlgorithm:"sliceAndDice",
10 | layoutStartingDirection:"vertical",alternateStartingDirection:!1,levelIsConstant:!0,states:{hover:{borderColor:"#A0A0A0",brightness:i.heatmap?0:0.1,shadow:!1}},drillUpButton:{position:{align:"left",x:10,y:-50}}});i.treemap=t(i.scatter,m({pointAttrToOptions:{stroke:"borderColor","stroke-width":"borderWidth",fill:"color",dashstyle:"borderDashStyle"},pointArrayMap:["value"],axisTypes:i.heatmap?["xAxis","yAxis","colorAxis"]:["xAxis","yAxis"],optionalAxis:"colorAxis",getSymbol:v,parallelArrays:["x","y",
11 | "value","colorValue"],colorKey:"colorValue",translateColors:i.heatmap&&i.heatmap.prototype.translateColors},{type:"treemap",trackerGroups:["group","dataLabelsGroup"],pointClass:t(g.Point,{setState:function(a,b){g.Point.prototype.setState.call(this,a,b);a==="hover"?this.dataLabel&&this.dataLabel.attr({zIndex:1002}):this.dataLabel&&this.dataLabel.attr({zIndex:this.pointAttr[""].zIndex+1})},setVisible:i.pie.prototype.pointClass.prototype.setVisible}),handleLayout:function(){var a=this.tree,b;if(this.points.length)this.rootNode=
12 | n(this.rootNode,""),a=this.tree=this.getTree(),this.levelMap=this.getLevels(),b=this.getSeriesArea(a.val),this.calculateChildrenAreas(a,b),this.setPointValues()},getTree:function(){var a,b=[],c=[],d=function(a){j(b[a],function(a){b[""].push(a)})};this.nodeMap=[];j(this.points,function(a,d){var h="";c.push(a.id);if(a.parent!==void 0)h=a.parent;b[h]===void 0&&(b[h]=[]);b[h].push(d)});for(a in b)b.hasOwnProperty(a)&&a!==""&&HighchartsAdapter.inArray(a,c)===-1&&(d(a),delete b[a]);a=this.buildNode("",
13 | -1,0,b,null);this.eachParents(this.nodeMap[this.rootNode],function(a){a.visible=!0});this.eachChildren(this.nodeMap[this.rootNode],function(a){a.visible=!0});this.setTreeValues(a);return a},buildNode:function(a,b,c,d,e){var f=this,h=[],w=f.points[b],g;j(d[a]||[],function(b){g=f.buildNode(f.points[b].id,b,c+1,d,a);h.push(g)});b={id:a,i:b,children:h,level:c,parent:e,visible:!1};f.nodeMap[b.id]=b;if(w)w.node=b;return b},setTreeValues:function(a){var b=this,c=0,d=[],e,f=b.points[a.i];j(a.children,function(a){a=
14 | b.setTreeValues(a);b.insertElementSorted(d,a,function(a,b){return a.val>b.val});a.ignore?b.eachChildren(a,function(a){s(a,{ignore:!0,isLeaf:!1,visible:!1})}):c+=a.val});e=n(f&&f.value,c);s(a,{children:d,childrenTotal:c,ignore:!(n(f&&f.visible,!0)&&e>0),isLeaf:a.visible&&!c,name:n(f&&f.name,""),val:e});return a},eachChildren:function(a,b){var c=this,d=a.children;b(a);d.length&&j(d,function(a){c.eachChildren(a,b)})},eachParents:function(a,b){var c=this.nodeMap[a.parent];b(a);c&&this.eachParents(c,b)},
15 | calculateChildrenAreas:function(a,b){var c=this,d=c.options,e=d.levelIsConstant?a.level:a.level-this.nodeMap[this.rootNode].level,f=this.levelMap[e+1],h=n(c[f&&f.layoutAlgorithm]&&f.layoutAlgorithm,d.layoutAlgorithm),g=d.alternateStartingDirection,p=[],k,d=x(a.children,function(a){return!a.ignore});if(f&&f.layoutStartingDirection)b.direction=f.layoutStartingDirection==="vertical"?0:1;p=c[h](b,d);j(d,function(a,d){k=c.points[a.i];k.level=e+1;a.values=m(p[d],{val:a.childrenTotal,direction:g?1-b.direction:
16 | b.direction});a.children.length&&c.calculateChildrenAreas(a,a.values)})},setPointValues:function(){var a=this,b=a.xAxis,c=a.yAxis;a.nodeMap[""].values={x:0,y:0,width:100,height:100};j(a.points,function(d){var e=d.node.values,f,h,g;e?(e.x/=a.axisRatio,e.width/=a.axisRatio,f=Math.round(b.translate(e.x,0,0,0,1)),h=Math.round(b.translate(e.x+e.width,0,0,0,1)),g=Math.round(c.translate(e.y,0,0,0,1)),e=Math.round(c.translate(e.y+e.height,0,0,0,1)),d.shapeType="rect",d.shapeArgs={x:Math.min(f,h),y:Math.min(g,
17 | e),width:Math.abs(h-f),height:Math.abs(e-g)},d.plotX=d.shapeArgs.x+d.shapeArgs.width/2,d.plotY=d.shapeArgs.y+d.shapeArgs.height/2):(delete d.plotX,delete d.plotY)})},getSeriesArea:function(a){var b=this.options.layoutStartingDirection==="vertical"?0:1,a={x:0,y:0,width:100*(this.axisRatio=this.xAxis.len/this.yAxis.len),height:100,direction:b,val:a};return this.nodeMap[""].values=a},getLevels:function(){var a=[],b=this.options.levels;b&&j(b,function(b){b.level!==void 0&&(a[b.level]=b)});return a},setColorRecursive:function(a,
18 | b){var c=this,d,e;if(a){d=c.points[a.i];e=c.levelMap[a.level];b=n(d&&d.options.color,e&&e.color,b);if(d)d.color=b;a.children.length&&j(a.children,function(a){c.setColorRecursive(a,b)})}},alg_func_group:function(a,b,c,d){this.height=a;this.width=b;this.plot=d;this.startDirection=this.direction=c;this.lH=this.nH=this.lW=this.nW=this.total=0;this.elArr=[];this.lP={total:0,lH:0,nH:0,lW:0,nW:0,nR:0,lR:0,aspectRatio:function(a,b){return Math.max(a/b,b/a)}};this.addElement=function(a){this.lP.total=this.elArr[this.elArr.length-
19 | 1];this.total+=a;this.direction===0?(this.lW=this.nW,this.lP.lH=this.lP.total/this.lW,this.lP.lR=this.lP.aspectRatio(this.lW,this.lP.lH),this.nW=this.total/this.height,this.lP.nH=this.lP.total/this.nW,this.lP.nR=this.lP.aspectRatio(this.nW,this.lP.nH)):(this.lH=this.nH,this.lP.lW=this.lP.total/this.lH,this.lP.lR=this.lP.aspectRatio(this.lP.lW,this.lH),this.nH=this.total/this.width,this.lP.nW=this.lP.total/this.nH,this.lP.nR=this.lP.aspectRatio(this.lP.nW,this.nH));this.elArr.push(a)};this.reset=function(){this.lW=
20 | this.nW=0;this.elArr=[];this.total=0}},alg_func_calcPoints:function(a,b,c,d){var e,f,h,g,p=c.lW,k=c.lH,l=c.plot,i,o=0,n=c.elArr.length-1;b?(p=c.nW,k=c.nH):i=c.elArr[c.elArr.length-1];j(c.elArr,function(a){if(b||ok.lP.lR&&e.alg_func_calcPoints(a,!1,k,d,h);g===i&&e.alg_func_calcPoints(a,!0,k,d,h);g+=1});return d},alg_func_fill:function(a,b,c){var d=[],e,f=b.direction,h=b.x,g=b.y,i=b.width,k=b.height,l,n,o,m;j(c,function(c){e=b.width*b.height*(c.val/
22 | b.val);l=h;n=g;f===0?(m=k,o=e/m,i-=o,h+=o):(o=i,m=e/o,k-=m,g+=m);d.push({x:l,y:n,width:o,height:m});a&&(f=1-f)});return d},strip:function(a,b){return this.alg_func_lowAspectRatio(!1,a,b)},squarified:function(a,b){return this.alg_func_lowAspectRatio(!0,a,b)},sliceAndDice:function(a,b){return this.alg_func_fill(!0,a,b)},stripes:function(a,b){return this.alg_func_fill(!1,a,b)},translate:function(){q.prototype.translate.call(this);this.handleLayout();this.colorAxis?this.translateColors():this.options.colorByPoint||
23 | this.setColorRecursive(this.tree,void 0)},drawDataLabels:function(){var a=this,b=a.dataLabelsGroup,c,d;j(a.points,function(b){d=a.levelMap[b.level];c={style:{}};if(!b.node.isLeaf)c.enabled=!1;if(d&&d.dataLabels)c=m(c,d.dataLabels),a._hasPointLabels=!0;if(b.shapeArgs)c.style.width=b.shapeArgs.width;b.dlOptions=m(c,b.options.dataLabels)});this.dataLabelsGroup=this.group;q.prototype.drawDataLabels.call(this);this.dataLabelsGroup=b},alignDataLabel:i.column.prototype.alignDataLabel,drawPoints:function(){var a=
24 | this,b=a.points,c=a.options,d,e,f;j(b,function(b){f=a.levelMap[b.level];d={stroke:c.borderColor,"stroke-width":c.borderWidth,dashstyle:c.borderDashStyle,r:0,fill:n(b.color,a.color)};if(f)d.stroke=f.borderColor||d.stroke,d["stroke-width"]=f.borderWidth||d["stroke-width"],d.dashstyle=f.borderDashStyle||d.dashstyle;d.stroke=b.borderColor||d.stroke;d["stroke-width"]=b.borderWidth||d["stroke-width"];d.dashstyle=b.borderDashStyle||d.dashstyle;d.zIndex=1E3-b.level*2;b.pointAttr=m(b.pointAttr);e=b.pointAttr.hover;
25 | e.zIndex=1001;e.fill=r(d.fill).brighten(c.states.hover.brightness).get();if(!b.node.isLeaf)n(c.interactByLeaf,!c.allowDrillToNode)?(d.fill="none",delete e.fill):(d.fill=r(d.fill).setOpacity(0.15).get(),e.fill=r(e.fill).setOpacity(0.75).get());if(b.node.level<=a.nodeMap[a.rootNode].level)d.fill="none",d.zIndex=0,delete e.fill;b.pointAttr[""]=g.extend(b.pointAttr[""],d);b.dataLabel&&b.dataLabel.attr({zIndex:b.pointAttr[""].zIndex+1})});i.column.prototype.drawPoints.call(this);j(b,function(a){a.graphic&&
26 | a.graphic.attr(a.pointAttr[""])});c.allowDrillToNode&&a.drillTo()},insertElementSorted:function(a,b,c){var d=0,e=!1;a.length!==0&&j(a,function(f){c(b,f)&&!e&&(a.splice(d,0,b),e=!0);d+=1});e||a.push(b)},drillTo:function(){var a=this;j(a.points,function(b){var c,d;g.removeEvent(b,"click.drillTo");b.graphic&&b.graphic.css({cursor:"default"});if(c=a.options.interactByLeaf?a.drillToByLeaf(b):a.drillToByGroup(b))d=a.nodeMap[a.rootNode].name||a.rootNode,b.graphic&&b.graphic.css({cursor:"pointer"}),g.addEvent(b,
27 | "click.drillTo",function(){b.setState("");a.drillToNode(c);a.showDrillUpButton(d)})})},drillToByGroup:function(a){var b=!1;if(a.node.level-this.nodeMap[this.rootNode].level===1&&!a.node.isLeaf)b=a.id;return b},drillToByLeaf:function(a){var b=!1;if(a.node.parent!==this.rootNode&&a.node.isLeaf)for(a=a.node;!b;)if(a=this.nodeMap[a.parent],a.parent===this.rootNode)b=a.id;return b},drillUp:function(){var a=null;this.rootNode&&(a=this.nodeMap[this.rootNode],a=a.parent!==null?this.nodeMap[a.parent]:this.nodeMap[""]);
28 | if(a!==null)this.drillToNode(a.id),a.id===""?this.drillUpButton=this.drillUpButton.destroy():(a=this.nodeMap[a.parent],this.showDrillUpButton(a.name||a.id))},drillToNode:function(a){var b=this.nodeMap[a].values;this.rootNode=a;this.xAxis.setExtremes(b.x,b.x+b.width,!1);this.yAxis.setExtremes(b.y,b.y+b.height,!1);this.isDirty=!0;this.chart.redraw()},showDrillUpButton:function(a){var b=this,a=a||"< Back",c=b.options.drillUpButton,d,e;if(c.text)a=c.text;this.drillUpButton?this.drillUpButton.attr({text:a}).align():
29 | (e=(d=c.theme)&&d.states,this.drillUpButton=this.chart.renderer.button(a,null,null,function(){b.drillUp()},d,e&&e.hover,e&&e.select).attr({align:c.position.align,zIndex:9}).add().align(c.position,!1,c.relativeTo||"plotBox"))},buildKDTree:v,drawLegendSymbol:g.LegendSymbolMixin.drawRectangle,getExtremes:function(){q.prototype.getExtremes.call(this,this.colorValueData);this.valueMin=this.dataMin;this.valueMax=this.dataMax;q.prototype.getExtremes.call(this)},getExtremesFromAll:!0,bindAxes:function(){var a=
30 | {endOnTick:!1,gridLineWidth:0,lineWidth:0,min:0,dataMin:0,minPadding:0,max:100,dataMax:100,maxPadding:0,startOnTick:!1,title:null,tickPositions:[]};q.prototype.bindAxes.call(this);g.extend(this.yAxis.options,a);g.extend(this.xAxis.options,a)}}))})(Highcharts);
31 |
--------------------------------------------------------------------------------
/.sencha/app/slice-impl.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 |
13 |
14 |
15 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
52 |
53 |
54 |
55 |
64 |
65 |
66 |
67 |
70 |
71 |
79 |
80 |
81 |
85 |
86 |
87 |
88 |
89 |
92 |
93 |
100 |
108 |
109 |
110 | Capture theme image to ${build.capture.png}
111 |
112 |
119 |
120 |
121 | Slicing theme images to ${build.resources.dir}
122 |
123 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 | Copying base framework images from ${framework.theme.dir} to ${tmp.img.dir}
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 | Building sass for theme ${theme.name}
171 |
172 |
173 |
174 |
175 |
176 | Slicing images for theme ${theme.name} to ${tmp.img.dir}
177 |
178 |
186 |
187 |
188 |
189 |
190 |
191 | Copying user defined images from @{theme}/images to ${tmp.img.dir}
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
205 |
206 |
207 |
208 |
210 |
211 |
212 |
215 |
216 |
217 |
219 |
220 |
221 |
222 |
223 |
227 |
228 | Processing theme directories from ${app.theme.dir}
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
--------------------------------------------------------------------------------
/.sencha/app/bootstrap-impl.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 | = 0) {
79 | project.setProperty('sencha.5.filters', 'true');
80 | } else {
81 | project.setProperty('sencha.5.filters', 'false');
82 | }
83 | } catch (err) {
84 | project.setProperty('sencha.5.filters', 'false');
85 | }
86 | ]]>
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
100 |
101 |
102 |
103 |
104 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 | include
117 | -namespace=Ext.ux
118 | and
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
261 |
262 |
267 |
268 |
272 |
273 |
277 |
278 |
282 |
283 |
286 |
287 | ${bootstrap.file.content}
288 | ${bootstrap.files.content}
289 | ${bootstrap.data.content}
290 | ${bootstrap.overrides.content}
291 | ${bootstrap.launch.content}
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
--------------------------------------------------------------------------------
/.sencha/app/Microloader.js:
--------------------------------------------------------------------------------
1 | // here, the extra check for window['Ext'] is needed for use with cmd-test
2 | // code injection. we need to make that this file will sync up with page global
3 | // scope to avoid duplicate Ext.Boot state. That check is after the initial Ext check
4 | // to allow the sandboxing template to inject an appropriate Ext var and prevent the
5 | // global detection.
6 | var Ext = Ext || window['Ext'] || {};
7 |
8 |
9 | //
10 | /**
11 | * @Class Ext.Microloader
12 | * @singleton
13 | */
14 | Ext.Microloader = Ext.Microloader || (function () {
15 | var apply = function (dest, src, defaults) {
16 | if (defaults) {
17 | apply(dest, defaults);
18 | }
19 |
20 | if (dest && src && typeof src == 'object') {
21 | for (var key in src) {
22 | dest[key] = src[key];
23 | }
24 | }
25 | return dest;
26 | },
27 | Boot = Ext.Boot,
28 | _listeners = [],
29 | _loaded = false,
30 | _tags = {},
31 | Microloader = {
32 |
33 | /**
34 | * the global map of tags used
35 | */
36 | platformTags: _tags,
37 |
38 | /**
39 | * The defult function that detects various platforms and sets tags
40 | * in the platform map accrodingly. Examples are iOS, android, tablet, etc.
41 | * @param tags the set of tags to populate
42 | */
43 | detectPlatformTags: function () {
44 | var ua = navigator.userAgent,
45 | isMobile = _tags.isMobile = /Mobile(\/|\s)/.test(ua),
46 | isPhone, isDesktop, isTablet, touchSupported, isIE10, isBlackberry,
47 | element = document.createElement('div'),
48 | uaTagChecks = [
49 | 'iPhone',
50 | 'iPod',
51 | 'Android',
52 | 'Silk',
53 | 'Android 2',
54 | 'BlackBerry',
55 | 'BB',
56 | 'iPad',
57 | 'RIM Tablet OS',
58 | 'MSIE 10',
59 | 'Trident',
60 | 'Chrome',
61 | 'Tizen',
62 | 'Firefox',
63 | 'Safari',
64 | 'Windows Phone'
65 | ],
66 | isEventSupported = function(name, tag) {
67 | if (tag === undefined) {
68 | tag = window;
69 | }
70 |
71 | var eventName = 'on' + name.toLowerCase(),
72 | isSupported = (eventName in element);
73 |
74 | if (!isSupported) {
75 | if (element.setAttribute && element.removeAttribute) {
76 | element.setAttribute(eventName, '');
77 | isSupported = typeof element[eventName] === 'function';
78 |
79 | if (typeof element[eventName] !== 'undefined') {
80 | element[eventName] = undefined;
81 | }
82 |
83 | element.removeAttribute(eventName);
84 | }
85 | }
86 |
87 | return isSupported;
88 | },
89 | uaTags = {},
90 | len = uaTagChecks.length, check, c;
91 |
92 | for (c = 0; c < len; c++) {
93 | check = uaTagChecks[c];
94 | uaTags[check] = new RegExp(check).test(ua);
95 | }
96 |
97 | isPhone =
98 | (uaTags.iPhone || uaTags.iPod) ||
99 | (!uaTags.Silk && (uaTags.Android && (uaTags['Android 2'] || isMobile))) ||
100 | ((uaTags.BlackBerry || uaTags.BB) && uaTags.isMobile) ||
101 | (uaTags['Windows Phone']);
102 |
103 | isTablet =
104 | (!_tags.isPhone) && (
105 | uaTags.iPad ||
106 | uaTags.Android ||
107 | uaTags.Silk ||
108 | uaTags['RIM Tablet OS'] ||
109 | (uaTags['MSIE 10'] && /; Touch/.test(ua))
110 | );
111 |
112 | touchSupported =
113 | // if the browser has touch events we can be reasonably sure the device has
114 | // a touch screen
115 | isEventSupported('touchend') ||
116 | // browsers that use pointer event have maxTouchPoints > 0 if the
117 | // device supports touch input
118 | // http://www.w3.org/TR/pointerevents/#widl-Navigator-maxTouchPoints
119 | navigator.maxTouchPoints ||
120 | // IE10 uses a vendor-prefixed maxTouchPoints property
121 | navigator.msMaxTouchPoints;
122 |
123 | isDesktop = !isPhone && !isTablet;
124 | isIE10 = uaTags['MSIE 10'];
125 | isBlackberry = uaTags.Blackberry || uaTags.BB;
126 |
127 | apply(_tags, Microloader.loadPlatformsParam(), {
128 | phone: isPhone,
129 | tablet: isTablet,
130 | desktop: isDesktop,
131 | touch: touchSupported,
132 | ios: (uaTags.iPad || uaTags.iPhone || uaTags.iPod),
133 | android: uaTags.Android || uaTags.Silk,
134 | blackberry: isBlackberry,
135 | safari: uaTags.Safari && isBlackberry,
136 | chrome: uaTags.Chrome,
137 | ie10: isIE10,
138 | windows: isIE10 || uaTags.Trident,
139 | tizen: uaTags.Tizen,
140 | firefox: uaTags.Firefox
141 | });
142 |
143 | if (Ext.beforeLoad) {
144 | Ext.beforeLoad(_tags);
145 | }
146 | },
147 |
148 | /**
149 | * Extracts user supplied platform tags from the "platformTags" query parameter
150 | * of the form:
151 | *
152 | * ?platformTags=name:state,name:state,...
153 | *
154 | * (each tag defaults to true when state is unspecified)
155 | *
156 | * Example:
157 | * ?platformTags=isTablet,isPhone:false,isDesktop:0,iOS:1,Safari:true, ...
158 | *
159 | * @returns {Object} the platform tags supplied by the query string
160 | */
161 | loadPlatformsParam: function () {
162 | // Check if the ?platform parameter is set in the URL
163 | var paramsString = window.location.search.substr(1),
164 | paramsArray = paramsString.split("&"),
165 | params = {}, i,
166 | platforms = {},
167 | tmpArray, tmplen, platform, name, enabled;
168 |
169 | for (i = 0; i < paramsArray.length; i++) {
170 | tmpArray = paramsArray[i].split("=");
171 | params[tmpArray[0]] = tmpArray[1];
172 | }
173 |
174 | if (params.platformTags) {
175 | tmpArray = params.platform.split(/\W/);
176 | for (tmplen = tmpArray.length, i = 0; i < tmplen; i++) {
177 | platform = tmpArray[i].split(":");
178 | name = platform[0];
179 | if (platform.length > 1) {
180 | enabled = platform[1];
181 | if (enabled === 'false' || enabled === '0') {
182 | enabled = false;
183 | } else {
184 | enabled = true;
185 | }
186 | }
187 | platforms[name] = enabled;
188 | }
189 | }
190 | return platform;
191 | },
192 |
193 | initPlatformTags: function () {
194 | Microloader.detectPlatformTags();
195 | },
196 |
197 | getPlatformTags: function () {
198 | return Microloader.platformTags;
199 | },
200 |
201 | filterPlatform: function (platform) {
202 | platform = [].concat(platform);
203 | var tags = Microloader.getPlatformTags(),
204 | len, p, tag;
205 |
206 | for (len = platform.length, p = 0; p < len; p++) {
207 | tag = platform[p];
208 | if (tags.hasOwnProperty(tag)) {
209 | return !!tags[tag];
210 | }
211 | }
212 | return false;
213 | },
214 |
215 | init: function () {
216 | Microloader.initPlatformTags();
217 | Ext.filterPlatform = Microloader.filterPlatform;
218 | },
219 |
220 | initManifest: function (manifest) {
221 | Microloader.init();
222 | var tmpManifest = manifest || Ext.manifest;
223 |
224 | if (typeof tmpManifest === "string") {
225 | var url = Boot.baseUrl + tmpManifest + ".json",
226 | content = Boot.fetchSync(url);
227 | tmpManifest = JSON.parse(content.content);
228 | }
229 |
230 | Ext.manifest = tmpManifest;
231 | return tmpManifest;
232 | },
233 |
234 | /**
235 | *
236 | * @param manifestDef
237 | */
238 | load: function (manifestDef) {
239 | var manifest = Microloader.initManifest(manifestDef),
240 | loadOrder = manifest.loadOrder,
241 | loadOrderMap = (loadOrder) ? Boot.createLoadOrderMap(loadOrder) : null,
242 | urls = [],
243 | js = manifest.js || [],
244 | css = manifest.css || [],
245 | resources = js.concat(css),
246 | resource, i, len, include,
247 | loadedFn = function () {
248 | _loaded = true;
249 | Microloader.notify();
250 | };
251 |
252 | for (len = resources.length, i = 0; i < len; i++) {
253 | resource = resources[i];
254 | include = true;
255 | if (resource.platform && !Microloader.filterPlatform(resource.platform)) {
256 | include = false;
257 | }
258 | if (include) {
259 | urls.push(resource.path);
260 | }
261 | }
262 |
263 |
264 | if (loadOrder) {
265 | manifest.loadOrderMap = loadOrderMap;
266 | }
267 |
268 | Boot.load({
269 | url: urls,
270 | loadOrder: loadOrder,
271 | loadOrderMap: loadOrderMap,
272 | sequential: true,
273 | success: loadedFn,
274 | failure: loadedFn
275 | });
276 | },
277 |
278 | onMicroloaderReady: function (listener) {
279 | if (_loaded) {
280 | listener();
281 | } else {
282 | _listeners.push(listener);
283 | }
284 | },
285 |
286 | /**
287 | * @private
288 | */
289 | notify: function () {
290 | var listener;
291 | while((listener = _listeners.shift())) {
292 | listener();
293 | }
294 | }
295 | };
296 |
297 | return Microloader;
298 | }());
299 |
300 | //
301 |
302 | /**
303 | * the current application manifest
304 | *
305 | *
306 | * {
307 | * name: 'name',
308 | * version: ,
309 | * debug: {
310 | * hooks: {
311 | * "*": true
312 | * }
313 | * },
314 | * localStorage: false,
315 | * mode: production,
316 | * js: [
317 | * ...
318 | * {
319 | * path: '../boo/baz.js',
320 | * version: ,
321 | * update: full | delta | ,
322 | * platform: ['phone', 'ios', 'android']
323 | * },
324 | * {
325 | * path: 'http://some.domain.com/api.js',
326 | * remote: true
327 | * },
328 | * ...
329 | * ],
330 | * css: [
331 | * ...
332 | * {
333 | * path: '../boo/baz.css',
334 | * version: ,
335 | * update: full | delta | ,
336 | * platform: ['phone', 'ios', 'android']
337 | * },
338 | * ...
339 | * ],
340 | * localStorage: false,
341 | * paths: {...},
342 | * loadOrder: [
343 | * ...
344 | * {
345 | * path: '../foo/bar.js",
346 | * idx: 158,
347 | * requires; [1,2,3,...,145,157],
348 | * uses: [182, 193]
349 | * },
350 | * ...
351 | * ],
352 | * classes: {
353 | * ...
354 | * 'Ext.panel.Panel': {
355 | * requires: [...],
356 | * uses: [...],
357 | * aliases: [...],
358 | * alternates: [...],
359 | * mixins: [...]
360 | * },
361 | * 'Ext.rtl.util.Renderable': {
362 | * requires: [...],
363 | * uses: [...],
364 | * aliases: [...],
365 | * alternates: [...],
366 | * mixins: [...]
367 | * override: 'Ext.util.Renderable'
368 | * },
369 | * ...
370 | * },
371 | * packages: {
372 | * ...
373 | * "sencha-core": {
374 | * version: '1.2.3.4',
375 | * requires: []
376 | * },
377 | * "ext": {
378 | * version: '5.0.0.0',
379 | * requires: ["sencha-core"]
380 | * }.
381 | * ...
382 | * }
383 | * }
384 | *
385 | *
386 | * @type {String/Object}
387 | */
388 | Ext.manifest = Ext.manifest || "bootstrap";
389 |
390 | Ext.Microloader.load();
--------------------------------------------------------------------------------
/resources/js/underscore-1.8.3.min.js:
--------------------------------------------------------------------------------
1 | // Underscore.js 1.8.3
2 | // http://underscorejs.org
3 | // (c) 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
4 | // Underscore may be freely distributed under the MIT license.
5 | (function(){function n(n){function t(t,r,e,u,i,o){for(;i>=0&&o>i;i+=n){var a=u?u[i]:i;e=r(e,t[a],a,t)}return e}return function(r,e,u,i){e=b(e,i,4);var o=!k(r)&&m.keys(r),a=(o||r).length,c=n>0?0:a-1;return arguments.length<3&&(u=r[o?o[c]:c],c+=n),t(r,e,u,o,c,a)}}function t(n){return function(t,r,e){r=x(r,e);for(var u=O(t),i=n>0?0:u-1;i>=0&&u>i;i+=n)if(r(t[i],i,t))return i;return-1}}function r(n,t,r){return function(e,u,i){var o=0,a=O(e);if("number"==typeof i)n>0?o=i>=0?i:Math.max(i+a,o):a=i>=0?Math.min(i+1,a):i+a+1;else if(r&&i&&a)return i=r(e,u),e[i]===u?i:-1;if(u!==u)return i=t(l.call(e,o,a),m.isNaN),i>=0?i+o:-1;for(i=n>0?o:a-1;i>=0&&a>i;i+=n)if(e[i]===u)return i;return-1}}function e(n,t){var r=I.length,e=n.constructor,u=m.isFunction(e)&&e.prototype||a,i="constructor";for(m.has(n,i)&&!m.contains(t,i)&&t.push(i);r--;)i=I[r],i in n&&n[i]!==u[i]&&!m.contains(t,i)&&t.push(i)}var u=this,i=u._,o=Array.prototype,a=Object.prototype,c=Function.prototype,f=o.push,l=o.slice,s=a.toString,p=a.hasOwnProperty,h=Array.isArray,v=Object.keys,g=c.bind,y=Object.create,d=function(){},m=function(n){return n instanceof m?n:this instanceof m?void(this._wrapped=n):new m(n)};"undefined"!=typeof exports?("undefined"!=typeof module&&module.exports&&(exports=module.exports=m),exports._=m):u._=m,m.VERSION="1.8.3";var b=function(n,t,r){if(t===void 0)return n;switch(null==r?3:r){case 1:return function(r){return n.call(t,r)};case 2:return function(r,e){return n.call(t,r,e)};case 3:return function(r,e,u){return n.call(t,r,e,u)};case 4:return function(r,e,u,i){return n.call(t,r,e,u,i)}}return function(){return n.apply(t,arguments)}},x=function(n,t,r){return null==n?m.identity:m.isFunction(n)?b(n,t,r):m.isObject(n)?m.matcher(n):m.property(n)};m.iteratee=function(n,t){return x(n,t,1/0)};var _=function(n,t){return function(r){var e=arguments.length;if(2>e||null==r)return r;for(var u=1;e>u;u++)for(var i=arguments[u],o=n(i),a=o.length,c=0;a>c;c++){var f=o[c];t&&r[f]!==void 0||(r[f]=i[f])}return r}},j=function(n){if(!m.isObject(n))return{};if(y)return y(n);d.prototype=n;var t=new d;return d.prototype=null,t},w=function(n){return function(t){return null==t?void 0:t[n]}},A=Math.pow(2,53)-1,O=w("length"),k=function(n){var t=O(n);return"number"==typeof t&&t>=0&&A>=t};m.each=m.forEach=function(n,t,r){t=b(t,r);var e,u;if(k(n))for(e=0,u=n.length;u>e;e++)t(n[e],e,n);else{var i=m.keys(n);for(e=0,u=i.length;u>e;e++)t(n[i[e]],i[e],n)}return n},m.map=m.collect=function(n,t,r){t=x(t,r);for(var e=!k(n)&&m.keys(n),u=(e||n).length,i=Array(u),o=0;u>o;o++){var a=e?e[o]:o;i[o]=t(n[a],a,n)}return i},m.reduce=m.foldl=m.inject=n(1),m.reduceRight=m.foldr=n(-1),m.find=m.detect=function(n,t,r){var e;return e=k(n)?m.findIndex(n,t,r):m.findKey(n,t,r),e!==void 0&&e!==-1?n[e]:void 0},m.filter=m.select=function(n,t,r){var e=[];return t=x(t,r),m.each(n,function(n,r,u){t(n,r,u)&&e.push(n)}),e},m.reject=function(n,t,r){return m.filter(n,m.negate(x(t)),r)},m.every=m.all=function(n,t,r){t=x(t,r);for(var e=!k(n)&&m.keys(n),u=(e||n).length,i=0;u>i;i++){var o=e?e[i]:i;if(!t(n[o],o,n))return!1}return!0},m.some=m.any=function(n,t,r){t=x(t,r);for(var e=!k(n)&&m.keys(n),u=(e||n).length,i=0;u>i;i++){var o=e?e[i]:i;if(t(n[o],o,n))return!0}return!1},m.contains=m.includes=m.include=function(n,t,r,e){return k(n)||(n=m.values(n)),("number"!=typeof r||e)&&(r=0),m.indexOf(n,t,r)>=0},m.invoke=function(n,t){var r=l.call(arguments,2),e=m.isFunction(t);return m.map(n,function(n){var u=e?t:n[t];return null==u?u:u.apply(n,r)})},m.pluck=function(n,t){return m.map(n,m.property(t))},m.where=function(n,t){return m.filter(n,m.matcher(t))},m.findWhere=function(n,t){return m.find(n,m.matcher(t))},m.max=function(n,t,r){var e,u,i=-1/0,o=-1/0;if(null==t&&null!=n){n=k(n)?n:m.values(n);for(var a=0,c=n.length;c>a;a++)e=n[a],e>i&&(i=e)}else t=x(t,r),m.each(n,function(n,r,e){u=t(n,r,e),(u>o||u===-1/0&&i===-1/0)&&(i=n,o=u)});return i},m.min=function(n,t,r){var e,u,i=1/0,o=1/0;if(null==t&&null!=n){n=k(n)?n:m.values(n);for(var a=0,c=n.length;c>a;a++)e=n[a],i>e&&(i=e)}else t=x(t,r),m.each(n,function(n,r,e){u=t(n,r,e),(o>u||1/0===u&&1/0===i)&&(i=n,o=u)});return i},m.shuffle=function(n){for(var t,r=k(n)?n:m.values(n),e=r.length,u=Array(e),i=0;e>i;i++)t=m.random(0,i),t!==i&&(u[i]=u[t]),u[t]=r[i];return u},m.sample=function(n,t,r){return null==t||r?(k(n)||(n=m.values(n)),n[m.random(n.length-1)]):m.shuffle(n).slice(0,Math.max(0,t))},m.sortBy=function(n,t,r){return t=x(t,r),m.pluck(m.map(n,function(n,r,e){return{value:n,index:r,criteria:t(n,r,e)}}).sort(function(n,t){var r=n.criteria,e=t.criteria;if(r!==e){if(r>e||r===void 0)return 1;if(e>r||e===void 0)return-1}return n.index-t.index}),"value")};var F=function(n){return function(t,r,e){var u={};return r=x(r,e),m.each(t,function(e,i){var o=r(e,i,t);n(u,e,o)}),u}};m.groupBy=F(function(n,t,r){m.has(n,r)?n[r].push(t):n[r]=[t]}),m.indexBy=F(function(n,t,r){n[r]=t}),m.countBy=F(function(n,t,r){m.has(n,r)?n[r]++:n[r]=1}),m.toArray=function(n){return n?m.isArray(n)?l.call(n):k(n)?m.map(n,m.identity):m.values(n):[]},m.size=function(n){return null==n?0:k(n)?n.length:m.keys(n).length},m.partition=function(n,t,r){t=x(t,r);var e=[],u=[];return m.each(n,function(n,r,i){(t(n,r,i)?e:u).push(n)}),[e,u]},m.first=m.head=m.take=function(n,t,r){return null==n?void 0:null==t||r?n[0]:m.initial(n,n.length-t)},m.initial=function(n,t,r){return l.call(n,0,Math.max(0,n.length-(null==t||r?1:t)))},m.last=function(n,t,r){return null==n?void 0:null==t||r?n[n.length-1]:m.rest(n,Math.max(0,n.length-t))},m.rest=m.tail=m.drop=function(n,t,r){return l.call(n,null==t||r?1:t)},m.compact=function(n){return m.filter(n,m.identity)};var S=function(n,t,r,e){for(var u=[],i=0,o=e||0,a=O(n);a>o;o++){var c=n[o];if(k(c)&&(m.isArray(c)||m.isArguments(c))){t||(c=S(c,t,r));var f=0,l=c.length;for(u.length+=l;l>f;)u[i++]=c[f++]}else r||(u[i++]=c)}return u};m.flatten=function(n,t){return S(n,t,!1)},m.without=function(n){return m.difference(n,l.call(arguments,1))},m.uniq=m.unique=function(n,t,r,e){m.isBoolean(t)||(e=r,r=t,t=!1),null!=r&&(r=x(r,e));for(var u=[],i=[],o=0,a=O(n);a>o;o++){var c=n[o],f=r?r(c,o,n):c;t?(o&&i===f||u.push(c),i=f):r?m.contains(i,f)||(i.push(f),u.push(c)):m.contains(u,c)||u.push(c)}return u},m.union=function(){return m.uniq(S(arguments,!0,!0))},m.intersection=function(n){for(var t=[],r=arguments.length,e=0,u=O(n);u>e;e++){var i=n[e];if(!m.contains(t,i)){for(var o=1;r>o&&m.contains(arguments[o],i);o++);o===r&&t.push(i)}}return t},m.difference=function(n){var t=S(arguments,!0,!0,1);return m.filter(n,function(n){return!m.contains(t,n)})},m.zip=function(){return m.unzip(arguments)},m.unzip=function(n){for(var t=n&&m.max(n,O).length||0,r=Array(t),e=0;t>e;e++)r[e]=m.pluck(n,e);return r},m.object=function(n,t){for(var r={},e=0,u=O(n);u>e;e++)t?r[n[e]]=t[e]:r[n[e][0]]=n[e][1];return r},m.findIndex=t(1),m.findLastIndex=t(-1),m.sortedIndex=function(n,t,r,e){r=x(r,e,1);for(var u=r(t),i=0,o=O(n);o>i;){var a=Math.floor((i+o)/2);r(n[a])i;i++,n+=r)u[i]=n;return u};var E=function(n,t,r,e,u){if(!(e instanceof t))return n.apply(r,u);var i=j(n.prototype),o=n.apply(i,u);return m.isObject(o)?o:i};m.bind=function(n,t){if(g&&n.bind===g)return g.apply(n,l.call(arguments,1));if(!m.isFunction(n))throw new TypeError("Bind must be called on a function");var r=l.call(arguments,2),e=function(){return E(n,e,t,this,r.concat(l.call(arguments)))};return e},m.partial=function(n){var t=l.call(arguments,1),r=function(){for(var e=0,u=t.length,i=Array(u),o=0;u>o;o++)i[o]=t[o]===m?arguments[e++]:t[o];for(;e=e)throw new Error("bindAll must be passed function names");for(t=1;e>t;t++)r=arguments[t],n[r]=m.bind(n[r],n);return n},m.memoize=function(n,t){var r=function(e){var u=r.cache,i=""+(t?t.apply(this,arguments):e);return m.has(u,i)||(u[i]=n.apply(this,arguments)),u[i]};return r.cache={},r},m.delay=function(n,t){var r=l.call(arguments,2);return setTimeout(function(){return n.apply(null,r)},t)},m.defer=m.partial(m.delay,m,1),m.throttle=function(n,t,r){var e,u,i,o=null,a=0;r||(r={});var c=function(){a=r.leading===!1?0:m.now(),o=null,i=n.apply(e,u),o||(e=u=null)};return function(){var f=m.now();a||r.leading!==!1||(a=f);var l=t-(f-a);return e=this,u=arguments,0>=l||l>t?(o&&(clearTimeout(o),o=null),a=f,i=n.apply(e,u),o||(e=u=null)):o||r.trailing===!1||(o=setTimeout(c,l)),i}},m.debounce=function(n,t,r){var e,u,i,o,a,c=function(){var f=m.now()-o;t>f&&f>=0?e=setTimeout(c,t-f):(e=null,r||(a=n.apply(i,u),e||(i=u=null)))};return function(){i=this,u=arguments,o=m.now();var f=r&&!e;return e||(e=setTimeout(c,t)),f&&(a=n.apply(i,u),i=u=null),a}},m.wrap=function(n,t){return m.partial(t,n)},m.negate=function(n){return function(){return!n.apply(this,arguments)}},m.compose=function(){var n=arguments,t=n.length-1;return function(){for(var r=t,e=n[t].apply(this,arguments);r--;)e=n[r].call(this,e);return e}},m.after=function(n,t){return function(){return--n<1?t.apply(this,arguments):void 0}},m.before=function(n,t){var r;return function(){return--n>0&&(r=t.apply(this,arguments)),1>=n&&(t=null),r}},m.once=m.partial(m.before,2);var M=!{toString:null}.propertyIsEnumerable("toString"),I=["valueOf","isPrototypeOf","toString","propertyIsEnumerable","hasOwnProperty","toLocaleString"];m.keys=function(n){if(!m.isObject(n))return[];if(v)return v(n);var t=[];for(var r in n)m.has(n,r)&&t.push(r);return M&&e(n,t),t},m.allKeys=function(n){if(!m.isObject(n))return[];var t=[];for(var r in n)t.push(r);return M&&e(n,t),t},m.values=function(n){for(var t=m.keys(n),r=t.length,e=Array(r),u=0;r>u;u++)e[u]=n[t[u]];return e},m.mapObject=function(n,t,r){t=x(t,r);for(var e,u=m.keys(n),i=u.length,o={},a=0;i>a;a++)e=u[a],o[e]=t(n[e],e,n);return o},m.pairs=function(n){for(var t=m.keys(n),r=t.length,e=Array(r),u=0;r>u;u++)e[u]=[t[u],n[t[u]]];return e},m.invert=function(n){for(var t={},r=m.keys(n),e=0,u=r.length;u>e;e++)t[n[r[e]]]=r[e];return t},m.functions=m.methods=function(n){var t=[];for(var r in n)m.isFunction(n[r])&&t.push(r);return t.sort()},m.extend=_(m.allKeys),m.extendOwn=m.assign=_(m.keys),m.findKey=function(n,t,r){t=x(t,r);for(var e,u=m.keys(n),i=0,o=u.length;o>i;i++)if(e=u[i],t(n[e],e,n))return e},m.pick=function(n,t,r){var e,u,i={},o=n;if(null==o)return i;m.isFunction(t)?(u=m.allKeys(o),e=b(t,r)):(u=S(arguments,!1,!1,1),e=function(n,t,r){return t in r},o=Object(o));for(var a=0,c=u.length;c>a;a++){var f=u[a],l=o[f];e(l,f,o)&&(i[f]=l)}return i},m.omit=function(n,t,r){if(m.isFunction(t))t=m.negate(t);else{var e=m.map(S(arguments,!1,!1,1),String);t=function(n,t){return!m.contains(e,t)}}return m.pick(n,t,r)},m.defaults=_(m.allKeys,!0),m.create=function(n,t){var r=j(n);return t&&m.extendOwn(r,t),r},m.clone=function(n){return m.isObject(n)?m.isArray(n)?n.slice():m.extend({},n):n},m.tap=function(n,t){return t(n),n},m.isMatch=function(n,t){var r=m.keys(t),e=r.length;if(null==n)return!e;for(var u=Object(n),i=0;e>i;i++){var o=r[i];if(t[o]!==u[o]||!(o in u))return!1}return!0};var N=function(n,t,r,e){if(n===t)return 0!==n||1/n===1/t;if(null==n||null==t)return n===t;n instanceof m&&(n=n._wrapped),t instanceof m&&(t=t._wrapped);var u=s.call(n);if(u!==s.call(t))return!1;switch(u){case"[object RegExp]":case"[object String]":return""+n==""+t;case"[object Number]":return+n!==+n?+t!==+t:0===+n?1/+n===1/t:+n===+t;case"[object Date]":case"[object Boolean]":return+n===+t}var i="[object Array]"===u;if(!i){if("object"!=typeof n||"object"!=typeof t)return!1;var o=n.constructor,a=t.constructor;if(o!==a&&!(m.isFunction(o)&&o instanceof o&&m.isFunction(a)&&a instanceof a)&&"constructor"in n&&"constructor"in t)return!1}r=r||[],e=e||[];for(var c=r.length;c--;)if(r[c]===n)return e[c]===t;if(r.push(n),e.push(t),i){if(c=n.length,c!==t.length)return!1;for(;c--;)if(!N(n[c],t[c],r,e))return!1}else{var f,l=m.keys(n);if(c=l.length,m.keys(t).length!==c)return!1;for(;c--;)if(f=l[c],!m.has(t,f)||!N(n[f],t[f],r,e))return!1}return r.pop(),e.pop(),!0};m.isEqual=function(n,t){return N(n,t)},m.isEmpty=function(n){return null==n?!0:k(n)&&(m.isArray(n)||m.isString(n)||m.isArguments(n))?0===n.length:0===m.keys(n).length},m.isElement=function(n){return!(!n||1!==n.nodeType)},m.isArray=h||function(n){return"[object Array]"===s.call(n)},m.isObject=function(n){var t=typeof n;return"function"===t||"object"===t&&!!n},m.each(["Arguments","Function","String","Number","Date","RegExp","Error"],function(n){m["is"+n]=function(t){return s.call(t)==="[object "+n+"]"}}),m.isArguments(arguments)||(m.isArguments=function(n){return m.has(n,"callee")}),"function"!=typeof/./&&"object"!=typeof Int8Array&&(m.isFunction=function(n){return"function"==typeof n||!1}),m.isFinite=function(n){return isFinite(n)&&!isNaN(parseFloat(n))},m.isNaN=function(n){return m.isNumber(n)&&n!==+n},m.isBoolean=function(n){return n===!0||n===!1||"[object Boolean]"===s.call(n)},m.isNull=function(n){return null===n},m.isUndefined=function(n){return n===void 0},m.has=function(n,t){return null!=n&&p.call(n,t)},m.noConflict=function(){return u._=i,this},m.identity=function(n){return n},m.constant=function(n){return function(){return n}},m.noop=function(){},m.property=w,m.propertyOf=function(n){return null==n?function(){}:function(t){return n[t]}},m.matcher=m.matches=function(n){return n=m.extendOwn({},n),function(t){return m.isMatch(t,n)}},m.times=function(n,t,r){var e=Array(Math.max(0,n));t=b(t,r,1);for(var u=0;n>u;u++)e[u]=t(u);return e},m.random=function(n,t){return null==t&&(t=n,n=0),n+Math.floor(Math.random()*(t-n+1))},m.now=Date.now||function(){return(new Date).getTime()};var B={"&":"&","<":"<",">":">",'"':""","'":"'","`":"`"},T=m.invert(B),R=function(n){var t=function(t){return n[t]},r="(?:"+m.keys(n).join("|")+")",e=RegExp(r),u=RegExp(r,"g");return function(n){return n=null==n?"":""+n,e.test(n)?n.replace(u,t):n}};m.escape=R(B),m.unescape=R(T),m.result=function(n,t,r){var e=null==n?void 0:n[t];return e===void 0&&(e=r),m.isFunction(e)?e.call(n):e};var q=0;m.uniqueId=function(n){var t=++q+"";return n?n+t:t},m.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var K=/(.)^/,z={"'":"'","\\":"\\","\r":"r","\n":"n","\u2028":"u2028","\u2029":"u2029"},D=/\\|'|\r|\n|\u2028|\u2029/g,L=function(n){return"\\"+z[n]};m.template=function(n,t,r){!t&&r&&(t=r),t=m.defaults({},t,m.templateSettings);var e=RegExp([(t.escape||K).source,(t.interpolate||K).source,(t.evaluate||K).source].join("|")+"|$","g"),u=0,i="__p+='";n.replace(e,function(t,r,e,o,a){return i+=n.slice(u,a).replace(D,L),u=a+t.length,r?i+="'+\n((__t=("+r+"))==null?'':_.escape(__t))+\n'":e?i+="'+\n((__t=("+e+"))==null?'':__t)+\n'":o&&(i+="';\n"+o+"\n__p+='"),t}),i+="';\n",t.variable||(i="with(obj||{}){\n"+i+"}\n"),i="var __t,__p='',__j=Array.prototype.join,"+"print=function(){__p+=__j.call(arguments,'');};\n"+i+"return __p;\n";try{var o=new Function(t.variable||"obj","_",i)}catch(a){throw a.source=i,a}var c=function(n){return o.call(this,n,m)},f=t.variable||"obj";return c.source="function("+f+"){\n"+i+"}",c},m.chain=function(n){var t=m(n);return t._chain=!0,t};var P=function(n,t){return n._chain?m(t).chain():t};m.mixin=function(n){m.each(m.functions(n),function(t){var r=m[t]=n[t];m.prototype[t]=function(){var n=[this._wrapped];return f.apply(n,arguments),P(this,r.apply(m,n))}})},m.mixin(m),m.each(["pop","push","reverse","shift","sort","splice","unshift"],function(n){var t=o[n];m.prototype[n]=function(){var r=this._wrapped;return t.apply(r,arguments),"shift"!==n&&"splice"!==n||0!==r.length||delete r[0],P(this,r)}}),m.each(["concat","join","slice"],function(n){var t=o[n];m.prototype[n]=function(){return P(this,t.apply(this._wrapped,arguments))}}),m.prototype.value=function(){return this._wrapped},m.prototype.valueOf=m.prototype.toJSON=m.prototype.value,m.prototype.toString=function(){return""+this._wrapped},"function"==typeof define&&define.amd&&define("underscore",[],function(){return m})}).call(this);
6 | //# sourceMappingURL=underscore-min.map
--------------------------------------------------------------------------------