From: Jeremy Mordkoff Date: Wed, 7 Sep 2016 22:59:17 +0000 (-0400) Subject: Rift.IO OSM R1 Initial Submission X-Git-Tag: v1.0.0~22 X-Git-Url: https://osm.etsi.org/gitweb/?p=osm%2FUI.git;a=commitdiff_plain;h=refs%2Fchanges%2F87%2F287%2F1 Rift.IO OSM R1 Initial Submission Signed-off-by: Jeremy Mordkoff --- diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..40934df14 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +.DS_Store/ +.DS_Store +err.log +out.log +node_modules/ +npm-debug.log +fixtures/ +.build diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 000000000..1c9b5766c --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,59 @@ +# +# Copyright 2016 RIFT.IO Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author(s): Kiran Kashalkar +# Creation Date: 08/18/2015 +# + +## +# DEPENDENCY ALERT +# The submodule dependencies must be specified in the +# .gitmodules.dep file at the top level (supermodule) directory +# If this submodule depends other submodules remember to update +# the .gitmodules.dep +## + +cmake_minimum_required(VERSION 2.8) + +## +# DO NOT add any code before this and DO NOT +# include this file anywhere else +## +include(rift_submodule) + +## +# Submodule specific includes will go here, +# These are specified here, since these variables are accessed +# from multiple sub directories. If the variable is subdirectory +# specific it must be declared in the subdirectory. +## + + +## +# Include the subdirs +## +set( + subdirs + skyquake + ) +rift_add_subdirs( + SUBDIR_LIST + ${subdirs} + ) + +## +# This macro adds targets for documentaion, unittests, code coverage and packaging +## +rift_add_submodule_targets(SUBMODULE_PACKAGE_NAME "rw.ui") diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..5c9d8648b --- /dev/null +++ b/Makefile @@ -0,0 +1,60 @@ +# +# Copyright 2016 RIFT.IO Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author(s): Austin Cormier +# Creation Date: 07/27/2016 +# +# + +.PHONY : clean + +makefile.top := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))) +TOP_SRC_PATH := $(makefile.top) +TOP_ROOT_PATH := /usr/rift +CMAKE_MODULE_PATH := $(TOP_ROOT_PATH)/cmake/modules + +RIFT_BUILD = $(TOP_SRC_PATH)/.build +RIFT_ARTIFACTS = $(TOP_ROOT_PATH)/artifacts +RIFT_INSTALL = $(TOP_ROOT_PATH) +RIFT_SHELL_EXE = $(TOP_ROOT_PATH)/rift-shell -b $(RIFT_BUILD) -i $(RIFT_INSTALL) -a $(RIFT_ARTIFACTS) -- + +CONFD = XML_ONLY + +BUILD_TYPE = Debug +NOT_DEVELOPER_TYPE = FALSE +COVERAGE_BUILD = FALSE +RIFT_AGENT_BUILD = $(CONFD) +PROJECT_TOP_DIR = $(TOP_ROOT_PATH) + +all: rw + +cmake:: + mkdir -p $(RIFT_BUILD) + mkdir -p $(RIFT_ARTIFACTS) + mkdir -p $(RIFT_INSTALL) + cd $(RIFT_BUILD) && $(RIFT_SHELL_EXE) cmake $(TOP_SRC_PATH) -DCMAKE_INSTALL_PREFIX=$(TOP_ROOT_PATH) -DCMAKE_BUILD_TYPE=$(BUILD_TYPE) -DNOT_DEVELOPER_BUILD=$(NOT_DEVELOPER_TYPE) -DCOVERAGE_BUILD=$(COVERAGE_TYPE) -DRIFT_AGENT_BUILD=$(RIFT_AGENT_BUILD) -DPROJECT_TOP_DIR=$(PROJECT_TOP_DIR) -DCMAKE_MODULE_PATH=${CMAKE_MODULE_PATH} -DRIFT_SUBMODULE_NAME=$(PROJECT_TOP_DIR) + +rw: cmake + $(RIFT_SHELL_EXE) $(MAKE) -C $(RIFT_BUILD) + +install: + $(RIFT_SHELL_EXE) $(MAKE) -C $(RIFT_BUILD) install + +unittest: + $(RIFT_SHELL_EXE) $(MAKE) -C $(RIFT_BUILD) rw.unittest + +clean: + @echo "Cleaning up.." + -rm -rf .build diff --git a/foss.txt b/foss.txt new file mode 100644 index 000000000..457c99cfb --- /dev/null +++ b/foss.txt @@ -0,0 +1,63 @@ +# +# webapp/node_modules +# +RIFT.ui, webapp/node_modules/angular, angularjs, MIT, https://github.com/angular/angular.js +RIFT.ui, webapp/node_modules/angular-ui-router, angular-ui-router, MIT, https://github.com/angular-ui/ui-router +RIFT.ui, webapp/node_modules/alt, alt, MIT, https://github.com/goatslacker/alt +RIFT.ui, webapp/node_modules/babel-core, babel-core, MIT, https://www.npmjs.com/package/babel-core +RIFT.ui, webapp/node_modules/babel-loader, babel-loader, MIT, https://github.com/babel/babel-loader +RIFT.ui, webapp/node_modules/babel-preset-es2015, babel-preset-es2015, MIT, https://github.com/babel/babel/tree/master/packages/babel-preset-es2015 +RIFT.ui, webapp/node_modules/babel-runtime, babel-runtime, MIT, https://www.npmjs.com/package/babel-runtime +RIFT.ui, webapp/node_modules/baggage-loader, baggage-loader, MIT, https://github.com/deepsweet/baggage-loader +#RIFT.ui, canv-gauge, canv-gauge, MIT, https://github.com/Mikhus/canv-gauge +RIFT.ui, webapp/node_modules/css-loader, css-loader, MIT, https://github.com/webpack/css-loader +RIFT.ui, webapp/node_modules/express, express, MIT, https://github.com/strongloop/express +RIFT.ui, webapp/node_modules/file-loader, file-loader, MIT, https://github.com/webpack/file-loader +RIFT.ui, webapp/node_modules/foundation-apps, foundation-apps, MIT, https://github.com/zurb/foundation-apps +RIFT.ui, webapp/node_modules/html-loader, html-loader, MIT, https://github.com/webpack/html-loader +RIFT.ui, webapp/node_modules/http-proxy, http-proxy, MIT, https://github.com/nodejitsu/node-http-proxy +RIFT.ui, webapp/node_modules/jquery, jquery, MIT-like, https://github.com/jquery/jquery +RIFT.ui, webapp/node_modules/loader-utils, loader-utils, MIT, https://github.com/webpack/loader-utils +RIFT.ui, webapp/node_modules/loaders.css, loaders.css, MIT, https://github.com/ConnorAtherton/loaders.css/tree/master +RIFT.ui, webapp/node_modules/minimist, minimist, MIT, https://github.com/substack/minimist +RIFT.ui, webapp/node_modules/ngtemplate-loader, ngtemplate-loader, MIT, https://github.com/WearyMonkey/ngtemplate-loader +RIFT.ui, webapp/node_modules/node-sass, node-sass, MIT-like, https://github.com/sass/node-sass +RIFT.ui, webapp/node_modules/open-iconic, open-iconic, MIT and SIL, https://github.com/iconic/open-iconic +RIFT.ui, webapp/node_modules/prismjs, prismjs, MIT, https://github.com/PrismJS/prism +RIFT.ui, webapp/node_modules/react, react, BSD, https://github.com/facebook/react +RIFT.ui, webapp/node_modules/react-d3-histogram, react-d3-histogram, ISC, https://github.com/english/react-d3-histogram +RIFT.ui, webapp/node_modules/react-slick, react-slick, MIT, https://github.com/akiran/react-slick +RIFT.ui, webapp/node_modules/react-tabs, react-tabs, MIT, https://github.com/rackt/react-tabs/blob/master/LICENSE +RIFT.ui, webapp/node_modules/react-treeview, react-treeview, MIT, https://www.npmjs.com/package/react-treeview +RIFT.ui, webapp/node_modules/sass-loader, sass-loader, MIT, https://github.com/jtangelder/sass-loader +RIFT.ui, webapp/node_modules/source-map, source-map, BSD, https://github.com/mozilla/source-map +RIFT.ui, webapp/node_modules/style-loader, style-loader, MIT, https://github.com/webpack/style-loader +RIFT.ui, webapp/node_modules/underscore, underscore, MIT-like, https://github.com/webpack/style-loader +RIFT.ui, webapp/node_modules/webpack, webpack, MIT, https://github.com/webpack/webpack +RIFT.ui, webapp/node_modules/webpack-dev-server, webpack-dev-server, MIT, https://github.com/webpack/webpack-dev-server +# +# api/node_modules +# +RIFT.ui, api/node_modules/bluebird, bluebird, MIT, https://github.com/petkaantonov/bluebird +RIFT.ui, api/node_modules/body-parser, body-parser, MIT, https://github.com/expressjs/body-parser +RIFT.ui, api/node_modules/cors, cors, MIT, https://github.com/expressjs/cors +RIFT.ui, api/node_modules/express, express, MIT, https://github.com/strongloop/express +RIFT.ui, api/node_modules/hawk, hawk, BSD-3-Clause, https://github.com/hueniverse/hawk +RIFT.ui, api/node_modules/minimist, minimist, MIT, https://github.com/substack/minimist +RIFT.ui, api/node_modules/promise, promise, MIT, https://github.com/then/promise +RIFT.ui, api/node_modules/qs, qs, BSD-3-Clause, https://github.com/hapijs/qs +RIFT.ui, api/node_modules/request, request, Apache-2.0, https://github.com/request/request +RIFT.ui, api/node_modules/request-promise, request-promise, MIT, https://github.com/request/request-promise +RIFT.ui, api/node_modules/underscore, underscore, MIT, https://github.com/jashkenas/underscore +RIFT.ui, api/node_modules/ws, ws, MIT, https://github.com/websockets/ws +RIFT.ui, api/node_modules/jasmine-node, jasmine-node, MIT, https://github.com/mhevery/jasmine-node +RIFT.ui, api/node_modules/nock, Nock is an HTTP mocking and expectations library for Node.js, MIT, https://github.com/pgte/nock +RIFT.ui, api/node_modules/replay, Node Replay, MIT, https://github.com/assaf/node-replay +RIFT.ui, api/node_modules/express-session, express-session, MIT, https://github.com/expressjs/session +# +# webapp/public +# +#RIFT.ui, webapp/public/vendor/css-reset-2.0, CSS Reset, NONE, http://meyerweb.com/eric/tools/css/reset/ + +RIFT.ui, skyquake/plugins/composer/node_modules/json2yaml, json2yaml, Apache-2.0, https://github.com/coolaj86/json2yaml +RIFT.ui, skyquake/plugins/composer/node_modules/react-inlinesvg, react-inlinesvg, MIT, https://github.com/matthewwithanm/react-inlinesvg diff --git a/manifest/LICENSE b/manifest/LICENSE new file mode 100644 index 000000000..e69de29bb diff --git a/skyquake/.babelrc b/skyquake/.babelrc new file mode 100644 index 000000000..f68f024b7 --- /dev/null +++ b/skyquake/.babelrc @@ -0,0 +1,5 @@ +{ + "presets": [ + "es2015", "stage-0", "react" + ] +} diff --git a/skyquake/.gitignore b/skyquake/.gitignore new file mode 100644 index 000000000..111d0cd3c --- /dev/null +++ b/skyquake/.gitignore @@ -0,0 +1,5 @@ +npm-debug.log +*.swp +node_modules +public +dist diff --git a/skyquake/.storybook/config.js b/skyquake/.storybook/config.js new file mode 100644 index 000000000..42edf3cd4 --- /dev/null +++ b/skyquake/.storybook/config.js @@ -0,0 +1,13 @@ +import { configure } from '@kadira/storybook'; + +function loadStories() { + // require('../tests/stories/button'); + // require('../tests/stories/sq-input-slider'); + // require('../tests/stories/sshKeyCard'); + // require('../tests/stories/button'); + // require('../tests/stories/sq-input-slider'); + require('../tests/stories/catalogCard'); + // require as many stories as you need. +} + +configure(loadStories, module); diff --git a/skyquake/.storybook/webpack.config.js b/skyquake/.storybook/webpack.config.js new file mode 100644 index 000000000..4feae7f23 --- /dev/null +++ b/skyquake/.storybook/webpack.config.js @@ -0,0 +1,59 @@ +/* + * + * Copyright 2016 RIFT.IO Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +var Webpack = require('webpack'); +var path = require('path'); +var nodeModulesPath = path.resolve(__dirname, 'node_modules'); +var buildPath = path.resolve(__dirname, 'public', 'build'); +var mainPath = path.resolve(__dirname, 'src', 'main.js'); +var frameworkPath = '../skyquake/framework/'; +var CommonsPlugin = new require("webpack/lib/optimize/CommonsChunkPlugin") +// Added to overcome node-sass bug https://github.com/iam4x/isomorphic-flux-boilerplate/issues/62 +process.env.UV_THREADPOOL_SIZE=64; +var config = { + resolve: { + extensions: ['', '.js', '.jsx', '.css', '.scss'], + root: path.resolve(frameworkPath), + alias: { + 'widgets': path.resolve(frameworkPath) + '/widgets', + 'style': path.resolve(frameworkPath) + '/style', + 'utils': path.resolve(frameworkPath) + '/utils' + } + }, + module: { + loaders: [{ + test: /\.(jpe?g|png|gif|svg|ttf|otf|eot|svg|woff(2)?)(\?[a-z0-9]+)?$/i, + loader: "file-loader" + }, + { + test: /\.(js|jsx)$/, + exclude: /node_modules/, + loader: 'babel-loader', + query: { + presets: ["es2015", "stage-0", "react"] + } + }, { + test: /\.css$/, + loader: 'style!css' + }, { + test: /\.scss/, + loader: 'style!css!sass?includePaths[]='+ path.resolve(frameworkPath) + } + ] + } +}; +module.exports = config; diff --git a/skyquake/CMakeLists.txt b/skyquake/CMakeLists.txt new file mode 100644 index 000000000..ac3637f26 --- /dev/null +++ b/skyquake/CMakeLists.txt @@ -0,0 +1,63 @@ +# +# Copyright 2016 RIFT.IO Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author(s): Kiran Kashalkar +# Creation Date: 08/18/2015 +# + +## +# DEPENDENCY ALERT +# The submodule dependencies must be specified in the +# .gitmodules.dep file at the top level (supermodule) directory +# If this submodule depends other submodules remember to update +# the .gitmodules.dep +## + +cmake_minimum_required(VERSION 2.8) + +## +# Submodule specific includes will go here, +# These are specified here, since these variables are accessed +# from multiple sub directories. If the variable is subdirectory +# specific it must be declared in the subdirectory. +## + +rift_externalproject_add( + skyquake + SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR} + CONFIGURE_COMMAND echo + BUILD_COMMAND + npm install + INSTALL_COMMAND + ${CMAKE_CURRENT_SOURCE_DIR}/scripts/install_skyquake.sh + ${CMAKE_CURRENT_BINARY_DIR}/skyquake/skyquake-build + ${CMAKE_INSTALL_PREFIX}/usr/share/rw.ui/skyquake + ${RIFT_SUBMODULE_INSTALL_PREFIX}/skyquake/${CMAKE_INSTALL_PREFIX}/usr/share/rw.ui/skyquake + + BCACHE_COMMAND echo +) + + +## +# Include the subdirs +## +set( + subdirs + plugins + ) +rift_add_subdirs( + SUBDIR_LIST + ${subdirs} + ) diff --git a/skyquake/__tests__/skyquakeRouter_test.js b/skyquake/__tests__/skyquakeRouter_test.js new file mode 100644 index 000000000..c451c34c8 --- /dev/null +++ b/skyquake/__tests__/skyquakeRouter_test.js @@ -0,0 +1,50 @@ +jest.dontMock('../framework/widgets/skyquake_container/skyquakeNav.jsx'); +// /skyquake/framework/widgets/skyquake_container/skyquakeNav.jsx +import React from 'react'; +import ReactDOM from 'react-dom'; +import TestUtils from 'react-addons-test-utils'; + +// import {returnLinkItem} from '../framework/widgets/skyquake_container/skyquakeNav.jsx'; + +const SkyquakeNav = require('../framework/widgets/skyquake_container/skyquakeNav.jsx') + +describe('SkyquakeNav', () => { + + let exampleRoutes; + let node; + beforeEach(function() { + node = document.createElement('div'); + exampleRoutes = [{ + "label": "Hello World Component 1", + "route": "/helloworld/#hello", + "component": "./helloWorldOne.jsx", + "path": "", + "type": "internal", + "isExternal": true + },{ + + "label": "Hello World Component 1", + "route": "hello", + "component": "./helloWorldOne.jsx", + "path": "", + "type": "internal", + "isExternal": false + }]; + }); + describe('returnLinkItem', () => { + it('Returns an tag when external', () => { + let element = SkyquakeNav.returnLinkItem(exampleRoutes[0]); + let Tag = TestUtils.renderIntoDocument(element); + let TagNode = ReactDOM.findDOMNode(Tag); + expect(Tag.constructor.displayName).toEqual("A"); + expect(TagNode.attributes.href).not.toEqual(undefined); + }); + it('Returns a tag when internal', () => { + let element = SkyquakeNav.returnLinkItem(exampleRoutes[1]); + let Tag = TestUtils.renderIntoDocument(element); + let TagNode = ReactDOM.findDOMNode(Tag); + expect(TagNode.constructor.displayName).toEqual("Link"); + expect(TagNode.attributes.href).not.toEqual(undefined); + }) + }) +}) diff --git a/skyquake/framework/core/api_utils/constants.js b/skyquake/framework/core/api_utils/constants.js new file mode 100644 index 000000000..dccadb713 --- /dev/null +++ b/skyquake/framework/core/api_utils/constants.js @@ -0,0 +1,77 @@ +/* + * + * Copyright 2016 RIFT.IO Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + /** + * constants module. Provides constants for use within the skyquake instance + * @module api_utils/constants + */ + +var constants = {}; + +constants.FOREVER_ON = true; +constants.HTTP_HEADERS = { + accept: { + data: { + 'Accept': 'application/vnd.yang.data+json' + }, + collection: { + 'Accept': 'application/vnd.yang.collection+json' + } + }, + content_type: { + data: { + 'Content-Type': 'application/vnd.yang.data+json' + }, + collection: { + 'Content-Type': 'application/vnd.yang.collection+json' + } + } +}; + +// (Incomplete) set of expected HTTP response codes +constants.HTTP_RESPONSE_CODES = { + SUCCESS: { + OK: 200, + CREATED: 201, + ACCEPTED: 202, + NO_CONTENT: 204, + MOVED_PERMANENTLY: 301, + NOT_MODIFIED: 304 + }, + ERROR: { + BAD_REQUEST: 400, + UNAUTHORIZED: 401, + FORBIDDEN: 403, + NOT_FOUND: 404, + METHOD_NOT_ALLOWED: 405, + NOT_ACCEPTABLE: 406, + CONFLICT: 409, + INTERNAL_SERVER_ERROR: 500, + NOT_IMPLEMENTED: 501, + BAD_GATEWAY: 502, + SERVICE_UNAVAILABLE: 504, + HTTP_VERSION_UNSUPPORTED: 505 + + } +} +constants.SOCKET_BASE_PORT = 3500; +constants.SOCKET_POOL_LENGTH = 20; +constants.SERVER_PORT = process.env.SERVER_PORT || 8000; +constants.SECURE_SERVER_PORT = process.env.SECURE_SERVER_PORT || 8443; + +module.exports = constants; \ No newline at end of file diff --git a/skyquake/framework/core/api_utils/sockets.js b/skyquake/framework/core/api_utils/sockets.js new file mode 100644 index 000000000..607659476 --- /dev/null +++ b/skyquake/framework/core/api_utils/sockets.js @@ -0,0 +1,325 @@ +/* + * + * Copyright 2016 RIFT.IO Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +//SOCKET MANAGER +// test +//Supports localhost node polling subscriptions and pass through subscriptions to other websockets +// +//TODO REFACTOR: this needs to happen. there's too much boilerplate code in here. +//TODO Document after refactoring +//TODO Improved logging for debugging +//TODO List of URLS + +var WebSocket = require('ws'); +var Request = require('request'); +var _ = require('lodash'); +var constants = require('./constants.js'); +var Promise = require('promise'); +var url = require('url'); +var sockjs = require('sockjs'); +var websocket_multiplex = require('websocket-multiplex'); + + +function getPortForProtocol (protocol) { + switch (protocol) { + case 'http': + return 8000; + case 'https': + return 8443; + } +} + +var Subscriptions = function() { + this.ID = 0; + this.socketServers = {}; +}; + +Subscriptions.prototype.configure = function(config) { + this.config = config; + this.ready = true; + // 1. Setup SockJS server + var sockjs_opts = {}; + this.service = sockjs.createServer(sockjs_opts); + // 2. Setup multiplexing + this.multiplexer = new websocket_multiplex.MultiplexServer(this.service); + + this.service.installHandlers(this.config.httpServer, {prefix:'/multiplex'}); +} + +/** + * [subscribe description] + * @param {Object} req + * @param {String} req.body.url May be http, https, or ws + * @param {Function} req.body.transform A function that will transform + * the data before sending it out + * through the socket. Receives one + * argument, which is the data + * returned from the subscription. + * @param {Function} callback Function that will receive the SubscriptionData reference object + * @return {Object} SubscriptionReference An object containing the subscription information. + * @return {Number} SubscriptionReference.id The subscription ID + */ +Subscriptions.prototype.subscribe = function(req, callback) { + var self = this; + var URL = req.body.url; + var SubscriptionReference; + var sessionId = req.session.id; + var protocolTest = /^(.{2,5}):\/\//; + var protocol = URL.match(protocolTest); + + if (!protocol) { + var origin = ''; + if (req.query['api_server']) { + var api_server_protocol = req.query['api_server'].match(protocolTest)[1]; + var api_server_origin = req.query['api_server'] + ':' + getPortForProtocol(api_server_protocol); + origin = api_server_origin; + protocol = api_server_protocol; + } else { + // TODO: NEED A WAY (URL PARAM) TO TRIGGER THIS PART OF THE CODE + // WHICH IS NECESSARY FOR DEVELOPMENT ON MAC + // No protocol was passed with the url in the body. Assume req.protocol is protocol and construct URL + protocol = req.protocol || 'https'; + // Converting relative URL to full path. + origin = protocol + '://' + req.headers.host + } + var a = url.resolve(origin, req.baseUrl); + var b = url.resolve(a, URL); + URL = b; + console.log('DEBUG URL IS', URL); + } else { + protocol = protocol[1] + } + + return new Promise(function(resolve, reject) { + + if (!self.ready) { + return reject({ + statusCode: 500, + errorMessage: 'SocketManager not configured yet. Cannot proceed' + }) + } + + self.createWebSocketServer().then(function(successData) { + + self.socketServers[sessionId + successData.id] = successData; + self.setUpSocketInstance(protocol, URL, req, self.socketServers[sessionId + successData.id].wss, successData.id); + return resolve({ + statusCode: 200, + data: { + id: self.socketServers[sessionId + successData.id].id + } + }); + }, + function(errorData) { + return reject({ + statusCode: 503, + errorMessage: errorData.error + }); + }); + }); +}; + +Subscriptions.prototype.setUpSocketInstance = function(protocol, URL, req, wss, channelId) { + var self = this; + //Need to refactor this to make it more scalable/dynamic + switch (protocol) { + case 'http': + self.socketInstance(URL, req, wss, PollingSocket, channelId); + break; + case 'https': + self.socketInstance(URL, req, wss, PollingSocket, channelId); + break; + case 'ws': + self.socketInstance(URL, req, wss, WebSocket, channelId); + break; + case 'wss': + self.socketInstance(URL, req, wss, WebSocket, channelId); + break; + } +} + +Subscriptions.prototype.createWebSocketServer = function() { + var self = this; + + return new Promise(function(resolve, reject) { + var wss = null; + + self.ID++; + + wss = self.multiplexer.registerChannel(self.ID); + + return resolve({ + id: self.ID, + wss: wss + }); + }); +}; + +Subscriptions.prototype.socketInstance = function(url, req, wss, Type, channelId) { + console.log('Creating a new socketInstance for:', url, 'sessionId:', req.session.id); + var self = this; + var Socket = null; + var Connections = []; + var Index = 0; + var sessionId = req.session.id; + var wssRef = wss; + var channelIdRef = channelId; + wss.on('connection', function(conn) { + console.log('New connection to multiplex-server for channelId', channelIdRef); + + conn.on('data', function(msg) { + console.log('Test purposes only. Received message from client:', msg); + conn.write('Test purposes only. Echo: ' + msg); + }); + + if (!Socket) { + if (Type == PollingSocket) { + Socket = new Type(url, req, 1000, req.body); + } else { + Socket = new Type(url); + } + console.log('Socket assigned for url', url); + } + conn.index = Index++; + // Add this client-connection into list of connections for this channelId/wss + Connections.push(conn); + + conn.on('close', function() { + // Remove the browser connection from list of Connections for this channelId/wss + Connections.splice(conn.index, 1); + console.log('splicing conn.index', conn.index,' for channel', channelIdRef); + + // Check if no other connections exist + if (Connections.length == 0) { + console.log('No more connections for', channelId, '. Will close socket server and downstream socket/poller.'); + try { + // Close downstream socket/poller + Socket.close(); + + // Close socket server + conn.end(); + + // Remove from list of socketServers + delete self.socketServers[sessionId + wss.id]; + + // There is no unregisterChannel. Assuming + // sockjs/websocket-multiplex do the right + // things and cleanup after themselves. + } catch (e) { + console.log('Error closing socket server: ', e); + } + Index = 0; + delete Socket; + } + }); + + Socket.onopen = function() { + console.log('Opened a websocket to southbound server'); + }; + + Socket.onerror = function(error) { + console.log('Error on southbound connection. Error:', error); + } + + Socket.onmessage = function(data) { + var i; + var self = this; + if (req.body.transform && req.body.transform.constructor.name == "String") { + //someTransformObject[req.body.transform](data, send) + //req.body.transform(data, send); + } else { + if (Type == PollingSocket) { + send(data); + } else { + send(data.data); + } + } + + function send(payload) { + var is401 = false; + try { + if (typeof payload == 'string') { + var jsonPayload = JSON.parse(payload); + is401 = jsonPayload.statusCode == 401; + } + else { + is401 = payload.statusCode == 401; + } + } catch(e) { + payload = {} + } + + for (i = Connections.length - 1; i >= 0; i -= 1) { + // console.log('Sending payload to channelId:', channelId, ' on connection', i); + Connections[i].write(payload); + }; + if (is401) { + try { + Socket.close(); + } catch (e) { + console.log('Error closing Socket') + } + } + } + + }; + }); +}; + +function PollingSocket(url, req, interval, config) { + console.log('Creating a new PollingSocket for url', url, 'sessionId:', req.session.id); + var self = this; + self.isClosed = false; + var requestHeaders = {}; + _.extend(requestHeaders, { + 'Authorization': req.get('Authorization') + }); + + var pollServer = function() { + Request({ + url: url, + method: config.method || 'GET', + headers: requestHeaders, + json: config.payload, + rejectUnauthorized: false, + forever: constants.FOREVER_ON + }, function(error, response, body) { + if (error) { + console.log('Error polling: ' + url); + } else { + if (!self.isClosed) { + self.poll = setTimeout(pollServer, 1000 || interval); + var data = response.body; + if (self.onmessage) { + self.onmessage(data); + } + } + } + }); + }; + pollServer(); +}; + +PollingSocket.prototype.close = function() { + console.log('Closing PollingSocket'); + var self = this; + this.isClosed = true; + clearTimeout(self.poll); +}; + + +module.exports = Subscriptions; diff --git a/skyquake/framework/core/api_utils/utils.js b/skyquake/framework/core/api_utils/utils.js new file mode 100644 index 000000000..163769a98 --- /dev/null +++ b/skyquake/framework/core/api_utils/utils.js @@ -0,0 +1,237 @@ +/* + * + * Copyright 2016 RIFT.IO Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +// Helper Functions + + +/** + * Utils for use across the api_server. + * @module framework/core/utils + */ + +var fs = require('fs'); +var request = require('request'); +var Promise = require('promise'); +var CONSTANTS = require('./constants.js'); +var CONFD_PORT = '8008'; +var APIVersion = '/v1'; +var _ = require('lodash'); + +var requestWrapper = function(request) { + if (process.env.HTTP_PROXY && process.env.HTTP_PROXY != '') { + request = request.defaults({ + 'proxy': process.env.HTTP_PROXY + }); + } + return request; +} + +var confdPort = function(api_server) { + try { + api_server = api_server.replace(api_server.match(/[0-9](:[0-9]+)/)[1], '') + } catch (e) { + + } + return api_server + ':' + CONFD_PORT; +}; + + +var validateResponse = function(callerName, error, response, body, resolve, reject) { + var res = {}; + + if (error) { + console.log('Problem with "', callerName, '": ', error); + res.statusCode = 500; + res.errorMessage = { + error: 'Problem with ' + callerName + ': ' + error + }; + reject(res); + return false; + } else if (response.statusCode >= 400) { + console.log('Problem with "', callerName, '": ', response.statusCode, ':', body); + res.statusCode = response.statusCode; + + // auth specific + if (response.statusCode == 401) { + res.errorMessage = { + error: 'Authentication needed' + body + }; + reject(res); + return false; + } + + res.errorMessage = { + error: 'Problem with ' + callerName + ': ' + response.statusCode + ': ' + typeof(body) == 'string' ? body : JSON.stringify(body), + body: body + }; + + reject(res); + return false; + } else if (response.statusCode == 204) { + resolve({ + statusCode: response.statusCode, + data: {} + }); + return false; + } else { + return true; + } +}; + + +var checkAuthorizationHeader = function(req) { + return new Promise(function(resolve, reject) { + if (req.get('Authorization') == null) { + reject(); + } else { + resolve(); + } + }); +}; + +if (process.env.LOG_REQUESTS) { + var logFile = process.env.REQUESTS_LOG_FILE; + + if (logFile && logFile != '') { + validateResponse = function(callerName, error, response, body, resolve, reject) { + var res = {}; + + if (error) { + console.log('Problem with "', callerName, '": ', error); + res.statusCode = 500; + res.errorMessage = { + error: 'Problem with ' + callerName + ': ' + error + }; + reject(res); + fs.appendFileSync(logFile, 'Request API: ' + response.request.uri.href + ' ; ' + 'Error: ' + error); + return false; + } else if (response.statusCode >= 400) { + console.log('Problem with "', callerName, '": ', response.statusCode, ':', body); + res.statusCode = response.statusCode; + + // auth specific + if (response.statusCode == 401) { + res.errorMessage = { + error: 'Authentication needed' + body + }; + reject(res); + fs.appendFileSync(logFile, 'Request API: ' + response.request.uri.href + ' ; ' + 'Error Body: ' + body); + return false; + } + + res.errorMessage = { + error: 'Problem with ' + callerName + ': ' + response.statusCode + ': ' + body + }; + + reject(res); + fs.appendFileSync(logFile, 'Request API: ' + response.request.uri.href + ' ; ' + 'Error Body: ' + body); + return false; + } else if (response.statusCode == 204) { + resolve(); + fs.appendFileSync(logFile, 'Request API: ' + response.request.uri.href + ' ; ' + 'Response Body: ' + body); + return false; + } else { + fs.appendFileSync(logFile, 'Request API: ' + response.request.uri.href + ' ; ' + 'Response Body: ' + body); + return true; + } + }; + } +} + +/** + * Serve the error response back back to HTTP requester + * @param {Object} error - object of the format + * { + * statusCode - HTTP code to respond back with + * error - actual error JSON object to serve + * } + * @param {Function} res - a handle to the express response function + */ +var sendErrorResponse = function(error, res) { + res.status(error.statusCode); + res.send(error); +} + +/** + * Serve the success response back to HTTP requester + * @param {Object} response - object of the format + * { + * statusCode - HTTP code to respond back with + * data - actual data JSON object to serve + * } + * @param {Function} res - a handle to the express response function + */ +var sendSuccessResponse = function(response, res) { + res.status(response.statusCode); + res.send(response.data); +} + +var passThroughConstructor = function(app) { + app.get('/passthrough/:type/*', function(req, res) { + var url = req.params[0]; + var type = req.params.type; + var api_server = req.query["api_server"]; + var uri = confdPort(api_server) + APIVersion + '/' + url + '?deep'; + // Check that type is valid + switch (type) { + case 'data': + ; + case 'collection': + break; + default: + res.send({}); + } + new Promise(function(resolve, reject) { + request({ + uri: uri, + method: 'GET', + headers: _.extend({}, CONSTANTS.HTTP_HEADERS.accept[type], { + 'Authorization': req.get('Authorization'), + forever: CONSTANTS.FOREVER_ON, + rejectUnauthorized: false, + }) + }, function(error, response, body) { + if (validateResponse('Passthrough: ' + url, error, response, body, resolve, reject)) { + resolve(JSON.parse(response.body)) + }; + }); + }).then(function(data) { + res.send(data); + }, function(error) { + res.send({'error': error, uri: uri}) + });; + }); +} + +module.exports = { + /** + * Ensure confd port is on api_server variable. + **/ + confdPort: confdPort, + + validateResponse: validateResponse, + + checkAuthorizationHeader: checkAuthorizationHeader, + + request: requestWrapper.call(null, request), + + sendErrorResponse: sendErrorResponse, + + sendSuccessResponse: sendSuccessResponse, + + passThroughConstructor: passThroughConstructor +}; diff --git a/skyquake/framework/core/modules/api/configuration.js b/skyquake/framework/core/modules/api/configuration.js new file mode 100644 index 000000000..376264396 --- /dev/null +++ b/skyquake/framework/core/modules/api/configuration.js @@ -0,0 +1,95 @@ +/* + * + * Copyright 2016 RIFT.IO Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/** + * Configuration api module. Provides API functions to configure node + * @module framework/core/modules/api/configuration + * @author Kiran Kashalkar + */ + +var Promise = require('bluebird'); +var constants = require('../../api_utils/constants'); +var utils = require('../../api_utils/utils'); +var request = utils.request; +var configurationAPI = {}; +var _ = require('lodash'); +var GLOBAL_CONFIGURATION = { + api_server: 'localhost', + ssl_enabled: true +}; + +/** + * Get the server configuration for the Express API + * @param {Object} req - the Express request object + * @return {Function} Promise that resolves with success object or rejects + * with error + */ +configurationAPI.get = function(req) { + return new Promise(function(resolve, reject) { + resolve({ + statusCode: constants.HTTP_RESPONSE_CODES.SUCCESS.OK, + data: GLOBAL_CONFIGURATION + }); + }); +}; + +/** + * Update the server configuration for the Express API + * @param {Object} req - the Express request object + * @return {Function} Promise that resolves with success object or rejects + * with error + */ +configurationAPI.update = function(req) { + var newConfiguration = req.body; + return new Promise(function(resolve, reject) { + try { + _.merge(GLOBAL_CONFIGURATION, newConfiguration); + resolve({ + statusCode: constants.HTTP_RESPONSE_CODES.SUCCESS.NO_CONTENT, + data: {} + }) + } catch (e) { + console.log('Merging on new configuration failed. Error:', e); + reject({ + statusCode: constants.HTTP_RESPONSE_CODES.ERROR.INTERNAL_SERVER_ERROR, + errorMessage: { + error: 'Problem with merging new configuration. Error: ' + JSON.stringify(e) + } + }); + } + }); +}; + +configurationAPI.globalConfiguration = {}; +/** + * Internally used (by Node.js components) to update server configuration + * @param {Object} data - the configuration to merge + */ +configurationAPI.globalConfiguration.update = function(data) { + _.merge(GLOBAL_CONFIGURATION, data); +}; + +/** + * Internally used (by Node.js components) to get server configuration + * @param {Object} data - the configuration to merge + */ +configurationAPI.globalConfiguration.get = function() { + return GLOBAL_CONFIGURATION; +}; + +module.exports = configurationAPI; diff --git a/skyquake/framework/core/modules/api/descriptorModelMetaAPI.js b/skyquake/framework/core/modules/api/descriptorModelMetaAPI.js new file mode 100644 index 000000000..b0223b2ab --- /dev/null +++ b/skyquake/framework/core/modules/api/descriptorModelMetaAPI.js @@ -0,0 +1,77 @@ +/* + * + * Copyright 2016 RIFT.IO Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +// DescriptorModelMeta API (NSD + VNFD) + + +var ModelMeta = {}; +var Promise = require('bluebird'); +var rp = require('request-promise'); +var Promise = require('promise'); +var constants = require('../../api_utils/constants'); +var utils = require('../../api_utils/utils'); +var _ = require('lodash'); + +ModelMeta.get = function(req) { + var self = this; + var api_server = req.query['api_server']; + + return new Promise(function(resolve, reject) { + Promise.all([ + rp({ + uri: utils.confdPort(api_server) + '/api/schema/nsd-catalog/nsd', + method: 'GET', + headers: _.extend({}, constants.HTTP_HEADERS.accept.collection, { + 'Authorization': req.get('Authorization') + }), + forever: constants.FOREVER_ON, + rejectUnauthorized: false, + resolveWithFullResponse: true + }), + rp({ + uri: utils.confdPort(api_server) + '/api/schema/vnfd-catalog/vnfd', + method: 'GET', + headers: _.extend({}, constants.HTTP_HEADERS.accept.collection, { + 'Authorization': req.get('Authorization') + }), + forever: constants.FOREVER_ON, + rejectUnauthorized: false, + resolveWithFullResponse: true + }) + ]).then(function(result) { + var response = {}; + response['data'] = {}; + if (result[0].body && result[1].body) { + response['data']['nsd'] = JSON.parse(result[0].body)['nsd']; + response['data']['vnfd'] = JSON.parse(result[1].body)['vnfd']; + } + response.statusCode = constants.HTTP_RESPONSE_CODES.SUCCESS.OK + + resolve(response); + }).catch(function(error) { + var response = {}; + console.log('Problem with ModelMeta.get', error); + response.statusCode = error.statusCode || 500; + response.errorMessage = { + error: 'Failed to get descriptorModelMeta' + error + }; + reject(response); + }); + }); +}; + +module.exports = ModelMeta; diff --git a/skyquake/framework/core/modules/api/navigation.js b/skyquake/framework/core/modules/api/navigation.js new file mode 100644 index 000000000..5f22835e9 --- /dev/null +++ b/skyquake/framework/core/modules/api/navigation.js @@ -0,0 +1,136 @@ +/* + * + * Copyright 2016 RIFT.IO Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/** + * navigation api module. Provides API functions to interact with + * the navigation_manager + * @module framework/core/modules/api/navigation + * @author Kiran Kashalkar + */ + +var navigation_manager = require('../navigation_manager'); +var Promise = require('promise'); +var constants = require('../../api_utils/constants'); +var navigationAPI = {}; + +/** + * Get the navigation object stored in this instance + * @param {Object} req - the Express request object with or without a plugin_id + * @return {Function} Promise that resolves with success object or rejects + * with error + */ +navigationAPI.get = function(req) { + return new Promise(function(resolve, reject) { + if (req.params.plugin_id) { + var navigation = navigation_manager.getNavigation(); + if (navigation[req.params.plugin_id]) { + resolve({ + statusCode: constants.HTTP_RESPONSE_CODES.SUCCESS.OK, + data: navigation[req.params.plugin_id] + }); + } else { + reject({ + statusCode: constants.HTTP_RESPONSE_CODES.ERROR.NOT_FOUND, + errorMessage: 'No navigation found for plugin_id ' + req.params.plugin_id + }); + } + } else { + resolve({ + statusCode: constants.HTTP_RESPONSE_CODES.SUCCESS.OK, + data: navigation_manager.getNavigation() + }); + } + }); +}; + +/** + * Create the navigation object stored in this instance for a plugin. + * To be used across framework instances for informing each other of + * inter-instance navigation options. + * @param {Object} req - the Express request object + * @return {Function} Promise that resolves with success object or rejects + * with error + */ +navigationAPI.post = function(req) { + return new Promise(function(resolve, reject) { + var plugin_nav = navigation_manager.getNavigation()[req.params.plugin_id] || {}; + if (plugin_nav != {}) { + reject({ + statusCode: constants.HTTP_RESPONSE_CODES.ERROR.CONFLICT, + errorMessage: 'Navigation for ' + req.params.plugin_id + ' already exist' + }); + } else { + navigation_manager.addNavigation(req.params.plugin_id, req.body); + resolve({ + statusCode: constants.HTTP_RESPONSE_CODES.SUCCESS.CREATED, + data: {} + }); + } + }); +}; + +/** + * Update the navigation object stored in this instance for a plugin. + * @param {Object} req - the Express request object with a plugin_id and route_id + * @return {Function} Promise that resolves with success object or rejects + * with error + */ +navigationAPI.put = function(req) { + return new Promise(function(resolve, reject) { + var plugin_nav = navigation_manager.getNavigation()[req.params.plugin_id]['routes'][req.params.route_id]; + if (plugin_nav == null || plugin_nav == undefined) { + reject({ + statusCode: constants.HTTP_RESPONSE_CODES.ERROR.BAD_REQUEST, + errorMessage: 'Navigation for route' + req.params.route_id + ' under plugin ' + req.params.plugin_id + ' does not exist' + }); + } else { + navigation_manager.addNavigation(req.plugin_id, req.body); + resolve({ + statusCode: constants.HTTP_RESPONSE_CODES.SUCCESS.OK, + data: {} + }); + } + }); +}; + +/** + * Delete a particulat route navigation object stored in this instance for a plugin. + * @param {Object} req - the Express request object with a plugin_id and route_id + * @return {Function} Promise that resolves with success object or rejects + * with error + */ +navigationAPI.delete = function(req) { + return new Promise(function(resolve, reject) { + var plugin_nav = navigation_manager.getNavigation()[req.params.plugin_id]['routes'[req.params.route_id]]; + if (plugin_nav == null || plugin_nav == undefined) { + reject({ + statusCode: constants.HTTP_RESPONSE_CODES.ERROR.BAD_REQUEST, + errorMessage: 'Navigation for route' + req.params.route_id + ' under plugin ' + req.params.plugin_id + ' does not exist' + }); + } else { + navigation_manager.deleteNavigation(req.params.plugin_id, req.params.route_id); + resolve({ + statusCode: constants.HTTP_RESPONSE_CODES.SUCCESS.OK, + data: {} + }); + } + }); +}; + + +module.exports = navigationAPI; \ No newline at end of file diff --git a/skyquake/framework/core/modules/api/restconf.js b/skyquake/framework/core/modules/api/restconf.js new file mode 100644 index 000000000..5ba0eb5aa --- /dev/null +++ b/skyquake/framework/core/modules/api/restconf.js @@ -0,0 +1,65 @@ +/* + * + * Copyright 2016 RIFT.IO Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/** + * restconf api module. Provides API functions to interact with RESTCONF + * @module framework/core/modules/api/restconf + * @author Kiran Kashalkar + */ + +var Promise = require('bluebird'); +var constants = require('../../api_utils/constants'); +var utils = require('../../api_utils/utils'); +var request = utils.request; +var restconfAPI = {}; +var _ = require('lodash'); +restconfAPI['streams'] = {}; + +/** + * Get the RESTCONF/Netconf streams from the RESTCONF endpoint + * @param {Object} req - the Express request object with or without a plugin_id + * @return {Function} Promise that resolves with success object or rejects + * with error + */ +restconfAPI['streams'].get = function(req) { + var api_server = req.query["api_server"]; + var uri = utils.confdPort(api_server); + var url = req.path; + return new Promise(function(resolve, reject) { + request({ + url: uri + url + '?deep', + method: 'GET', + headers: _.extend({}, constants.HTTP_HEADERS.accept.data, { + 'Authorization': req.get('Authorization') + }), + forever: constants.FOREVER_ON, + rejectUnauthorized: false, + }, function(error, response, body) { + if (utils.validateResponse('restconfAPI.streams', error, response, body, resolve, reject)) { + // resolve(JSON.parse(response.body)) + resolve({ + statusCode: response.statusCode, + data: JSON.parse(response.body) + }) + }; + }) + }) +}; + + +module.exports = restconfAPI; diff --git a/skyquake/framework/core/modules/navigation_manager.js b/skyquake/framework/core/modules/navigation_manager.js new file mode 100644 index 000000000..c85eba618 --- /dev/null +++ b/skyquake/framework/core/modules/navigation_manager.js @@ -0,0 +1,106 @@ +/* + * + * Copyright 2016 RIFT.IO Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/** + * navigation_manager module. manages navigation state for a skyquake instance + * @module framework/core/modules/navigation_manager + * @author Kiran Kashalkar + */ + +var path = require('path'); +var fs = require('fs'); +var util = require('util'); +var skyquakeEmitter = require('./skyquakeEmitter'); +require('require-json'); +var _ = require('lodash'); + +/** + * navigation looks like: + * { + * "plugin_name": <{routes>}> + * } + */ + +var NAVIGATION = {}; + +function addNavigation(plugin_name, routes) { + if (!NAVIGATION[plugin_name]) { + NAVIGATION[plugin_name] = {}; + } + + if (!NAVIGATION[plugin_name].routes) { + NAVIGATION[plugin_name].routes = routes; + } else { + _.extend(NAVIGATION[plugin_name].routes, routes); + } +} +function addOrder(plugin_name, order) { + if (!NAVIGATION[plugin_name]) { + NAVIGATION[plugin_name] = {}; + } + NAVIGATION[plugin_name].order = order || 5; +} +function addPriority(plugin_name, priority) { + if (!NAVIGATION[plugin_name]) { + NAVIGATION[plugin_name] = {}; + } + NAVIGATION[plugin_name].priority = priority || 1; +} + +function addLabel(plugin_name, label) { + if (!NAVIGATION[plugin_name]) { + NAVIGATION[plugin_name] = {}; + } + NAVIGATION[plugin_name].label = label || 'RW.UI Plugin'; +} + +function getNavigation() { + return NAVIGATION; +} + +function deleteNavigation(plugin_name, route_id) { + delete NAVIGATION[plugin_name]['routes'][route_id]; +} + +function onNavigationDiscovered(plugin_name, plugin) { + addNavigation(plugin_name, plugin.routes); + addOrder(plugin_name, plugin.order); + addPriority(plugin_name, plugin.priority); + addLabel(plugin_name, plugin.name); +} + +function init() { + skyquakeEmitter.on('config_discoverer.navigation_discovered', onNavigationDiscovered); +} + +function config(config) { + +} + +function run() { + +} + + +module.exports = { + init: init, + config: config, + run: run, + addNavigation: addNavigation, + getNavigation: getNavigation +}; diff --git a/skyquake/framework/core/modules/plugin_discoverer.js b/skyquake/framework/core/modules/plugin_discoverer.js new file mode 100644 index 000000000..6653e41e1 --- /dev/null +++ b/skyquake/framework/core/modules/plugin_discoverer.js @@ -0,0 +1,80 @@ + +/* + * + * Copyright 2016 RIFT.IO Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/** + * plugin_discoverer module. Static plugin discovery at bootstrap + * and dynamic plugin discovery during runtime. + * @module framework/core/modules/plugin_discoverer + * @author Kiran Kashalkar + */ +var path = require('path'); +var fs = require('fs'); +var util = require('util'); +var skyquakeEmitter = require('./skyquakeEmitter'); + + +var plugins_path = ''; + +/** + * Module initialization function + */ +function init() { + +} + +/** + * Module configuration function + * @param {Object} config - configuration object + */ +function config(config) { + plugins_path = config.plugins_path; +} + +/** + * Module run function. + */ +function run() { + + fs.readdir(plugins_path, function(err, filenames) { + if (!err) { + filenames.forEach(function(filename) { + fs.stat(plugins_path + '/' + filename, function(err, stats) { + if (!err) { + if (stats.isDirectory()) { + skyquakeEmitter.emit('plugin_discoverer.plugin_discovered', filename, path.join(plugins_path, path.sep, filename)); + } + } + }); + }); + } + }); + + // Watch for modifications and new plugins + fs.watch(plugins_path, {persistent: true, recursive: true}, function(event, filename) { + var splitPath = filename.split(path.sep) + skyquakeEmitter.emit('plugin_discoverer.plugin_updated', splitPath[0], filename); + }); +} + + +module.exports = { + init: init, + config: config, + run: run +}; \ No newline at end of file diff --git a/skyquake/framework/core/modules/routes/configuration.js b/skyquake/framework/core/modules/routes/configuration.js new file mode 100644 index 000000000..3a686f094 --- /dev/null +++ b/skyquake/framework/core/modules/routes/configuration.js @@ -0,0 +1,56 @@ + +/* + * + * Copyright 2016 RIFT.IO Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/** + * Node configuration routes module. + * Provides a RESTful API to provide configuration + * details such as api_server. + * @module framework/core/modules/routes/configuration + * @author Kiran Kashalkar + */ + +var cors = require('cors'); +var bodyParser = require('body-parser'); +var configurationAPI = require('../api/configuration'); +var Router = require('express').Router(); +var utils = require('../../api_utils/utils'); + +Router.use(bodyParser.json()); +Router.use(cors()); +Router.use(bodyParser.urlencoded({ + extended: true +})); + +Router.put('/server-configuration', cors(), function(req, res) { + configurationAPI.update(req).then(function(data) { + utils.sendSuccessResponse(data, res); + }, function(error) { + utils.sendErrorResponse(error, res); + }); +}); + +Router.get('/server-configuration', cors(), function(req, res) { + configurationAPI.get(req).then(function(data) { + utils.sendSuccessResponse(data, res); + }, function(error) { + utils.sendErrorResponse(error, res); + }); +}); + +module.exports = Router; diff --git a/skyquake/framework/core/modules/routes/descriptorModel.js b/skyquake/framework/core/modules/routes/descriptorModel.js new file mode 100644 index 000000000..28b7f15ef --- /dev/null +++ b/skyquake/framework/core/modules/routes/descriptorModel.js @@ -0,0 +1,50 @@ + +/* + * + * Copyright 2016 RIFT.IO Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/** + * inactivity routes module. Provides a RESTful API for this + * skyquake instance's inactivity state. + * @module framework/core/modules/routes/inactivity + * @author Laurence Maultsby + */ + +var cors = require('cors'); +var bodyParser = require('body-parser'); +var Router = require('express').Router(); +var utils = require('../../api_utils/utils'); +var descriptorModelMetaAPI = require('../api/descriptorModelMetaAPI.js'); + +Router.use(bodyParser.json()); +Router.use(cors()); +Router.use(bodyParser.urlencoded({ + extended: true +})); + +Router.get('/descriptor-model-meta', cors(), function(req, res) { + descriptorModelMetaAPI.get(req).then(function(response) { + utils.sendSuccessResponse(response, res); + }, function(error) { + utils.sendErrorResponse(error, res); + }); +}); + +module.exports = Router; + + + diff --git a/skyquake/framework/core/modules/routes/inactivity.js b/skyquake/framework/core/modules/routes/inactivity.js new file mode 100644 index 000000000..7c3c4408b --- /dev/null +++ b/skyquake/framework/core/modules/routes/inactivity.js @@ -0,0 +1,48 @@ + +/* + * + * Copyright 2016 RIFT.IO Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/** + * inactivity routes module. Provides a RESTful API for this + * skyquake instance's inactivity state. + * @module framework/core/modules/routes/inactivity + * @author Laurence Maultsby + */ + +var cors = require('cors'); +var bodyParser = require('body-parser'); +var Router = require('express').Router(); +var utils = require('../../api_utils/utils'); + +Router.use(bodyParser.json()); +Router.use(cors()); +Router.use(bodyParser.urlencoded({ + extended: true +})); + +Router.get('/inactivity-timeout', cors(), function(req, res) { + var response = { + statusCode: 200, + data: { + 'inactivity-timeout': process.env.UI_TIMEOUT_SECS || 600000 + } + } + utils.sendSuccessResponse(response, res); +}); + +module.exports = Router; diff --git a/skyquake/framework/core/modules/routes/navigation.js b/skyquake/framework/core/modules/routes/navigation.js new file mode 100644 index 000000000..82c7ec580 --- /dev/null +++ b/skyquake/framework/core/modules/routes/navigation.js @@ -0,0 +1,85 @@ + +/* + * + * Copyright 2016 RIFT.IO Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/** + * navigation routes module. Provides a RESTful API for this + * skyquake instance's navigation state. + * @module framework/core/modules/routes/navigation + * @author Kiran Kashalkar + */ + +var cors = require('cors'); +var bodyParser = require('body-parser'); +var navAPI = require('../api/navigation'); +var Router = require('express').Router(); +var utils = require('../../api_utils/utils'); +var configurationAPI = require('../api/configuration'); + +Router.use(bodyParser.json()); +Router.use(cors()); +Router.use(bodyParser.urlencoded({ + extended: true +})); + +Router.get('/', cors(), function(req, res, next) { + res.redirect('/launchpad/?api_server=' + req.protocol + '://' + configurationAPI.globalConfiguration.get().api_server + '&upload_server=' + req.protocol + '://' + (configurationAPI.globalConfiguration.get().upload_server || req.hostname)); +}); + +Router.get('/nav', cors(), function(req, res) { + navAPI.get(req).then(function(data) { + utils.sendSuccessResponse(data, res); + }, function(error) { + utils.sendErrorResponse(error, res); + }); +}); + +Router.get('/nav/:plugin_id', cors(), function(req, res) { + navAPI.get(req).then(function(data) { + utils.sendSuccessResponse(data, res); + }, function(error) { + utils.sendErrorResponse(error, res); + }); +}); + +Router.post('/nav/:plugin_id', cors(), function(req, res) { + navAPI.create(req).then(function(data) { + utils.sendSuccessResponse(data, res); + }, function(error) { + utils.sendErrorResponse(error, res); + }); +}); + +Router.put('/nav/:plugin_id/:route_id', cors(), function(req, res) { + navAPI.update(req).then(function(data) { + utils.sendSuccessResponse(data, res); + }, function(error) { + utils.sendErrorResponse(error, res); + }); +}); + +Router.delete('/nav/:plugin_id/:route_id', cors(), function(req, res) { + navAPI.delete(req).then(function(data) { + utils.sendSuccessResponse(data, res); + }, function(error) { + utils.sendErrorResponse(error, res); + }); +}); + + +module.exports = Router; diff --git a/skyquake/framework/core/modules/routes/restconf.js b/skyquake/framework/core/modules/routes/restconf.js new file mode 100644 index 000000000..7a073c5e2 --- /dev/null +++ b/skyquake/framework/core/modules/routes/restconf.js @@ -0,0 +1,47 @@ + +/* + * + * Copyright 2016 RIFT.IO Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/** + * restconf specific routes module. Provides a RESTful API for RESTCONF APIs. + * @module framework/core/modules/routes/restconf + * @author Kiran Kashalkar + */ + +var cors = require('cors'); +var bodyParser = require('body-parser'); +var restconfAPI = require('../api/restconf'); +var Router = require('express').Router(); +var utils = require('../../api_utils/utils'); + +Router.use(bodyParser.json()); +Router.use(cors()); +Router.use(bodyParser.urlencoded({ + extended: true +})); + +Router.get('/api/operational/restconf-state/streams', cors(), function(req, res) { + restconfAPI['streams'].get(req).then(function(data) { + // res.send(data); + utils.sendSuccessResponse(data, res); + }, function(error) { + utils.sendErrorResponse(error, res); + }); +}); + +module.exports = Router; diff --git a/skyquake/framework/core/modules/routes/sockets.js b/skyquake/framework/core/modules/routes/sockets.js new file mode 100644 index 000000000..da3f3b1bd --- /dev/null +++ b/skyquake/framework/core/modules/routes/sockets.js @@ -0,0 +1,62 @@ + +/* + * + * Copyright 2016 RIFT.IO Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/** + * socket routes module. Provides a RESTful API for this + * skyquake instance's navigation state. + * @module framework/core/modules/routes/socket + * @author Kiran Kashalkar + */ + +var cors = require('cors'); +var bodyParser = require('body-parser'); +var Router = require('express').Router(); +var utils = require('../../api_utils/utils'); + +var sockets = {}; + +sockets.routes = function(socketManager) { + console.log('Configuring socket routes'); + Router.use(bodyParser.json()); + Router.use(cors()); + Router.use(bodyParser.urlencoded({ + extended: true + })); + + Router.post('/socket-polling', cors(), function(req, res) { + socketManager.subscribe(req).then(function(data) { + utils.sendSuccessResponse(data, res); + }, function(error) { + utils.sendErrorResponse(error, res); + }); + }); + + Router.get('/socket-polling-test', cors(), function(req, res) { + utils.sendSuccessResponse({ + statusCode: 200, + data: { + message: 'Hello socket polling' + } + }, res); + }); +} + +sockets.router = Router; + +module.exports = sockets; diff --git a/skyquake/framework/core/modules/skyquakeEmitter.js b/skyquake/framework/core/modules/skyquakeEmitter.js new file mode 100644 index 000000000..d3cfeb93e --- /dev/null +++ b/skyquake/framework/core/modules/skyquakeEmitter.js @@ -0,0 +1,28 @@ +/* + * + * Copyright 2016 RIFT.IO Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/** + * skyquakeEmitter module. global channel for communication + * @module framework/core/modules/skyquakeEmitter + * @author Kiran Kashalkar + */ + +var events = require('events'); +var skyquakeEmitter = new events.EventEmitter(); + +module.exports = skyquakeEmitter; \ No newline at end of file diff --git a/skyquake/framework/js/gauge-modified.js b/skyquake/framework/js/gauge-modified.js new file mode 100644 index 000000000..ad881c3eb --- /dev/null +++ b/skyquake/framework/js/gauge-modified.js @@ -0,0 +1,1258 @@ + +/* + * + * Copyright 2016 RIFT.IO Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +// (function(window){ + /**! + * @license + * HTML5 Canvas Gauge implementation + * + * This code is subject to MIT license. + * + * Copyright (c) 2012 Mykhailo Stadnyk + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * @authors: Mykhailo Stadnyk + * Chris Poile + * Luca Invernizzi + * Robert Blackburn + */ + + /** + * @param {Object} config + * @constructor + */ + var Gauge = function (config) { + Gauge.Collection.push(this); + + /** + * Default gauge configuration + * @struct + */ + this.config = { + isAggregate: false, + renderTo: null, + width: 200, + height: 200, + title: false, + maxValue: 100, + minValue: 0, + majorTicks: [], + minorTicks: 10, + strokeTicks: true, + units: false, + valueFormat: { "int": 3, "dec": 2 }, + majorTicksFormat: { "int": 1, "dec": 0 }, + glow: true, + animation: { + delay: 10, + duration: 250, + fn: 'cycle' + }, + colors: { + plate: '#fff', + majorTicks: '#444', + minorTicks: '#666', + title: '#888', + units: '#888', + numbers: '#444', + needle: { start: 'rgba(240, 128, 128, 1)', end: 'rgba(255, 160, 122, .9)' } + }, + highlights: [ + { + from: 20, + to: 60, + color: '#eee' + }, + { + from: 60, + to: 80, + color: '#ccc' + }, + { + from: 80, + to: 100, + color: '#999' + } + ] + }; + + var + value = 0, + self = this, + fromValue = 0, + toValue = 0, + imready = false + ; + + /** + * Sets a new value to gauge and updates the gauge view + * + * @param {number} val - the new value to set to the gauge + * @return {Gauge} this - returns self + */ + this.setValue = function (val) { + + fromValue = config.animation ? value : val; + + var dv = (config.maxValue - config.minValue) / 100; + + toValue = val > config.maxValue ? + config.maxValue + dv : + val < config.minValue ? + config.minValue - dv : + val + ; + + value = val; + + if (toValue >= fromValue) { + config.arrow = 'up'; + } else { + config.arrow = 'down'; + } + + config.animation ? animate() : this.draw(); + + return this; + }; + + /** + * Sets a new value to gauge and updates the gauge view without + * any animation (even if configured) + * + * @param {number} val - the new value to set to the gauge + * @return {Gauge} this - returns self + */ + this.setRawValue = function (val) { + fromValue = value = val; + this.draw(); + return this; + }; + + /** + * Clears the value of the gauge + * @return {Gauge} + */ + this.clear = function () { + value = fromValue = toValue = this.config.minValue; + this.draw(); + return this; + }; + + + /** + * Returns the current value been set to the gauge + * + * @return {number} value - current gauge's value + */ + this.getValue = function () { + return value; + }; + + /** + * Ready event for the gauge. Use it whenever you + * initialize the gauge to be assured it was fully drawn + * before you start the update on it + * + * @event {Function} onready + */ + this.onready = function () { + }; + + function applyRecursive(dst, src) { + for (var i in src) { + // modification by Chris Poile, Oct 08, 2012. More correct check of an Array instance + if (typeof src[i] == "object" && !(Object.prototype.toString.call(src[i]) === '[object Array]') && i != 'renderTo') { + if (typeof dst[i] != "object") { + dst[i] = {}; + } + + applyRecursive(dst[i], src[i]); + } else { + dst[i] = src[i]; + } + } + }; + + applyRecursive(this.config, config); + + this.config.minValue = parseFloat(this.config.minValue); + this.config.maxValue = parseFloat(this.config.maxValue); + + config = this.config; + fromValue = value = config.minValue; + + if (!config.renderTo) { + throw Error("Canvas element was not specified when creating the Gauge object!"); + } + + var + canvas = config.renderTo.tagName ? config.renderTo : document.getElementById(config.renderTo), + ctx = canvas.getContext('2d'), + cache, CW, CH, CX, CY, max, cctx + ; + + function baseInit() { + canvas.width = config.width; + canvas.height = config.height; + + cache = canvas.cloneNode(true); + cctx = cache.getContext('2d'); + CW = canvas.width; + CH = canvas.height; + CX = CW / 2; + CY = CH / 2; + max = CX < CY ? CX : CY; + + cache.i8d = false; + + // translate cache to have 0, 0 in center + cctx.translate(CX, CY); + cctx.save(); + + // translate canvas to have 0,0 in center + ctx.translate(CX, CY); + ctx.save(); + }; + + // do basic initialization + baseInit(); + + /** + * Updates the gauge config + * + * @param {Object} config + * @return {Gauge} + */ + this.updateConfig = function (config) { + applyRecursive(this.config, config); + baseInit(); + this.draw(); + return this; + }; + + var animateFx = { + linear: function (p) { + return p; + }, + quad: function (p) { + return Math.pow(p, 2); + }, + quint: function (p) { + return Math.pow(p, 5); + }, + cycle: function (p) { + return 1 - Math.sin(Math.acos(p)); + }, + bounce: function (p) { + return 1 - (function (p) { + for (var a = 0, b = 1; 1; a += b, b /= 2) { + if (p >= (7 - 4 * a) / 11) { + return -Math.pow((11 - 6 * a - 11 * p) / 4, 2) + Math.pow(b, 2); + } + } + })(1 - p); + }, + elastic: function (p) { + return 1 - (function (p) { + var x = 1.5; + return Math.pow(2, 10 * (p - 1)) * Math.cos(20 * Math.PI * x / 3 * p); + })(1 - p); + } + }; + + var animateInterval = null; + + function _animate(opts) { + var start = new Date; + + animateInterval = setInterval(function () { + var + timePassed = new Date - start, + progress = timePassed / opts.duration + ; + + if (progress > 1) { + progress = 1; + } + + var animateFn = typeof opts.delta == "function" ? + opts.delta : + animateFx[opts.delta] + ; + + var delta = animateFn(progress); + opts.step(delta); + + if (progress == 1) { + clearInterval(animateInterval); + } + }, opts.delay || 10); + }; + + function animate() { + animateInterval && clearInterval(animateInterval); // stop previous animation + var + path = (toValue - fromValue), + from = fromValue, + cfg = config.animation + ; + + _animate({ + delay: cfg.delay, + duration: cfg.duration, + delta: cfg.fn, + step: function (delta) { + fromValue = parseFloat(from) + path * delta; + self.draw(); + } + }); + }; + + // defaults + ctx.lineCap = "round"; + + /** + * Drows the gauge. Normally this function should be used to + * initally draw the gauge + * + * @return {Gauge} this - returns the self Gauge object + */ + this.draw = function () { + if (!cache.i8d) { + // clear the cache + cctx.clearRect(-CX, -CY, CW, CH); + cctx.save(); + + var tmp = {ctx: ctx}; + ctx = cctx; + + drawPlate(); + drawHighlights(); + drawMinorTicks(); + drawMajorTicks(); + drawNumbers(); + drawTitle(); + drawUnits(); + + cache.i8d = true; + ctx = tmp.ctx; + delete tmp.ctx; + } + + // clear the canvas + ctx.clearRect(-CX, -CY, CW, CH); + ctx.save(); + + ctx.drawImage(cache, -CX, -CY, CW, CH); + + if (!Gauge.initialized) { + var iv = setInterval(function () { + if (!Gauge.initialized) { + return; + } + + clearInterval(iv); + + drawValueBox(); + drawNeedle(); + // drawArrow(); + + if (!imready) { + self.onready && self.onready(); + imready = true; + } + }, 10); + } else { + drawValueBox(); + drawNeedle(); + // drawArrow(); + + if (!imready) { + self.onready && self.onready(); + imready = true; + } + } + + return this; + }; + + /** + * Transforms degrees to radians + */ + function radians(degrees) { + return degrees * Math.PI / 180; + }; + + /** + * Linear gradient + */ + function lgrad(clrFrom, clrTo, len) { + var grad = ctx.createLinearGradient(0, 0, 0, len); + grad.addColorStop(0, clrFrom); + grad.addColorStop(1, clrTo); + + return grad; + }; + + function drawPlate() { + var + r0 = max / 100 * 93, + d0 = max - r0, + r1 = max / 100 * 91, + d1 = max - r1, + r2 = max / 100 * 88, + d2 = max - r2, + r3 = max / 100 * 85; + + ctx.save(); + + if (config.glow) { + ctx.shadowBlur = d0; + ctx.shadowColor = 'rgba(0, 0, 0, 0.5)'; + } + + ctx.beginPath(); + ctx.arc(0, 0, r0, 0, Math.PI * 2, true); + // ctx.fillStyle = lgrad( '#ddd', '#aaa', r0); + ctx.fillStyle = lgrad('hsla(0, 0%, 17%, 1)', 'hsla(0, 0%, 0%, 1)', r0); + //ctx.fill(); + + //ctx.restore(); + + ctx.beginPath(); + ctx.arc(0, 0, r1, 0, Math.PI * 2, true); + // ctx.fillStyle = lgrad( '#fafafa', '#ccc', r1); + ctx.fillStyle = lgrad('hsla(0, 0%, 47%, 1)', 'hsla(0, 0%, 33%, 1)', r1); + //ctx.fill(); + + ctx.beginPath(); + ctx.arc(0, 0, r2, 0, Math.PI * 2, true); + // ctx.fillStyle = lgrad( '#eee', '#f0f0f0', r2); + ctx.fillStyle = lgrad('hsla(0, 0%, 33%, 1)', 'hsla(0, 0%, 43%, 1)', r2); + //ctx.fill(); + + ctx.beginPath(); + ctx.arc(0, 0, r3, 0, Math.PI * 2, true); + ctx.fillStyle = config.colors.plate; + //ctx.fill(); + + ctx.save(); + }; + + /** + * Formats a number for display on the dial's plate using the majorTicksFormat config option. + * + * @param {number} num The number to format + * @returns {string} The formatted number + */ + function formatMajorTickNumber(num) { + var r, isDec = false; + + // First, force the correct number of digits right of the decimal. + if (config.majorTicksFormat.dec === 0) { + r = Math.round(num).toString(); + } else { + r = num.toFixed(config.majorTicksFormat.dec); + } + + // Second, force the correct number of digits left of the decimal. + if (config.majorTicksFormat["int"] > 1) { + // Does this number have a decimal? + isDec = (r.indexOf('.') > -1); + + // Is this number a negative number? + if (r.indexOf('-') > -1) { + return '-' + [ + config.majorTicksFormat["int"] + config.majorTicksFormat.dec + 2 + (isDec ? 1 : 0) - r.length + ].join('0') + r.replace('-', ''); + } else { + return [ + config.majorTicksFormat["int"] + config.majorTicksFormat.dec + 1 + (isDec ? 1 : 0) - r.length + ].join('0') + r; + } + } else { + return r; + } + } + + // major ticks draw + function drawMajorTicks() { + var r = max / 100 * 81; + + ctx.lineWidth = 1; + ctx.strokeStyle = config.colors.majorTicks; + ctx.save(); + + if (config.majorTicks.length === 0) { + var numberOfDefaultTicks = 5; + var tickSize = (config.maxValue - config.minValue) / numberOfDefaultTicks; + + for (var i = 0; i < numberOfDefaultTicks; i++) { + config.majorTicks.push(formatMajorTickNumber(config.minValue + (tickSize * i))); + } + config.majorTicks.push(formatMajorTickNumber(config.maxValue)); + } + + for (var i = 0; i < config.majorTicks.length; ++i) { + var a = 45 + i * (270 / (config.majorTicks.length - 1)); + ctx.rotate(radians(a)); + + ctx.beginPath(); + ctx.moveTo(0, r); + ctx.lineTo(0, r - max / 100 * 15); + ctx.stroke(); + + ctx.restore(); + ctx.save(); + } + + if (config.strokeTicks) { + ctx.rotate(radians(90)); + + ctx.beginPath(); + ctx.arc(0, 0, r, radians(45), radians(315), false); + ctx.stroke(); + ctx.restore(); + + ctx.save(); + } + }; + + // minor ticks draw + function drawMinorTicks() { + var r = max / 100 * 81; + + ctx.lineWidth = 1; + ctx.strokeStyle = config.colors.minorTicks; + + ctx.save(); + + var len = config.minorTicks * (config.majorTicks.length - 1); + + for (var i = 0; i < len; ++i) { + var a = 45 + i * (270 / len); + ctx.rotate(radians(a)); + + ctx.beginPath(); + ctx.moveTo(0, r); + ctx.lineTo(0, r - max / 100 * 7.5); + ctx.stroke(); + + ctx.restore(); + ctx.save(); + } + }; + + // tick numbers draw + function drawNumbers() { + //var r = max / 100 * 55; + // + //for (var i = 0; i < config.majorTicks.length; ++i) { + // var + // a = 45 + i * (270 / (config.majorTicks.length - 1)), + // p = rpoint(r, radians(a)) + // ; + // + // ctx.font = 20 * (max / 200) + "px Arial"; + // ctx.fillStyle = config.colors.numbers; + // ctx.lineWidth = 0; + // ctx.textAlign = "center"; + // ctx.fillText(config.majorTicks[i], p.x, p.y + 3); + //} + }; + + // title draw + function drawTitle() { + if (!config.title) { + return; + } + + ctx.save(); + ctx.font = 24 * (max / 200) + "px Arial"; + ctx.fillStyle = config.colors.title; + ctx.textAlign = "center"; + ctx.fillText(config.title, 0, -max / 4.25); + ctx.restore(); + }; + + // units draw + function drawUnits() { + if (!config.units) { + return; + } + + ctx.save(); + ctx.font = 22 * (max / 200) + "px Arial"; + ctx.fillStyle = config.colors.units; + ctx.textAlign = "center"; + ctx.fillText(config.units, 0, max / 3.25); + ctx.restore(); + }; + + + function drawArrow() { + + if (config.arrow != "false") { + + if (config.arrow == "up") { + var r = max / 100 * 2.0; + y0 = max / 100 * 64; + y1 = max / 100 * 55; + y2 = max / 100 * 53; + y3 = max / 100 * 50; + y4 = max / 100 * 45; + arrow_color = "rgba(107, 184, 20, 1)"; + + var x0 = max / 100 * -8 + var x1 = max / 100 * -6 + var x2 = max / 100 * -1.5 + var x3 = max / 100 * 0 + var x4 = max / 100 * 1.5 + var x5 = max / 100 * 6 + var x6 = max / 100 * 8 + + ctx.beginPath(); + ctx.moveTo(x0, y2); + ctx.lineTo(x3 - r, y4 + r); + ctx.arcTo(x3, y4 - r, x3 + r, y4 + r, r * 1.09); + ctx.lineTo(x6, y2); + ctx.arcTo(x6 + r/2.0, y2 + r/1.0, x5, y1, r*.9) + ctx.lineTo(x4, y3); + ctx.lineTo(x4, y0); + ctx.arcTo(x3, y0 + r, x2, y0, r*.9); + ctx.lineTo(x2, y3); + ctx.lineTo(x1, y1); + ctx.arcTo(x1 - r, y1 - r/2.0, x0, y2, r*1.09) + + + ctx.closePath(); + ctx.fillStyle = arrow_color; + ctx.fill(); + } else { + var r = max / 100 * 2.0; + var y0 = max / 100 * 45; + var y1 = max / 100 * 54; + var y2 = max / 100 * 56; + var y3 = max / 100 * 59; + var y4 = max / 100 * 64; + var arrow_color = "rgba(252, 38, 50, 1)"; + + var x0 = max / 100 * -8 + var x1 = max / 100 * -6 + var x2 = max / 100 * -1.5 + var x3 = max / 100 * 0 + var x4 = max / 100 * 1.5 + var x5 = max / 100 * 6 + var x6 = max / 100 * 8 + + ctx.beginPath(); + ctx.moveTo(x0, y2); + ctx.lineTo(x3 - r, y4 - r); + ctx.arcTo(x3, y4 + r, x3 + r, y4 - r, r * 1.09); + ctx.lineTo(x6, y2); + ctx.arcTo(x6 + r/2.0, y2 - r/1.0, x5, y1, r*.9) + ctx.lineTo(x4, y3); + ctx.lineTo(x4, y0); + ctx.arcTo(x3, y0 - r, x2, y0, r*.9); + ctx.lineTo(x2, y3); + ctx.lineTo(x1, y1); + ctx.arcTo(x1 - r, y1 + r/2.0, x0, y2, r*1.09) + + + ctx.closePath(); + ctx.fillStyle = arrow_color; + ctx.fill(); + } + ctx.save(); + ctx.restore(); + } + } + + function padValue(val) { + var cdec = config.valueFormat['dec'] + var cint = config.valueFormat['int'] + + val = parseFloat(val); + var n = (val < 0); + + val = Math.abs(val); + + if (cdec > 0) { + val = val.toFixed(cdec).toString().split('.'); + + for (var i = 0, s = cint - val[0].length; i < s; ++i) { + val[0] = '0' + val[0]; + } + + val = (n ? '-' : '') + val[0] + '.' + val[1]; + } else { + val = Math.round(val).toString(); + + for (var i = 0, s = cint - val.length; i < s; ++i) { + val = '0' + val; + } + + val = (n ? '-' : '') + val + } + + return val; + }; + + function rpoint(r, a) { + var + x = 0, y = r, + + sin = Math.sin(a), + cos = Math.cos(a), + + X = x * cos - y * sin, + Y = x * sin + y * cos + ; + + return { x: X, y: Y }; + }; + function clearCircle(x, y, radius) + { + ctx.beginPath(); + ctx.arc(x, y, radius, 0, 2 * Math.PI, false); + ctx.clip(); + ctx.clearRect(x - radius - 1, y - radius - 1, + radius * 2 + 2, radius * 2 + 2); + + }; + // draws the highlight colors + function drawHighlights() { + ctx.save(); + + var r1 = max / 100 * 81; + var r2 = r1 - max / 100 * 15; + + for (var i = 0, s = config.highlights.length; i < s; i++) { + var + hlt = config.highlights[i], + vd = (config.maxValue - config.minValue) / 270, + sa = radians(45 + (hlt.from - config.minValue) / vd), + ea = radians(45 + (hlt.to - config.minValue) / vd) + ; + + ctx.beginPath(); + + ctx.rotate(radians(90)); + ctx.arc(0, 0, r1, sa, ea, false); + ctx.restore(); + ctx.save(); + + var + ps = rpoint(r2, sa), + pe = rpoint(r1, sa) + ; + ctx.moveTo(ps.x, ps.y); + ctx.lineTo(pe.x, pe.y); + + var + ps1 = rpoint(r1, ea), + pe1 = rpoint(r2, ea) + ; + + ctx.lineTo(ps1.x, ps1.y); + ctx.lineTo(pe1.x, pe1.y); + ctx.lineTo(ps.x, ps.y); + + ctx.closePath(); + + ctx.fillStyle = hlt.color; + ctx.fill(); + + ctx.beginPath(); + ctx.rotate(radians(90)); + ctx.arc(0, 0, r2, sa - 0.2, ea + 0.2, false); + ctx.restore(); + + ctx.closePath(); + + ctx.fillStyle = config.colors.plate; + ctx.fill(); + ctx.save(); + ctx.imageSmoothingEnabled = true + //clearCircle(0, 0, 100) + + } + }; + + // drows the gauge needle + function drawNeedle() { + var + r1 = 0 , + r2 = 0 , + rIn = max / 100 * 85, + rOut = max / 100 * 63, + rP = max / 100 * 59, + pad1 = max / 100 * 3, + pad2 = max / 100 * 2.5, + + shad = function () { + ctx.shadowOffsetX = 2; + ctx.shadowOffsetY = 2; + ctx.shadowBlur = 10; + // ctx.shadowColor = 'rgba(188, 143, 143, 0.45)'; + ctx.shadowColor = 'rgba(50, 50, 50, .3)'; + } + ; + + shad(); + + ctx.save(); + + if (fromValue < 0) { + fromValue = Math.abs(config.minValue - fromValue); + } else if (config.minValue > 0) { + fromValue -= config.minValue + } else { + fromValue = Math.abs(config.minValue) + fromValue; + } + + ctx.rotate(radians(45 + fromValue / ((config.maxValue - config.minValue) / 270))); + + ctx.beginPath(); + ctx.lineTo(-pad2, rOut); + ctx.lineTo(-pad2, rIn); + ctx.lineTo(pad2, rIn); + ctx.lineTo(pad2, rOut); + ctx.lineTo(0, rP); + ctx.closePath(); + ctx.strokeStyle = "#999" + ctx.stroke(); + + ctx.fillStyle = lgrad( + config.colors.needle.start, + config.colors.needle.end, + rIn - rOut + ); + ctx.fill(); + + // ctx.beginPath(); + // ctx.lineTo(-_pad2, _rOut); + // ctx.lineTo(-_pad2, _rIn); + // ctx.lineTo(_pad2, _rIn); + // ctx.lineTo(_pad2, _rOut); + // ctx.lineTo(0, _rOut - 5); + // ctx.closePath(); + + // ctx.fillStyle = "#ccc" + // ctx.fill(); + + ctx.beginPath(); + ctx.lineTo(-pad2, rIn); + ctx.lineTo(-pad2, rIn); + ctx.lineTo(-pad1, 0); + ctx.lineTo(-pad2, rOut); + ctx.lineTo(pad2 / 2 - 2, rOut); + ctx.closePath(); + ctx.fillStyle = 'rgba(255, 255, 255, 0.2)'; + + //ctx.fill(); + + ctx.restore(); + + //shad(); + + ctx.beginPath(); + ctx.arc(0, 0, r2 +.5, 0, Math.PI * 2, true); + // ctx.fillStyle = lgrad( '#f0f0f0', '#ccc', r1); + ctx.fillStyle = lgrad('#3b3b3b', '#121212', r1); + //ctx.fill(); + + ctx.restore(); + + ctx.beginPath(); + ctx.arc(0, 0, r2, 0, Math.PI * 2, true); + // ctx.fillStyle = lgrad( "#e8e8e8", "#f5f5f5", r2); + ctx.fillStyle = 'rgba(255,255,255,1)'; + //ctx.fill(); + }; + + function roundRect(x, y, w, h, r) { + ctx.beginPath(); + + ctx.moveTo(x + r, y); + ctx.lineTo(x + w - r, y); + + ctx.quadraticCurveTo(x + w, y, x + w, y + r); + ctx.lineTo(x + w, y + h - r); + + ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h); + ctx.lineTo(x + r, y + h); + + ctx.quadraticCurveTo(x, y + h, x, y + h - r); + ctx.lineTo(x, y + r); + + ctx.quadraticCurveTo(x, y, x + r, y); + + ctx.closePath(); + }; + + // value box draw + function drawValueBox() { + ctx.save(); + + ctx.font = 100 + " " + 73 * (max / 200) + "px 'roboto-light"; + var + text = padValue(value), + tw = ctx.measureText('-' + padValue(0)).width, + y = max - max / 100 * 96, + x = 0, + th = 0.12 * max + ; + + ctx.save(); + + roundRect( + -tw / 2 - 0.025 * max, + y - th - 0.04 * max, + tw + 0.05 * max, + th + 0.07 * max, + 0.025 * max + ); + + var grd = ctx.createRadialGradient( + x, + y - 0.12 * max - 0.025 * max + (0.12 * max + 0.045 * max) / 2, + max / 10, + x, + y - 0.12 * max - 0.025 * max + (0.12 * max + 0.045 * max) / 2, + max / 5 + ); + + // grd.addColorStop( 0, "#888"); + // grd.addColorStop( 1, "#666"); + + // ctx.strokeStyle = grd; + // ctx.lineWidth = 0.05 * max; + // ctx.stroke(); + + // ctx.shadowBlur = 0.012 * max; + // ctx.shadowColor = 'rgba(0, 0, 0, 1)'; + + // ctx.fillStyle = "#babab2"; + // ctx.fill(); + + // ctx.restore(); + + // ctx.shadowOffsetX = 0.004 * max; + // ctx.shadowOffsetY = 0.004 * max; + // ctx.shadowBlur = 0.012 * max; + // ctx.shadowColor = 'rgba(0, 0, 0, 0.3)'; + + // ctx.fillStyle = "#444"; + ctx.fillStyle = "rgba(50,50,50,1)"; + // ctx.fillStyle = "rgba(50,50,50,1)"; + ctx.textAlign = "center"; + + ctx.fillText(text, -x, y); + + + ctx.save(); + ctx.font = 100 + " " + 20 * (max / 200) + "px 'roboto-light"; + //ctx.fillText(config.unit, -x, y + 30); + ctx.restore(); + + }; + }; + +// initialize + Gauge.initialized = false; + (function () { + var + d = document, + h = d.getElementsByTagName('head')[0], + ie = navigator.userAgent.toLocaleLowerCase().indexOf('msie') != -1, + url = 'fonts/digital-7-mono.' + (ie ? 'eot' : 'ttf'), + + // RW: don't use mono font, this was causing err in js console + text = '', +// text = "@font-face {" + +// "font-family: 'Led';" + +// "src: url('" + url + "');" + +// "}", + ss, + r = d.createElement('style') + ; + + r.type = 'text/css'; + + if (ie) { + h.appendChild(r); + ss = r.styleSheet; + ss.cssText = text; + } else { + try { + r.appendChild(d.createTextNode(text)); + } catch (e) { + r.cssText = text; + } + + h.appendChild(r); + + ss = r.styleSheet ? r.styleSheet : + (r.sheet || d.styleSheets[d.styleSheets.length - 1]) + ; + } + + var iv = setInterval(function () { + if (!d.body) { + return; + } + + clearInterval(iv); + + var dd = d.createElement('div'); + + dd.style.fontFamily = 'Led'; + dd.style.position = 'absolute'; + dd.style.height = dd.style.width = 0; + dd.style.overflow = 'hidden'; + + dd.innerHTML = '.'; + + d.body.appendChild(dd); + + setTimeout(function () { // no other way to handle font is rendered by a browser + // just give the browser around 250ms to do that :( + Gauge.initialized = true; + dd.parentNode.removeChild(dd); + }, 250); + }, 1); + })(); + + Gauge.Collection = []; + Gauge.Collection.get = function (id) { + var self = this; + + if (typeof(id) == 'string') { + for (var i = 0, s = self.length; i < s; i++) { + var canvas = self[i].config.renderTo.tagName ? self[i].config.renderTo : document.getElementById(self[i].config.renderTo); + if (canvas.getAttribute('id') == id) { + return self[i]; + } + } + } else if (typeof(id) == 'number') { + return self[id]; + } else { + return null; + } + }; + + function domReady(handler) { + if (window.addEventListener) { + window.addEventListener('DOMContentLoaded', handler, false); + } else { + window.attachEvent('onload', handler); + } + } + + domReady(function () { + function toCamelCase(arr) { + var str = arr[0]; + for (var i = 1, s = arr.length; i < s; i++) { + str += arr[i].substr(0, 1).toUpperCase() + arr[i].substr(1, arr[i].length - 1); + } + return str; + }; + + function trim(str) { + return str.replace(/^\s+|\s+$/g, ''); + }; + + var c = document.getElementsByTagName('canvas'); + + for (var i = 0, s = c.length; i < s; i++) { + + if (c[i].getAttribute('data-type') == 'canv-gauge') { + var + gauge = c[i], + config = {}, + prop, + w = parseInt(gauge.getAttribute('width'), 10), + h = parseInt(gauge.getAttribute('height'), 10) + ; + + config.renderTo = gauge; + + if (w) { + config.width = w; + } + + if (h) { + config.height = h; + } + + for (var ii = 0, ss = gauge.attributes.length; ii < ss; ii++) { + prop = gauge.attributes.item(ii).nodeName; + + if (prop != 'data-type' && prop.substr(0, 5) == 'data-') { + var + cfgProp = prop.substr(5, prop.length - 5).toLowerCase().split('-'), + attrValue = gauge.getAttribute(prop) + ; + + if (!attrValue) { + continue; + } + + switch (cfgProp[0]) { + case 'colors' : + { + if (cfgProp[1]) { + if (!config.colors) { + config.colors = {}; + } + + if (cfgProp[1] == 'needle') { + var parts = attrValue.split(/\s+/); + + if (parts[0] && parts[1]) { + config.colors.needle = { start: parts[0], end: parts[1] }; + } + else { + config.colors.needle = attrValue; + } + } + else { + cfgProp.shift(); + config.colors[toCamelCase(cfgProp)] = attrValue; + } + } + break; + } + case 'highlights' : + { + if (!config.highlights) { + config.highlights = []; + } + + var hls = attrValue.match(/(?:(?:-?\d*\.)?(-?\d+){1,2} ){2}(?:(?:#|0x)?(?:[0-9A-F|a-f]){3,8}|rgba?\(.*?\))/g); + + for (var j = 0, l = hls.length; j < l; j++) { + var + cfg = trim(hls[j]).split(/\s+/), + hlCfg = {} + ; + + if (cfg[0] && cfg[0] != '') { + hlCfg.from = cfg[0]; + } + + if (cfg[1] && cfg[1] != '') { + hlCfg.to = cfg[1]; + } + + if (cfg[2] && cfg[2] != '') { + hlCfg.color = cfg[2]; + } + + config.highlights.push(hlCfg); + } + break; + } + case 'animation' : + { + if (cfgProp[1]) { + if (!config.animation) { + config.animation = {}; + } + + if (cfgProp[1] == 'fn' && /^\s*function\s*\(/.test(attrValue)) { + attrValue = eval('(' + attrValue + ')'); + } + + config.animation[cfgProp[1]] = attrValue; + } + break; + } + default : + { + var cfgName = toCamelCase(cfgProp); + + if (cfgName == 'onready') { + continue; + } + + if (cfgName == 'majorTicks') { + attrValue = attrValue.split(/\s+/); + } + else if (cfgName == 'strokeTicks' || cfgName == 'glow') { + attrValue = attrValue == 'true' ? true : false; + } + else if (cfgName == 'valueFormat') { + var val = attrValue.split('.'); + + if (val.length == 2) { + attrValue = { + 'int': parseInt(val[0], 10), + 'dec': parseInt(val[1], 10) + } + } + else { + continue; + } + } + + config[cfgName] = attrValue; + break; + } + } + } + } + + var g = new Gauge(config); + + if (gauge.getAttribute('data-value')) { + g.setRawValue(parseFloat(gauge.getAttribute('data-value'))); + } + + if (gauge.getAttribute('data-onready')) { + g.onready = function () { + eval(this.config.renderTo.getAttribute('data-onready')); + }; + } + + g.draw(); + } + } + }); +module.exports = Gauge; + // window['Gauge'] = Gauge; + +// })(window); diff --git a/skyquake/framework/js/n3-line-chart.js b/skyquake/framework/js/n3-line-chart.js new file mode 100644 index 000000000..b3105cd22 --- /dev/null +++ b/skyquake/framework/js/n3-line-chart.js @@ -0,0 +1,1821 @@ + +/* + * + * Copyright 2016 RIFT.IO Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* +line-chart - v1.1.9 - 21 June 2015 +https://github.com/n3-charts/line-chart +Copyright (c) 2015 n3-charts + */ +var directive, m, mod, old_m, + __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; + +old_m = angular.module('n3-charts.linechart', ['n3charts.utils']); + +m = angular.module('n3-line-chart', ['n3charts.utils']); + +directive = function(name, conf) { + old_m.directive(name, conf); + return m.directive(name, conf); +}; + +directive('linechart', [ + 'n3utils', '$window', '$timeout', function(n3utils, $window, $timeout) { + var link; + link = function(scope, element, attrs, ctrl) { + var dispatch, id, initialHandlers, isUpdatingOptions, promise, updateEvents, window_resize, _u; + _u = n3utils; + dispatch = _u.getEventDispatcher(); + id = _u.uuid(); + element[0].style['font-size'] = 0; + scope.redraw = function() { + scope.update(); + }; + isUpdatingOptions = false; + initialHandlers = { + onSeriesVisibilityChange: function(_arg) { + var index, newVisibility, series; + series = _arg.series, index = _arg.index, newVisibility = _arg.newVisibility; + scope.options.series[index].visible = newVisibility; + return scope.$apply(); + } + }; + scope.update = function() { + var axes, columnWidth, dataPerSeries, dimensions, fn, handlers, isThumbnail, options, svg; + options = _u.sanitizeOptions(scope.options, attrs.mode); + handlers = angular.extend(initialHandlers, _u.getTooltipHandlers(options)); + dataPerSeries = _u.getDataPerSeries(scope.data, options); + dimensions = _u.getDimensions(options, element, attrs); + isThumbnail = attrs.mode === 'thumbnail'; + _u.clean(element[0]); + svg = _u.bootstrap(element[0], id, dimensions); + fn = function(key) { + return (options.series.filter(function(s) { + return s.axis === key && s.visible !== false; + })).length > 0; + }; + axes = _u.createAxes(svg, dimensions, options.axes).andAddThemIf({ + all: !isThumbnail, + x: true, + y: fn('y'), + y2: fn('y2') + }); + if (dataPerSeries.length) { + _u.setScalesDomain(axes, scope.data, options.series, svg, options); + } + _u.createContent(svg, id, options, handlers); + if (dataPerSeries.length) { + columnWidth = _u.getBestColumnWidth(axes, dimensions, dataPerSeries, options); + _u.drawArea(svg, axes, dataPerSeries, options, handlers).drawColumns(svg, axes, dataPerSeries, columnWidth, options, handlers, dispatch).drawLines(svg, axes, dataPerSeries, options, handlers); + if (options.drawDots) { + _u.drawDots(svg, axes, dataPerSeries, options, handlers, dispatch); + } + } + if (options.drawLegend) { + _u.drawLegend(svg, options.series, dimensions, handlers, dispatch); + } + if (options.tooltip.mode === 'scrubber') { + return _u.createGlass(svg, dimensions, handlers, axes, dataPerSeries, options, dispatch, columnWidth); + } else if (options.tooltip.mode !== 'none') { + return _u.addTooltips(svg, dimensions, options.axes); + } + }; + updateEvents = function() { + if (scope.oldclick) { + dispatch.on('click', scope.oldclick); + } else if (scope.click) { + dispatch.on('click', scope.click); + } else { + dispatch.on('click', null); + } + if (scope.oldhover) { + dispatch.on('hover', scope.oldhover); + } else if (scope.hover) { + dispatch.on('hover', scope.hover); + } else { + dispatch.on('hover', null); + } + if (scope.oldfocus) { + dispatch.on('focus', scope.oldfocus); + } else if (scope.focus) { + dispatch.on('focus', scope.focus); + } else { + dispatch.on('focus', null); + } + if (scope.toggle) { + return dispatch.on('toggle', scope.toggle); + } else { + return dispatch.on('toggle', null); + } + }; + promise = void 0; + window_resize = function() { + if (promise != null) { + $timeout.cancel(promise); + } + return promise = $timeout(scope.redraw, 1); + }; + $window.addEventListener('resize', window_resize); + scope.$watch('data', scope.redraw, true); + scope.$watch('options', scope.redraw, true); + scope.$watchCollection('[click, hover, focus, toggle]', updateEvents); + scope.$watchCollection('[oldclick, oldhover, oldfocus]', updateEvents); + console.log('data', scope.data) + window_resize(); + }; + return { + replace: true, + restrict: 'E', + scope: { + data: '=', + options: '=', + oldclick: '=click', + oldhover: '=hover', + oldfocus: '=focus', + click: '=onClick', + hover: '=onHover', + focus: '=onFocus', + toggle: '=onToggle' + }, + template: '
', + link: link + }; + } +]); + +mod = angular.module('n3charts.utils', []); + +mod.factory('n3utils', [ + '$window', '$log', '$rootScope', function($window, $log, $rootScope) { + return { + addPatterns: function(svg, series) { + var pattern; + pattern = svg.select('defs').selectAll('pattern').data(series.filter(function(s) { + return s.striped; + })).enter().append('pattern').attr({ + id: function(s) { + return s.type + 'Pattern_' + s.index; + }, + patternUnits: "userSpaceOnUse", + x: 0, + y: 0, + width: 60, + height: 60 + }).append('g').style({ + 'fill': function(s) { + return s.color; + }, + 'fill-opacity': 0.3 + }); + pattern.append('rect').style('fill-opacity', 0.3).attr('width', 60).attr('height', 60); + pattern.append('path').attr('d', "M 10 0 l10 0 l -20 20 l 0 -10 z"); + pattern.append('path').attr('d', "M40 0 l10 0 l-50 50 l0 -10 z"); + pattern.append('path').attr('d', "M60 10 l0 10 l-40 40 l-10 0 z"); + return pattern.append('path').attr('d', "M60 40 l0 10 l-10 10 l -10 0 z"); + }, + drawArea: function(svg, scales, data, options) { + var areaSeries, drawers; + areaSeries = data.filter(function(series) { + return series.type === 'area'; + }); + this.addPatterns(svg, areaSeries); + drawers = { + y: this.createLeftAreaDrawer(scales, options.lineMode, options.tension), + y2: this.createRightAreaDrawer(scales, options.lineMode, options.tension) + }; + svg.select('.content').selectAll('.areaGroup').data(areaSeries).enter().append('g').attr('class', function(s) { + return 'areaGroup ' + 'series_' + s.index; + }).append('path').attr('class', 'area').style('fill', function(s) { + if (s.striped !== true) { + return s.color; + } + return "url(#areaPattern_" + s.index + ")"; + }).style('opacity', function(s) { + if (s.striped) { + return '1'; + } else { + return '0.3'; + } + }).attr('d', function(d) { + return drawers[d.axis](d.values); + }); + return this; + }, + createLeftAreaDrawer: function(scales, mode, tension) { + return d3.svg.area().x(function(d) { + return scales.xScale(d.x); + }).y0(function(d) { + return scales.yScale(d.y0); + }).y1(function(d) { + return scales.yScale(d.y0 + d.y); + }).interpolate(mode).tension(tension); + }, + createRightAreaDrawer: function(scales, mode, tension) { + return d3.svg.area().x(function(d) { + return scales.xScale(d.x); + }).y0(function(d) { + return scales.y2Scale(d.y0); + }).y1(function(d) { + return scales.y2Scale(d.y0 + d.y); + }).interpolate(mode).tension(tension); + }, + getPseudoColumns: function(data, options) { + var keys, pseudoColumns; + data = data.filter(function(s) { + return s.type === 'column'; + }); + pseudoColumns = {}; + keys = []; + data.forEach(function(series) { + var i, inAStack, index; + inAStack = false; + options.stacks.forEach(function(stack, index) { + var _ref; + if ((series.id != null) && (_ref = series.id, __indexOf.call(stack.series, _ref) >= 0)) { + pseudoColumns[series.id] = index; + if (__indexOf.call(keys, index) < 0) { + keys.push(index); + } + return inAStack = true; + } + }); + if (inAStack === false) { + i = pseudoColumns[series.id] = index = keys.length; + return keys.push(i); + } + }); + return { + pseudoColumns: pseudoColumns, + keys: keys + }; + }, + getMinDelta: function(seriesData, key, scale, range) { + return d3.min(seriesData.map(function(series) { + return series.values.map(function(d) { + return scale(d[key]); + }).filter(function(e) { + if (range) { + return e >= range[0] && e <= range[1]; + } else { + return true; + } + }).reduce(function(prev, cur, i, arr) { + var diff; + diff = i > 0 ? cur - arr[i - 1] : Number.MAX_VALUE; + if (diff < prev) { + return diff; + } else { + return prev; + } + }, Number.MAX_VALUE); + })); + }, + getBestColumnWidth: function(axes, dimensions, seriesData, options) { + var colData, delta, innerWidth, keys, nSeries, pseudoColumns, _ref; + if (!(seriesData && seriesData.length !== 0)) { + return 10; + } + if ((seriesData.filter(function(s) { + return s.type === 'column'; + })).length === 0) { + return 10; + } + _ref = this.getPseudoColumns(seriesData, options), pseudoColumns = _ref.pseudoColumns, keys = _ref.keys; + innerWidth = dimensions.width - dimensions.left - dimensions.right; + colData = seriesData.filter(function(d) { + return pseudoColumns.hasOwnProperty(d.id); + }); + delta = this.getMinDelta(colData, 'x', axes.xScale, [0, innerWidth]); + if (delta > innerWidth) { + delta = 0.25 * innerWidth; + } + nSeries = keys.length; + return parseInt((delta - options.columnsHGap) / nSeries); + }, + getColumnAxis: function(data, columnWidth, options) { + var keys, pseudoColumns, x1, _ref; + _ref = this.getPseudoColumns(data, options), pseudoColumns = _ref.pseudoColumns, keys = _ref.keys; + x1 = d3.scale.ordinal().domain(keys).rangeBands([0, keys.length * columnWidth], 0); + return function(s) { + var index; + if (pseudoColumns[s.id] == null) { + return 0; + } + index = pseudoColumns[s.id]; + return x1(index) - keys.length * columnWidth / 2; + }; + }, + drawColumns: function(svg, axes, data, columnWidth, options, handlers, dispatch) { + var colGroup, x1; + data = data.filter(function(s) { + return s.type === 'column'; + }); + x1 = this.getColumnAxis(data, columnWidth, options); + data.forEach(function(s) { + return s.xOffset = x1(s) + columnWidth * .5; + }); + colGroup = svg.select('.content').selectAll('.columnGroup').data(data).enter().append("g").attr('class', function(s) { + return 'columnGroup series_' + s.index; + }).attr('transform', function(s) { + return "translate(" + x1(s) + ",0)"; + }); + colGroup.each(function(series) { + return d3.select(this).selectAll("rect").data(series.values).enter().append("rect").style({ + 'stroke': series.color, + 'fill': series.color, + 'stroke-opacity': function(d) { + if (d.y === 0) { + return '0'; + } else { + return '1'; + } + }, + 'stroke-width': '1px', + 'fill-opacity': function(d) { + if (d.y === 0) { + return 0; + } else { + return 0.7; + } + } + }).attr({ + width: columnWidth, + x: function(d) { + return axes.xScale(d.x); + }, + height: function(d) { + if (d.y === 0) { + return axes[d.axis + 'Scale'].range()[0]; + } + return Math.abs(axes[d.axis + 'Scale'](d.y0 + d.y) - axes[d.axis + 'Scale'](d.y0)); + }, + y: function(d) { + if (d.y === 0) { + return 0; + } else { + return axes[d.axis + 'Scale'](Math.max(0, d.y0 + d.y)); + } + } + }).on({ + 'click': function(d, i) { + return dispatch.click(d, i); + } + }).on('mouseover', function(d, i) { + dispatch.hover(d, i); + return typeof handlers.onMouseOver === "function" ? handlers.onMouseOver(svg, { + series: series, + x: axes.xScale(d.x), + y: axes[d.axis + 'Scale'](d.y0 + d.y), + datum: d + }, options.axes) : void 0; + }).on('mouseout', function(d) { + return typeof handlers.onMouseOut === "function" ? handlers.onMouseOut(svg) : void 0; + }); + }); + return this; + }, + drawDots: function(svg, axes, data, options, handlers, dispatch) { + var dotGroup; + dotGroup = svg.select('.content').selectAll('.dotGroup').data(data.filter(function(s) { + var _ref; + return ((_ref = s.type) === 'line' || _ref === 'area') && s.drawDots; + })).enter().append('g'); + dotGroup.attr({ + "class": function(s) { + return "dotGroup series_" + s.index; + }, + fill: function(s) { + return s.color; + } + }).selectAll('.dot').data(function(d) { + return d.values; + }).enter().append('circle').attr({ + 'class': 'dot', + 'r': function(d) { + return d.dotSize; + }, + 'cx': function(d) { + return axes.xScale(d.x); + }, + 'cy': function(d) { + return axes[d.axis + 'Scale'](d.y + d.y0); + } + }).style({ + 'stroke': 'white', + 'stroke-width': '2px' + }).on({ + 'click': function(d, i) { + return dispatch.click(d, i); + } + }).on({ + 'mouseover': function(d, i) { + return dispatch.hover(d, i); + } + }); + if (options.tooltip.mode !== 'none') { + dotGroup.on('mouseover', function(series) { + var d, target; + target = d3.select(d3.event.target); + d = target.datum(); + target.attr('r', function(s) { + return s.dotSize + 2; + }); + return typeof handlers.onMouseOver === "function" ? handlers.onMouseOver(svg, { + series: series, + x: target.attr('cx'), + y: target.attr('cy'), + datum: d + }, options.axes) : void 0; + }).on('mouseout', function(d) { + d3.select(d3.event.target).attr('r', function(s) { + return s.dotSize; + }); + return typeof handlers.onMouseOut === "function" ? handlers.onMouseOut(svg) : void 0; + }); + } + return this; + }, + getEventDispatcher: function() { + var events; + events = ['focus', 'hover', 'click', 'toggle']; + return d3.dispatch.apply(this, events); + }, + computeLegendLayout: function(svg, series, dimensions) { + var cumul, i, j, leftLayout, leftWidths, padding, rightLayout, rightWidths, that, w; + padding = 10; + that = this; + leftWidths = this.getLegendItemsWidths(svg, 'y'); + leftLayout = [0]; + i = 1; + while (i < leftWidths.length) { + leftLayout.push(leftWidths[i - 1] + leftLayout[i - 1] + padding); + i++; + } + rightWidths = this.getLegendItemsWidths(svg, 'y2'); + if (!(rightWidths.length > 0)) { + return [leftLayout]; + } + w = dimensions.width - dimensions.right - dimensions.left; + cumul = 0; + rightLayout = []; + j = rightWidths.length - 1; + while (j >= 0) { + rightLayout.push(w - cumul - rightWidths[j]); + cumul += rightWidths[j] + padding; + j--; + } + rightLayout.reverse(); + return [leftLayout, rightLayout]; + }, + getLegendItemsWidths: function(svg, axis) { + var bbox, i, items, that, widths; + that = this; + bbox = function(t) { + return that.getTextBBox(t).width; + }; + items = svg.selectAll(".legendItem." + axis); + if (!(items.length > 0)) { + return []; + } + widths = []; + i = 0; + while (i < items[0].length) { + widths.push(bbox(items[0][i])); + i++; + } + return widths; + }, + drawLegend: function(svg, series, dimensions, handlers, dispatch) { + var d, groups, legend, that, translateLegends; + that = this; + legend = svg.append('g').attr('class', 'legend'); + d = 16; + svg.select('defs').append('svg:clipPath').attr('id', 'legend-clip').append('circle').attr('r', d / 2); + groups = legend.selectAll('.legendItem').data(series); + groups.enter().append('g').on('click', function(s, i) { + var visibility; + visibility = !(s.visible !== false); + dispatch.toggle(s, i, visibility); + return typeof handlers.onSeriesVisibilityChange === "function" ? handlers.onSeriesVisibilityChange({ + series: s, + index: i, + newVisibility: visibility + }) : void 0; + }); + groups.attr({ + 'class': function(s, i) { + return "legendItem series_" + i + " " + s.axis; + }, + 'opacity': function(s, i) { + if (s.visible === false) { + that.toggleSeries(svg, i); + return '0.2'; + } + return '1'; + } + }).each(function(s) { + var item, _ref; + item = d3.select(this); + item.append('circle').attr({ + 'fill': s.color, + 'stroke': s.color, + 'stroke-width': '2px', + 'r': d / 2 + }); + item.append('path').attr({ + 'clip-path': 'url(#legend-clip)', + 'fill-opacity': (_ref = s.type) === 'area' || _ref === 'column' ? '1' : '0', + 'fill': 'white', + 'stroke': 'white', + 'stroke-width': '2px', + 'd': that.getLegendItemPath(s, d, d) + }); + item.append('circle').attr({ + 'fill-opacity': 0, + 'stroke': s.color, + 'stroke-width': '2px', + 'r': d / 2 + }); + return item.append('text').attr({ + 'class': function(d, i) { + return "legendText series_" + i; + }, + 'font-family': 'Courier', + 'font-size': 10, + 'transform': 'translate(13, 4)', + 'text-rendering': 'geometric-precision' + }).text(s.label || s.y); + }); + translateLegends = function() { + var left, right, _ref; + _ref = that.computeLegendLayout(svg, series, dimensions), left = _ref[0], right = _ref[1]; + return groups.attr({ + 'transform': function(s, i) { + if (s.axis === 'y') { + return "translate(" + (left.shift()) + "," + (dimensions.height - 40) + ")"; + } else { + return "translate(" + (right.shift()) + "," + (dimensions.height - 40) + ")"; + } + } + }); + }; + translateLegends(); + setTimeout(translateLegends, 0); + return this; + }, + getLegendItemPath: function(series, w, h) { + var base_path, path; + if (series.type === 'column') { + path = 'M' + (-w / 3) + ' ' + (-h / 8) + ' l0 ' + h + ' '; + path += 'M0' + ' ' + (-h / 3) + ' l0 ' + h + ' '; + path += 'M' + w / 3 + ' ' + (-h / 10) + ' l0 ' + h + ' '; + return path; + } + base_path = 'M-' + w / 2 + ' 0' + h / 3 + ' l' + w / 3 + ' -' + h / 3 + ' l' + w / 3 + ' ' + h / 3 + ' l' + w / 3 + ' -' + 2 * h / 3; + if (series.type === 'area') { + base_path + ' l0 ' + h + ' l-' + w + ' 0z'; + } + return base_path; + }, + toggleSeries: function(svg, index) { + var isVisible; + isVisible = false; + svg.select('.content').selectAll('.series_' + index).style('display', function(s) { + if (d3.select(this).style('display') === 'none') { + isVisible = true; + return 'initial'; + } else { + isVisible = false; + return 'none'; + } + }); + return isVisible; + }, + drawLines: function(svg, scales, data, options, handlers) { + var drawers, interpolateData, lineGroup; + drawers = { + y: this.createLeftLineDrawer(scales, options.lineMode, options.tension), + y2: this.createRightLineDrawer(scales, options.lineMode, options.tension) + }; + lineGroup = svg.select('.content').selectAll('.lineGroup').data(data.filter(function(s) { + var _ref; + return (_ref = s.type) === 'line' || _ref === 'area'; + })).enter().append('g'); + lineGroup.style('stroke', function(s) { + return s.color; + }).attr('class', function(s) { + return "lineGroup series_" + s.index; + }).append('path').attr({ + "class": 'line', + d: function(d) { + return drawers[d.axis](d.values); + } + }).style({ + 'fill': 'none', + 'stroke-width': function(s) { + return s.thickness; + }, + 'stroke-dasharray': function(s) { + if (s.lineMode === 'dashed') { + return '10,3'; + } + return void 0; + } + }); + if (options.tooltip.interpolate) { + interpolateData = function(series) { + var datum, error, i, interpDatum, maxXPos, maxXValue, maxYPos, maxYValue, minXPos, minXValue, minYPos, minYValue, mousePos, target, valuesData, x, xPercentage, xVal, y, yPercentage, yVal, _i, _len; + target = d3.select(d3.event.target); + try { + mousePos = d3.mouse(this); + } catch (_error) { + error = _error; + mousePos = [0, 0]; + } + valuesData = target.datum().values; + for (i = _i = 0, _len = valuesData.length; _i < _len; i = ++_i) { + datum = valuesData[i]; + x = scales.xScale(datum.x); + y = scales.yScale(datum.y); + if ((typeof minXPos === "undefined" || minXPos === null) || x < minXPos) { + minXPos = x; + minXValue = datum.x; + } + if ((typeof maxXPos === "undefined" || maxXPos === null) || x > maxXPos) { + maxXPos = x; + maxXValue = datum.x; + } + if ((typeof minYPos === "undefined" || minYPos === null) || y < minYPos) { + minYPos = y; + } + if ((typeof maxYPos === "undefined" || maxYPos === null) || y > maxYPos) { + maxYPos = y; + } + if ((typeof minYValue === "undefined" || minYValue === null) || datum.y < minYValue) { + minYValue = datum.y; + } + if ((typeof maxYValue === "undefined" || maxYValue === null) || datum.y > maxYValue) { + maxYValue = datum.y; + } + } + xPercentage = (mousePos[0] - minXPos) / (maxXPos - minXPos); + yPercentage = (mousePos[1] - minYPos) / (maxYPos - minYPos); + xVal = Math.round(xPercentage * (maxXValue - minXValue) + minXValue); + yVal = Math.round((1 - yPercentage) * (maxYValue - minYValue) + minYValue); + interpDatum = { + x: xVal, + y: yVal + }; + return typeof handlers.onMouseOver === "function" ? handlers.onMouseOver(svg, { + series: series, + x: mousePos[0], + y: mousePos[1], + datum: interpDatum + }, options.axes) : void 0; + }; + lineGroup.on('mousemove', interpolateData).on('mouseout', function(d) { + return typeof handlers.onMouseOut === "function" ? handlers.onMouseOut(svg) : void 0; + }); + } + return this; + }, + createLeftLineDrawer: function(scales, mode, tension) { + return d3.svg.line().x(function(d) { + return scales.xScale(d.x); + }).y(function(d) { + return scales.yScale(d.y + d.y0); + }).interpolate(mode).tension(tension); + }, + createRightLineDrawer: function(scales, mode, tension) { + return d3.svg.line().x(function(d) { + return scales.xScale(d.x); + }).y(function(d) { + return scales.y2Scale(d.y + d.y0); + }).interpolate(mode).tension(tension); + }, + getPixelCssProp: function(element, propertyName) { + var string; + string = $window.getComputedStyle(element, null).getPropertyValue(propertyName); + return +string.replace(/px$/, ''); + }, + getDefaultMargins: function() { + return { + top: 20, + right: 50, + bottom: 60, + left: 50 + }; + }, + getDefaultThumbnailMargins: function() { + return { + top: 1, + right: 1, + bottom: 2, + left: 0 + }; + }, + getElementDimensions: function(element, width, height) { + var bottom, dim, left, parent, right, top; + dim = {}; + parent = element; + top = this.getPixelCssProp(parent, 'padding-top'); + bottom = this.getPixelCssProp(parent, 'padding-bottom'); + left = this.getPixelCssProp(parent, 'padding-left'); + right = this.getPixelCssProp(parent, 'padding-right'); + dim.width = +(width || parent.offsetWidth || 900) - left - right; + dim.height = +(height || parent.offsetHeight || 500) - top - bottom; + return dim; + }, + getDimensions: function(options, element, attrs) { + var dim; + dim = this.getElementDimensions(element[0].parentElement, attrs.width, attrs.height); + dim = angular.extend(options.margin, dim); + return dim; + }, + clean: function(element) { + return d3.select(element).on('keydown', null).on('keyup', null).select('svg').remove(); + }, + uuid: function() { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { + var r, v; + r = Math.random() * 16 | 0; + v = c === 'x' ? r : r & 0x3 | 0x8; + return v.toString(16); + }); + }, + bootstrap: function(element, id, dimensions) { + var defs, height, svg, width; + d3.select(element).classed('chart', true); + width = dimensions.width; + height = dimensions.height; + svg = d3.select(element).append('svg').attr({ + width: width, + height: height + }).append('g').attr('transform', 'translate(' + dimensions.left + ',' + dimensions.top + ')'); + defs = svg.append('defs').attr('class', 'patterns'); + defs.append('clipPath').attr('class', 'content-clip').attr('id', "content-clip-" + id).append('rect').attr({ + 'x': 0, + 'y': 0, + 'width': width - dimensions.left - dimensions.right, + 'height': height - dimensions.top - dimensions.bottom + }); + return svg; + }, + createContent: function(svg, id, options) { + var content; + content = svg.append('g').attr('class', 'content'); + if (options.hideOverflow) { + return content.attr('clip-path', "url(#content-clip-" + id + ")"); + } + }, + createGlass: function(svg, dimensions, handlers, axes, data, options, dispatch, columnWidth) { + var glass, scrubberGroup, that; + that = this; + glass = svg.append('g').attr({ + 'class': 'glass-container', + 'opacity': 0 + }); + scrubberGroup = glass.selectAll('.scrubberItem').data(data).enter().append('g').attr('class', function(s, i) { + return "scrubberItem series_" + i; + }); + scrubberGroup.each(function(s, i) { + var g, g2, item; + item = d3.select(this); + g = item.append('g').attr({ + 'class': "rightTT" + }); + g.append('path').attr({ + 'class': "scrubberPath series_" + i, + 'y': '-7px', + 'fill': s.color + }); + that.styleTooltip(g.append('text').style('text-anchor', 'start').attr({ + 'class': function(d, i) { + return "scrubberText series_" + i; + }, + 'height': '14px', + 'transform': 'translate(7, 3)', + 'text-rendering': 'geometric-precision' + })).text(s.label || s.y); + g2 = item.append('g').attr({ + 'class': "leftTT" + }); + g2.append('path').attr({ + 'class': "scrubberPath series_" + i, + 'y': '-7px', + 'fill': s.color + }); + that.styleTooltip(g2.append('text').style('text-anchor', 'end').attr({ + 'class': "scrubberText series_" + i, + 'height': '14px', + 'transform': 'translate(-13, 3)', + 'text-rendering': 'geometric-precision' + })).text(s.label || s.y); + return item.append('circle').attr({ + 'class': "scrubberDot series_" + i, + 'fill': 'white', + 'stroke': s.color, + 'stroke-width': '2px', + 'r': 4 + }); + }); + return glass.append('rect').attr({ + "class": 'glass', + width: dimensions.width - dimensions.left - dimensions.right, + height: dimensions.height - dimensions.top - dimensions.bottom + }).style('fill', 'white').style('fill-opacity', 0.000001).on('mouseover', function() { + return handlers.onChartHover(svg, d3.select(this), axes, data, options, dispatch, columnWidth); + }); + }, + getDataPerSeries: function(data, options) { + var axes, layout, series, straightened; + series = options.series; + axes = options.axes; + if (!(series && series.length && data && data.length)) { + return []; + } + straightened = series.map(function(s, i) { + var seriesData; + seriesData = { + index: i, + name: s.y, + values: [], + color: s.color, + axis: s.axis || 'y', + xOffset: 0, + type: s.type, + thickness: s.thickness, + drawDots: s.drawDots !== false + }; + if (s.dotSize != null) { + seriesData.dotSize = s.dotSize; + } + if (s.striped === true) { + seriesData.striped = true; + } + if (s.lineMode != null) { + seriesData.lineMode = s.lineMode; + } + if (s.id) { + seriesData.id = s.id; + } + data.filter(function(row) { + return row[s.y] != null; + }).forEach(function(row) { + var d; + d = { + x: row[options.axes.x.key], + y: row[s.y], + y0: 0, + axis: s.axis || 'y' + }; + if (s.dotSize != null) { + d.dotSize = s.dotSize; + } + return seriesData.values.push(d); + }); + return seriesData; + }); + if ((options.stacks == null) || options.stacks.length === 0) { + return straightened; + } + layout = d3.layout.stack().values(function(s) { + return s.values; + }); + options.stacks.forEach(function(stack) { + var layers; + if (!(stack.series.length > 0)) { + return; + } + layers = straightened.filter(function(s, i) { + var _ref; + return (s.id != null) && (_ref = s.id, __indexOf.call(stack.series, _ref) >= 0); + }); + return layout(layers); + }); + return straightened; + }, + estimateSideTooltipWidth: function(svg, text) { + var bbox, t; + t = svg.append('text'); + t.text('' + text); + this.styleTooltip(t); + bbox = this.getTextBBox(t[0][0]); + t.remove(); + return bbox; + }, + getTextBBox: function(svgTextElement) { + var error; + if (svgTextElement !== null) { + try { + return svgTextElement.getBBox(); + } catch (_error) { + error = _error; + return { + height: 0, + width: 0, + y: 0, + x: 0 + }; + } + } + return {}; + }, + getWidestTickWidth: function(svg, axisKey) { + var bbox, max, ticks, _ref; + max = 0; + bbox = this.getTextBBox; + ticks = svg.select("." + axisKey + ".axis").selectAll('.tick'); + if ((_ref = ticks[0]) != null) { + _ref.forEach(function(t) { + return max = Math.max(max, bbox(t).width); + }); + } + return max; + }, + getWidestOrdinate: function(data, series, options) { + var widest; + widest = ''; + data.forEach(function(row) { + return series.forEach(function(series) { + var v, _ref; + v = row[series.y]; + if ((series.axis != null) && ((_ref = options.axes[series.axis]) != null ? _ref.ticksFormatter : void 0)) { + v = options.axes[series.axis].ticksFormatter(v); + } + if (v == null) { + return; + } + if (('' + v).length > ('' + widest).length) { + return widest = v; + } + }); + }); + return widest; + }, + getDefaultOptions: function() { + return { + tooltip: { + mode: 'scrubber' + }, + lineMode: 'linear', + tension: 0.7, + margin: this.getDefaultMargins(), + axes: { + x: { + type: 'linear', + key: 'x' + }, + y: { + type: 'linear' + } + }, + series: [], + drawLegend: true, + drawDots: true, + stacks: [], + columnsHGap: 5, + hideOverflow: false + }; + }, + sanitizeOptions: function(options, mode) { + var defaultMargin; + if (options == null) { + options = {}; + } + if (mode === 'thumbnail') { + options.drawLegend = false; + options.drawDots = false; + options.tooltip = { + mode: 'none', + interpolate: false + }; + } + options.series = this.sanitizeSeriesOptions(options.series); + options.stacks = this.sanitizeSeriesStacks(options.stacks, options.series); + options.axes = this.sanitizeAxes(options.axes, this.haveSecondYAxis(options.series)); + options.tooltip = this.sanitizeTooltip(options.tooltip); + options.margin = this.sanitizeMargins(options.margin); + options.lineMode || (options.lineMode = this.getDefaultOptions().lineMode); + options.tension = /^\d+(\.\d+)?$/.test(options.tension) ? options.tension : this.getDefaultOptions().tension; + options.drawLegend = options.drawLegend !== false; + options.drawDots = options.drawDots !== false; + if (!angular.isNumber(options.columnsHGap)) { + options.columnsHGap = 5; + } + options.hideOverflow = options.hideOverflow || false; + defaultMargin = mode === 'thumbnail' ? this.getDefaultThumbnailMargins() : this.getDefaultMargins(); + options.series = angular.extend(this.getDefaultOptions().series, options.series); + options.axes = angular.extend(this.getDefaultOptions().axes, options.axes); + options.tooltip = angular.extend(this.getDefaultOptions().tooltip, options.tooltip); + options.margin = angular.extend(defaultMargin, options.margin); + return options; + }, + sanitizeMargins: function(options) { + var attrs, margin, opt, value; + attrs = ['top', 'right', 'bottom', 'left']; + margin = {}; + for (opt in options) { + value = options[opt]; + if (__indexOf.call(attrs, opt) >= 0) { + margin[opt] = parseFloat(value); + } + } + return margin; + }, + sanitizeSeriesStacks: function(stacks, series) { + var seriesKeys; + if (stacks == null) { + return []; + } + seriesKeys = {}; + series.forEach(function(s) { + return seriesKeys[s.id] = s; + }); + stacks.forEach(function(stack) { + return stack.series.forEach(function(id) { + var s; + s = seriesKeys[id]; + if (s != null) { + if (s.axis !== stack.axis) { + return $log.warn("Series " + id + " is not on the same axis as its stack"); + } + } else { + if (!s) { + return $log.warn("Unknown series found in stack : " + id); + } + } + }); + }); + return stacks; + }, + sanitizeTooltip: function(options) { + var _ref; + if (!options) { + return { + mode: 'scrubber' + }; + } + if ((_ref = options.mode) !== 'none' && _ref !== 'axes' && _ref !== 'scrubber') { + options.mode = 'scrubber'; + } + if (options.mode === 'scrubber') { + delete options.interpolate; + } else { + options.interpolate = !!options.interpolate; + } + if (options.mode === 'scrubber' && options.interpolate) { + throw new Error('Interpolation is not supported for scrubber tooltip mode.'); + } + return options; + }, + sanitizeSeriesOptions: function(options) { + var colors, knownIds; + if (options == null) { + return []; + } + colors = d3.scale.category10(); + knownIds = {}; + options.forEach(function(s, i) { + if (knownIds[s.id] != null) { + throw new Error("Twice the same ID (" + s.id + ") ? Really ?"); + } + if (s.id != null) { + return knownIds[s.id] = s; + } + }); + options.forEach(function(s, i) { + var cnt, _ref, _ref1, _ref2, _ref3; + s.axis = ((_ref = s.axis) != null ? _ref.toLowerCase() : void 0) !== 'y2' ? 'y' : 'y2'; + s.color || (s.color = colors(i)); + s.type = (_ref1 = s.type) === 'line' || _ref1 === 'area' || _ref1 === 'column' ? s.type : "line"; + if (s.type === 'column') { + delete s.thickness; + delete s.lineMode; + delete s.drawDots; + delete s.dotSize; + } else if (!/^\d+px$/.test(s.thickness)) { + s.thickness = '1px'; + } + if ((_ref2 = s.type) === 'line' || _ref2 === 'area') { + if ((_ref3 = s.lineMode) !== 'dashed') { + delete s.lineMode; + } + if (s.drawDots !== false && (s.dotSize == null)) { + s.dotSize = 2; + } + } + if (s.id == null) { + cnt = 0; + while (knownIds["series_" + cnt] != null) { + cnt++; + } + s.id = "series_" + cnt; + knownIds[s.id] = s; + } + if (s.drawDots === false) { + return delete s.dotSize; + } + }); + return options; + }, + sanitizeAxes: function(axesOptions, secondAxis) { + var _base; + if (axesOptions == null) { + axesOptions = {}; + } + axesOptions.x = this.sanitizeAxisOptions(axesOptions.x); + (_base = axesOptions.x).key || (_base.key = "x"); + axesOptions.y = this.sanitizeAxisOptions(axesOptions.y); + if (secondAxis) { + axesOptions.y2 = this.sanitizeAxisOptions(axesOptions.y2); + } + return axesOptions; + }, + sanitizeExtrema: function(options) { + var max, min; + min = this.getSanitizedNumber(options.min); + if (min != null) { + options.min = min; + } else { + delete options.min; + } + max = this.getSanitizedNumber(options.max); + if (max != null) { + return options.max = max; + } else { + return delete options.max; + } + }, + getSanitizedNumber: function(value) { + var number; + if (value == null) { + return void 0; + } + number = parseFloat(value); + if (isNaN(number)) { + $log.warn("Invalid extremum value : " + value + ", deleting it."); + return void 0; + } + return number; + }, + sanitizeAxisOptions: function(options) { + if (options == null) { + return { + type: 'linear' + }; + } + options.type || (options.type = 'linear'); + if (options.ticksRotate != null) { + options.ticksRotate = this.getSanitizedNumber(options.ticksRotate); + } + if (options.labelFunction != null) { + options.ticksFormatter = options.labelFunction; + } + if (options.ticksFormat != null) { + if (options.type === 'date') { + options.ticksFormatter = d3.time.format(options.ticksFormat); + } else { + options.ticksFormatter = d3.format(options.ticksFormat); + } + if (options.tooltipFormatter == null) { + options.tooltipFormatter = options.ticksFormatter; + } + } + if (options.tooltipFormat != null) { + if (options.type === 'date') { + options.tooltipFormatter = d3.time.format(options.tooltipFormat); + } else { + options.tooltipFormatter = d3.format(options.tooltipFormat); + } + } + if (options.ticksInterval != null) { + options.ticksInterval = this.getSanitizedNumber(options.ticksInterval); + } + this.sanitizeExtrema(options); + return options; + }, + createAxes: function(svg, dimensions, axesOptions) { + var createY2Axis, height, style, width, x, xAxis, y, y2, y2Axis, yAxis; + createY2Axis = axesOptions.y2 != null; + width = dimensions.width; + height = dimensions.height; + width = width - dimensions.left - dimensions.right; + height = height - dimensions.top - dimensions.bottom; + x = void 0; + if (axesOptions.x.type === 'date') { + x = d3.time.scale().rangeRound([0, width]); + } else { + x = d3.scale.linear().rangeRound([0, width]); + } + xAxis = this.createAxis(x, 'x', axesOptions); + y = void 0; + if (axesOptions.y.type === 'log') { + y = d3.scale.log().clamp(true).rangeRound([height, 0]); + } else { + y = d3.scale.linear().rangeRound([height, 0]); + } + y.clamp(true); + yAxis = this.createAxis(y, 'y', axesOptions); + y2 = void 0; + if (createY2Axis && axesOptions.y2.type === 'log') { + y2 = d3.scale.log().clamp(true).rangeRound([height, 0]); + } else { + y2 = d3.scale.linear().rangeRound([height, 0]); + } + y2.clamp(true); + y2Axis = this.createAxis(y2, 'y2', axesOptions); + style = function(group) { + group.style({ + 'font': '10px Courier', + 'shape-rendering': 'crispEdges' + }); + return group.selectAll('path').style({ + 'fill': 'none', + 'stroke': '#000' + }); + }; + return { + xScale: x, + yScale: y, + y2Scale: y2, + xAxis: xAxis, + yAxis: yAxis, + y2Axis: y2Axis, + andAddThemIf: function(conditions) { + if (!!conditions.all) { + if (!!conditions.x) { + svg.append('g').attr('class', 'x axis').attr('transform', 'translate(0,' + height + ')').call(xAxis).call(style); + } + if (!!conditions.y) { + svg.append('g').attr('class', 'y axis').call(yAxis).call(style); + } + if (createY2Axis && !!conditions.y2) { + svg.append('g').attr('class', 'y2 axis').attr('transform', 'translate(' + width + ', 0)').call(y2Axis).call(style); + } + } + return { + xScale: x, + yScale: y, + y2Scale: y2, + xAxis: xAxis, + yAxis: yAxis, + y2Axis: y2Axis + }; + } + }; + }, + createAxis: function(scale, key, options) { + var axis, o, sides; + sides = { + x: 'bottom', + y: 'left', + y2: 'right' + }; + o = options[key]; + axis = d3.svg.axis().scale(scale).orient(sides[key]).tickFormat(o != null ? o.ticksFormatter : void 0); + if (o == null) { + return axis; + } + if (angular.isArray(o.ticks)) { + axis.tickValues(o.ticks); + } else if (angular.isNumber(o.ticks)) { + axis.ticks(o.ticks); + } else if (angular.isFunction(o.ticks)) { + axis.ticks(o.ticks, o.ticksInterval); + } + return axis; + }, + setScalesDomain: function(scales, data, series, svg, options) { + var axis, y2Domain, yDomain; + this.setXScale(scales.xScale, data, series, options.axes); + axis = svg.selectAll('.x.axis').call(scales.xAxis); + if (options.axes.x.ticksRotate != null) { + axis.selectAll('.tick>text').attr('dy', null).attr('transform', 'translate(0,5) rotate(' + options.axes.x.ticksRotate + ' 0,6)').style('text-anchor', options.axes.x.ticksRotate >= 0 ? 'start' : 'end'); + } + if ((series.filter(function(s) { + return s.axis === 'y' && s.visible !== false; + })).length > 0) { + yDomain = this.getVerticalDomain(options, data, series, 'y'); + scales.yScale.domain(yDomain).nice(); + axis = svg.selectAll('.y.axis').call(scales.yAxis); + if (options.axes.y.ticksRotate != null) { + axis.selectAll('.tick>text').attr('transform', 'rotate(' + options.axes.y.ticksRotate + ' -6,0)').style('text-anchor', 'end'); + } + } + if ((series.filter(function(s) { + return s.axis === 'y2' && s.visible !== false; + })).length > 0) { + y2Domain = this.getVerticalDomain(options, data, series, 'y2'); + scales.y2Scale.domain(y2Domain).nice(); + axis = svg.selectAll('.y2.axis').call(scales.y2Axis); + if (options.axes.y2.ticksRotate != null) { + return axis.selectAll('.tick>text').attr('transform', 'rotate(' + options.axes.y2.ticksRotate + ' 6,0)').style('text-anchor', 'start'); + } + } + }, + getVerticalDomain: function(options, data, series, key) { + var domain, mySeries, o; + if (!(o = options.axes[key])) { + return []; + } + if ((o.ticks != null) && angular.isArray(o.ticks)) { + return [o.ticks[0], o.ticks[o.ticks.length - 1]]; + } + mySeries = series.filter(function(s) { + return s.axis === key && s.visible !== false; + }); + domain = this.yExtent(series.filter(function(s) { + return s.axis === key && s.visible !== false; + }), data, options.stacks.filter(function(stack) { + return stack.axis === key; + })); + if (o.type === 'log') { + domain[0] = domain[0] === 0 ? 0.001 : domain[0]; + } + if (o.min != null) { + domain[0] = o.min; + } + if (o.max != null) { + domain[1] = o.max; + } + return domain; + }, + yExtent: function(series, data, stacks) { + var groups, maxY, minY; + minY = Number.POSITIVE_INFINITY; + maxY = Number.NEGATIVE_INFINITY; + groups = []; + stacks.forEach(function(stack) { + return groups.push(stack.series.map(function(id) { + return (series.filter(function(s) { + return s.id === id; + }))[0]; + })); + }); + series.forEach(function(series, i) { + var isInStack; + isInStack = false; + stacks.forEach(function(stack) { + var _ref; + if (_ref = series.id, __indexOf.call(stack.series, _ref) >= 0) { + return isInStack = true; + } + }); + if (!isInStack) { + return groups.push([series]); + } + }); + groups.forEach(function(group) { + group = group.filter(Boolean); + minY = Math.min(minY, d3.min(data, function(d) { + return group.reduce((function(a, s) { + return Math.min(a, d[s.y]); + }), Number.POSITIVE_INFINITY); + })); + return maxY = Math.max(maxY, d3.max(data, function(d) { + return group.reduce((function(a, s) { + return a + d[s.y]; + }), 0); + })); + }); + if (minY === maxY) { + if (minY > 0) { + return [0, minY * 2]; + } else { + return [minY * 2, 0]; + } + } + return [minY, maxY]; + }, + setXScale: function(xScale, data, series, axesOptions) { + var domain, o; + domain = this.xExtent(data, axesOptions.x.key); + if (series.filter(function(s) { + return s.type === 'column'; + }).length) { + this.adjustXDomainForColumns(domain, data, axesOptions.x.key); + } + o = axesOptions.x; + if (o.min != null) { + domain[0] = o.min; + } + if (o.max != null) { + domain[1] = o.max; + } + return xScale.domain(domain); + }, + xExtent: function(data, key) { + var from, to, _ref; + _ref = d3.extent(data, function(d) { + return d[key]; + }), from = _ref[0], to = _ref[1]; + if (from === to) { + if (from > 0) { + return [0, from * 2]; + } else { + return [from * 2, 0]; + } + } + return [from, to]; + }, + adjustXDomainForColumns: function(domain, data, field) { + var step; + step = this.getAverageStep(data, field); + if (angular.isDate(domain[0])) { + domain[0] = new Date(domain[0].getTime() - step); + return domain[1] = new Date(domain[1].getTime() + step); + } else { + domain[0] = domain[0] - step; + return domain[1] = domain[1] + step; + } + }, + getAverageStep: function(data, field) { + var i, n, sum; + if (!(data.length > 1)) { + return 0; + } + sum = 0; + n = data.length - 1; + i = 0; + while (i < n) { + sum += data[i + 1][field] - data[i][field]; + i++; + } + return sum / n; + }, + haveSecondYAxis: function(series) { + return !series.every(function(s) { + return s.axis !== 'y2'; + }); + }, + showScrubber: function(svg, glass, axes, data, options, dispatch, columnWidth) { + var that; + that = this; + glass.on('mousemove', function() { + svg.selectAll('.glass-container').attr('opacity', 1); + return that.updateScrubber(svg, d3.mouse(this), axes, data, options, dispatch, columnWidth); + }); + return glass.on('mouseout', function() { + glass.on('mousemove', null); + return svg.selectAll('.glass-container').attr('opacity', 0); + }); + }, + getClosestPoint: function(values, xValue) { + var d, d0, d1, i, xBisector; + xBisector = d3.bisector(function(d) { + return d.x; + }).left; + i = xBisector(values, xValue); + if (i === 0) { + return values[0]; + } + if (i > values.length - 1) { + return values[values.length - 1]; + } + d0 = values[i - 1]; + d1 = values[i]; + d = xValue - d0.x > d1.x - xValue ? d1 : d0; + return d; + }, + updateScrubber: function(svg, _arg, axes, data, options, dispatch, columnWidth) { + var ease, positions, that, tickLength, x, y; + x = _arg[0], y = _arg[1]; + ease = function(element) { + return element.transition().duration(50); + }; + that = this; + positions = []; + data.forEach(function(series, index) { + var color, item, lText, left, rText, right, side, sizes, text, v, xInvert, xPos, yInvert; + item = svg.select(".scrubberItem.series_" + index); + if (options.series[index].visible === false) { + item.attr('opacity', 0); + return; + } + item.attr('opacity', 1); + xInvert = axes.xScale.invert(x); + yInvert = axes.yScale.invert(y); + v = that.getClosestPoint(series.values, xInvert); + dispatch.focus(v, series.values.indexOf(v), [xInvert, yInvert]); + text = v.x + ' : ' + v.y; + if (options.tooltip.formatter) { + text = options.tooltip.formatter(v.x, v.y, options.series[index]); + } + right = item.select('.rightTT'); + rText = right.select('text'); + rText.text(text); + left = item.select('.leftTT'); + lText = left.select('text'); + lText.text(text); + sizes = { + right: that.getTextBBox(rText[0][0]).width + 5, + left: that.getTextBBox(lText[0][0]).width + 5 + }; + side = series.axis === 'y2' ? 'right' : 'left'; + xPos = axes.xScale(v.x); + if (side === 'left') { + if (xPos + that.getTextBBox(lText[0][0]).x - 10 < 0) { + side = 'right'; + } + } else if (side === 'right') { + if (xPos + sizes.right > that.getTextBBox(svg.select('.glass')[0][0]).width) { + side = 'left'; + } + } + if (side === 'left') { + ease(right).attr('opacity', 0); + ease(left).attr('opacity', 1); + } else { + ease(right).attr('opacity', 1); + ease(left).attr('opacity', 0); + } + positions[index] = { + index: index, + x: xPos, + y: axes[v.axis + 'Scale'](v.y + v.y0), + side: side, + sizes: sizes + }; + color = angular.isFunction(series.color) ? series.color(v, series.values.indexOf(v)) : series.color; + item.selectAll('circle').attr('stroke', color); + return item.selectAll('path').attr('fill', color); + }); + positions = this.preventOverlapping(positions); + tickLength = Math.max(15, 100 / columnWidth); + return data.forEach(function(series, index) { + var item, p, tt, xOffset; + if (options.series[index].visible === false) { + return; + } + p = positions[index]; + item = svg.select(".scrubberItem.series_" + index); + tt = item.select("." + p.side + "TT"); + xOffset = (p.side === 'left' ? series.xOffset : -series.xOffset); + tt.select('text').attr('transform', function() { + if (p.side === 'left') { + return "translate(" + (-3 - tickLength - xOffset) + ", " + (p.labelOffset + 3) + ")"; + } else { + return "translate(" + (4 + tickLength + xOffset) + ", " + (p.labelOffset + 3) + ")"; + } + }); + tt.select('path').attr('d', that.getScrubberPath(p.sizes[p.side] + 1, p.labelOffset, p.side, tickLength + xOffset)); + return ease(item).attr({ + 'transform': "translate(" + (positions[index].x + series.xOffset) + ", " + positions[index].y + ")" + }); + }); + }, + getScrubberPath: function(w, yOffset, side, padding) { + var h, p, xdir, ydir; + h = 18; + p = padding; + w = w; + xdir = side === 'left' ? 1 : -1; + ydir = 1; + if (yOffset !== 0) { + ydir = Math.abs(yOffset) / yOffset; + } + yOffset || (yOffset = 0); + return ["m0 0", "l" + xdir + " 0", "l0 " + (yOffset + ydir), "l" + (-xdir * (p + 1)) + " 0", "l0 " + (-h / 2 - ydir), "l" + (-xdir * w) + " 0", "l0 " + h, "l" + (xdir * w) + " 0", "l0 " + (-h / 2 - ydir), "l" + (xdir * (p - 1)) + " 0", "l0 " + (-yOffset + ydir), "l1 0", "z"].join(''); + }, + preventOverlapping: function(positions) { + var abscissas, getNeighbours, h, offset; + h = 18; + abscissas = {}; + positions.forEach(function(p) { + var _name; + abscissas[_name = p.x] || (abscissas[_name] = { + left: [], + right: [] + }); + return abscissas[p.x][p.side].push(p); + }); + getNeighbours = function(side) { + var foundNeighbour, neighbourhood, neighbours, neighboursForX, p, sides, x, y, _ref; + neighbours = []; + for (x in abscissas) { + sides = abscissas[x]; + if (sides[side].length === 0) { + continue; + } + neighboursForX = {}; + while (sides[side].length > 0) { + p = sides[side].pop(); + foundNeighbour = false; + for (y in neighboursForX) { + neighbourhood = neighboursForX[y]; + if ((+y - h <= (_ref = p.y) && _ref <= +y + h)) { + neighbourhood.push(p); + foundNeighbour = true; + } + } + if (!foundNeighbour) { + neighboursForX[p.y] = [p]; + } + } + neighbours.push(neighboursForX); + } + return neighbours; + }; + offset = function(neighboursForAbscissas) { + var abs, n, neighbours, start, step, xNeighbours, y; + step = 20; + for (abs in neighboursForAbscissas) { + xNeighbours = neighboursForAbscissas[abs]; + for (y in xNeighbours) { + neighbours = xNeighbours[y]; + n = neighbours.length; + if (n === 1) { + neighbours[0].labelOffset = 0; + continue; + } + neighbours = neighbours.sort(function(a, b) { + return a.y - b.y; + }); + if (n % 2 === 0) { + start = -(step / 2) * (n / 2); + } else { + start = -(n - 1) / 2 * step; + } + neighbours.forEach(function(neighbour, i) { + return neighbour.labelOffset = start + step * i; + }); + } + } + }; + offset(getNeighbours('left')); + offset(getNeighbours('right')); + return positions; + }, + getTooltipHandlers: function(options) { + if (options.tooltip.mode === 'scrubber') { + return { + onChartHover: angular.bind(this, this.showScrubber) + }; + } else { + return { + onMouseOver: angular.bind(this, this.onMouseOver), + onMouseOut: angular.bind(this, this.onMouseOut) + }; + } + }, + styleTooltip: function(d3TextElement) { + return d3TextElement.attr({ + 'font-family': 'monospace', + 'font-size': 10, + 'fill': 'white', + 'text-rendering': 'geometric-precision' + }); + }, + addTooltips: function(svg, dimensions, axesOptions) { + var h, height, p, w, width, xTooltip, y2Tooltip, yTooltip; + width = dimensions.width; + height = dimensions.height; + width = width - dimensions.left - dimensions.right; + height = height - dimensions.top - dimensions.bottom; + w = 24; + h = 18; + p = 5; + xTooltip = svg.append('g').attr({ + 'id': 'xTooltip', + 'class': 'xTooltip', + 'opacity': 0 + }); + xTooltip.append('path').attr('transform', "translate(0," + (height + 1) + ")"); + this.styleTooltip(xTooltip.append('text').style('text-anchor', 'middle').attr({ + 'width': w, + 'height': h, + 'transform': 'translate(0,' + (height + 19) + ')' + })); + yTooltip = svg.append('g').attr({ + id: 'yTooltip', + "class": 'yTooltip', + opacity: 0 + }); + yTooltip.append('path'); + this.styleTooltip(yTooltip.append('text').attr({ + 'width': h, + 'height': w + })); + if (axesOptions.y2 != null) { + y2Tooltip = svg.append('g').attr({ + 'id': 'y2Tooltip', + 'class': 'y2Tooltip', + 'opacity': 0, + 'transform': 'translate(' + width + ',0)' + }); + y2Tooltip.append('path'); + return this.styleTooltip(y2Tooltip.append('text').attr({ + 'width': h, + 'height': w + })); + } + }, + onMouseOver: function(svg, event, axesOptions) { + this.updateXTooltip(svg, event, axesOptions.x); + if (event.series.axis === 'y2') { + return this.updateY2Tooltip(svg, event, axesOptions.y2); + } else { + return this.updateYTooltip(svg, event, axesOptions.y); + } + }, + onMouseOut: function(svg) { + return this.hideTooltips(svg); + }, + updateXTooltip: function(svg, _arg, xAxisOptions) { + var color, datum, label, series, textX, x, xTooltip, _f; + x = _arg.x, datum = _arg.datum, series = _arg.series; + xTooltip = svg.select("#xTooltip"); + xTooltip.transition().attr({ + 'opacity': 1.0, + 'transform': "translate(" + x + ",0)" + }); + _f = xAxisOptions.tooltipFormatter; + textX = _f ? _f(datum.x) : datum.x; + label = xTooltip.select('text'); + label.text(textX); + color = angular.isFunction(series.color) ? series.color(datum, series.values.indexOf(datum)) : series.color; + return xTooltip.select('path').style('fill', color).attr('d', this.getXTooltipPath(label[0][0])); + }, + getXTooltipPath: function(textElement) { + var h, p, w; + w = Math.max(this.getTextBBox(textElement).width, 15); + h = 18; + p = 5; + return 'm-' + w / 2 + ' ' + p + ' ' + 'l0 ' + h + ' ' + 'l' + w + ' 0 ' + 'l0 ' + '' + (-h) + 'l' + (-w / 2 + p) + ' 0 ' + 'l' + (-p) + ' -' + h / 4 + ' ' + 'l' + (-p) + ' ' + h / 4 + ' ' + 'l' + (-w / 2 + p) + ' 0z'; + }, + updateYTooltip: function(svg, _arg, yAxisOptions) { + var color, datum, label, series, textY, w, y, yTooltip, _f; + y = _arg.y, datum = _arg.datum, series = _arg.series; + yTooltip = svg.select("#yTooltip"); + yTooltip.transition().attr({ + 'opacity': 1.0, + 'transform': "translate(0, " + y + ")" + }); + _f = yAxisOptions.tooltipFormatter; + textY = _f ? _f(datum.y) : datum.y; + label = yTooltip.select('text'); + label.text(textY); + w = this.getTextBBox(label[0][0]).width + 5; + label.attr({ + 'transform': 'translate(' + (-w - 2) + ',3)', + 'width': w + }); + color = angular.isFunction(series.color) ? series.color(datum, series.values.indexOf(datum)) : series.color; + return yTooltip.select('path').style('fill', color).attr('d', this.getYTooltipPath(w)); + }, + updateY2Tooltip: function(svg, _arg, yAxisOptions) { + var color, datum, label, series, textY, w, y, y2Tooltip, _f; + y = _arg.y, datum = _arg.datum, series = _arg.series; + y2Tooltip = svg.select("#y2Tooltip"); + y2Tooltip.transition().attr('opacity', 1.0); + _f = yAxisOptions.tooltipFormatter; + textY = _f ? _f(datum.y) : datum.y; + label = y2Tooltip.select('text'); + label.text(textY); + w = this.getTextBBox(label[0][0]).width + 5; + label.attr({ + 'transform': 'translate(7, ' + (parseFloat(y) + 3) + ')', + 'w': w + }); + color = angular.isFunction(series.color) ? series.color(datum, series.values.indexOf(datum)) : series.color; + return y2Tooltip.select('path').style('fill', color).attr({ + 'd': this.getY2TooltipPath(w), + 'transform': 'translate(0, ' + y + ')' + }); + }, + getYTooltipPath: function(w) { + var h, p; + h = 18; + p = 5; + return 'm0 0' + 'l' + (-p) + ' ' + (-p) + ' ' + 'l0 ' + (-h / 2 + p) + ' ' + 'l' + (-w) + ' 0 ' + 'l0 ' + h + ' ' + 'l' + w + ' 0 ' + 'l0 ' + (-h / 2 + p) + 'l' + (-p) + ' ' + p + 'z'; + }, + getY2TooltipPath: function(w) { + var h, p; + h = 18; + p = 5; + return 'm0 0' + 'l' + p + ' ' + p + ' ' + 'l0 ' + (h / 2 - p) + ' ' + 'l' + w + ' 0 ' + 'l0 ' + (-h) + ' ' + 'l' + (-w) + ' 0 ' + 'l0 ' + (h / 2 - p) + ' ' + 'l' + (-p) + ' ' + p + 'z'; + }, + hideTooltips: function(svg) { + svg.select("#xTooltip").transition().attr('opacity', 0); + svg.select("#yTooltip").transition().attr('opacity', 0); + return svg.select("#y2Tooltip").transition().attr('opacity', 0); + } + }; + } +]); diff --git a/skyquake/framework/style/_colors.scss b/skyquake/framework/style/_colors.scss new file mode 100644 index 000000000..9378984ba --- /dev/null +++ b/skyquake/framework/style/_colors.scss @@ -0,0 +1,124 @@ + +/* + * + * Copyright 2016 RIFT.IO Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +$darker-gray: rgb(91,91,91); +$dark-gray: rgb(211, 211, 211); +$lightest-gray: rgb(221,221,221); +$light-green: rgb(147, 203, 67); +$light-blue: rgb(0, 168, 235); +$lighter-blue: rgb(159, 196, 244); + + +$primary-header: $light-green; +$secondary-header: $dark-gray; +$body-color:$lightest-gray; + +$error-red:#FF5F5F; + +//PC + +$black: #000; +$gray-lightest: #f1f1f1; +$gray-lighter: #e5e5e5; +$gray-light: #dadada; +$gray: #ccc; +$gray-dark: #999; +$gray-darker: #666; +$gray-darkest: #333; +$white: #FFF; +// +// Brand Colors +// +$brand-blue-light: #30baef; +$brand-blue: #00acee; +$brand-blue-dark: #147ca3; +$brand-green-light: #93cb43; +$brand-green: #7cc118; + + +/* + New Styles. Phase out old above +*/ + +/* NEUTRL +############################################################################ */ + +$neutral-white: hsl( 0, 100%, 100%); +$neutral-light-1: hsl(360, 100%, 50%); +$neutral-light-2: hsl(360, 100%, 50%); +$neutral-light-3: hsl(360, 100%, 50%); +$neutral-light-4: hsl(360, 100%, 50%); +$neutral-light-5: hsl(360, 100%, 50%); + +$neutral-dark-1: hsl(360, 100%, 50%); +$neutral-dark-2: hsl(360, 100%, 50%); +$neutral-dark-3: hsl(360, 100%, 50%); +$neutral-dark-4: hsl(360, 100%, 50%); +$neutral-dark-5: hsl(360, 100%, 50%); +$netral-black: hsl(0, 100%, 0%); + + + +/* SUCCESS, WARNING, ALERT +############################################################################ */ + +$success: hsl(198, 59%, 56%); +$warning: hsl(39, 89%, 67%); +$alert: hsl(5, 100%, 71%); + + + +/* ROOT +############################################################################ */ + +$header: hsl(218, 18%, 17%); +$body: hsl(181, 6%, 95%); + + + +/* BASE +############################################################################ */ + +$primary: hsl(204, 100%, 39%); +$focus: hsla(204, 100%, 39%, 1); + + + +/* BUTTON +############################################################################ */ +/* Normal */ +$normalBackground: hsl(211, 3%, 91%); +$normalForeground: hsl(211, 3%, 11%); + +$normalHoverBackground: darken($normalBackground, 10%); +$normalHoverForeground: $normalForeground; + +$normalActiveBackground: saturate($primary, 50%); +$normalActiveForeground: $neutral-white; + +/* Primary */ +$primaryBackground: $primary; +$primaryForeground: $neutral-white; + +$primaryHoverBackground: darken($primaryBackground, 10%); +$primaryHoverForeground: $primaryForeground; + +$primaryActiveBackground: saturate($primary, 50%); +$primaryActiveForeground: $neutral-white; + diff --git a/skyquake/framework/style/base.scss b/skyquake/framework/style/base.scss new file mode 100644 index 000000000..02dbee058 --- /dev/null +++ b/skyquake/framework/style/base.scss @@ -0,0 +1,433 @@ +/* /fonts +############################################################################ */ + +/* Source Sans Pro */ +@font-face{ + font-family: 'Source Sans Pro'; + font-stretch: normal; + font-style: normal; + font-weight: 200; + src: url('/fonts/SourceSansPro-ExtraLight.otf.woff') format('woff'); +} + +@font-face{ + font-family: 'Source Sans Pro'; + font-stretch: normal; + font-style: italic; + font-weight: 200; + src: url('/fonts/SourceSansPro-ExtraLightIt.otf.woff') format('woff'); +} + +@font-face{ + font-family: 'Source Sans Pro'; + font-stretch: normal; + font-style: normal; + font-weight: 300; + src: url('/fonts/SourceSansPro-Light.otf.woff') format('woff'); +} + +@font-face{ + font-family: 'Source Sans Pro'; + font-stretch: normal; + font-style: italic; + font-weight: 300; + src: url('/fonts/SourceSansPro-LightIt.otf.woff') format('woff'); +} + +@font-face{ + font-family: 'Source Sans Pro'; + font-stretch: normal; + font-style: normal; + font-weight: 400; + src: url('/fonts/SourceSansPro-Regular.otf.woff') format('woff'); +} + +@font-face{ + font-family: 'Source Sans Pro'; + font-stretch: normal; + font-style: italic; + font-weight: 400; + src: url('/fonts/SourceSansPro-It.otf.woff') format('woff'); +} + +@font-face{ + font-family: 'Source Sans Pro'; + font-stretch: normal; + font-style: normal; + font-weight: 600; + src: url('/fonts/SourceSansPro-Semibold.otf.woff') format('woff'); +} + +@font-face{ + font-family: 'Source Sans Pro'; + font-stretch: normal; + font-style: italic; + font-weight: 600; + src: url('/fonts/SourceSansPro-SemiboldIt.otf.woff') format('woff'); +} + +@font-face{ + font-family: 'Source Sans Pro'; + font-stretch: normal; + font-style: normal; + font-weight: 700; + src: url('/fonts/SourceSansPro-Bold.otf.woff') format('woff'); +} + +@font-face{ + font-family: 'Source Sans Pro'; + font-stretch: normal; + font-style: italic; + font-weight: 700; + src: url('/fonts/SourceSansPro-BoldIt.otf.woff') format('woff'); +} + +@font-face{ + font-family: 'Source Sans Pro'; + font-stretch: normal; + font-style: normal; + font-weight: 900; + src: url('/fonts/SourceSansPro-Black.otf.woff') format('woff'); +} + +@font-face{ + font-family: 'Source Sans Pro'; + font-stretch: normal; + font-style: italic; + font-weight: 900; + src: url('/fonts/SourceSansPro-BlackIt.otf.woff') format('woff'); +} + +/* Source Sans Code */ +@font-face{ + font-family: 'Source Sans Code'; + font-stretch: normal; + font-style: normal; + font-weight: 200; + src: url('/fonts/SourceSansCode-ExtraLight.otf.woff') format('woff'); +} + +@font-face{ + font-family: 'Source Sans Code'; + font-stretch: normal; + font-style: italic; + font-weight: 200; + src: url('/fonts/SourceSansCode-ExtraLightIt.otf.woff') format('woff'); +} + +@font-face{ + font-family: 'Source Sans Code'; + font-stretch: normal; + font-style: normal; + font-weight: 300; + src: url('/fonts/SourceSansCode-Light.otf.woff') format('woff'); +} + +@font-face{ + font-family: 'Source Sans Code'; + font-stretch: normal; + font-style: italic; + font-weight: 300; + src: url('/fonts/SourceSansCode-LightIt.otf.woff') format('woff'); +} + +@font-face{ + font-family: 'Source Sans Code'; + font-stretch: normal; + font-style: normal; + font-weight: 400; + src: url('/fonts/SourceSansCode-Regular.otf.woff') format('woff'); +} + +@font-face{ + font-family: 'Source Sans Code'; + font-stretch: normal; + font-style: italic; + font-weight: 400; + src: url('/fonts/SourceSansCode-It.otf.woff') format('woff'); +} + +@font-face{ + font-family: 'Source Sans Code'; + font-stretch: normal; + font-style: normal; + font-weight: 600; + src: url('/fonts/SourceSansCode-Semibold.otf.woff') format('woff'); +} + +@font-face{ + font-family: 'Source Sans Code'; + font-stretch: normal; + font-style: italic; + font-weight: 600; + src: url('/fonts/SourceSansCode-SemiboldIt.otf.woff') format('woff'); +} + +@font-face{ + font-family: 'Source Sans Code'; + font-stretch: normal; + font-style: normal; + font-weight: 700; + src: url('/fonts/SourceSansCode-Bold.otf.woff') format('woff'); +} + +@font-face{ + font-family: 'Source Sans Code'; + font-stretch: normal; + font-style: italic; + font-weight: 700; + src: url('/fonts/SourceSansCode-BoldIt.otf.woff') format('woff'); +} + +@font-face{ + font-family: 'Source Sans Code'; + font-stretch: normal; + font-style: normal; + font-weight: 900; + src: url('/fonts/SourceSansCode-Black.otf.woff') format('woff'); +} + +@font-face{ + font-family: 'Source Sans Code'; + font-stretch: normal; + font-style: italic; + font-weight: 900; + src: url('/fonts/SourceSansCode-BlackIt.otf.woff') format('woff'); +} + + +/* ICONS +############################################################################ */ + +.svg-24px { + fill: black; + height: 24px; + width: 24px; +} + + + +/* ROOT +############################################################################ */ + +*, *::before, *::after { + box-sizing: border-box; +} + +html { + font-family: "Source Sans Pro", helvetica, arial, sans-serif; + font-size: 16px; + height: 100%; + line-height: 1; + -ms-text-size-adjust: 100%; + -webkit-text-size-adjust: 100%; + width: 100%; +} + +body { + height: 100%; + margin: 0; + width: 100% +} + + + +/* DISPLAY +############################################################################ */ + +article, +aside, +details, +figcaption, +figure, +footer, +header, +main, +menu, +nav, +section, +summary { + display: block; +} + +progress { + vertical-align: baseline; +} + + + +/* LINKS +############################################################################ */ + +a { + background-color: transparent; + -webkit-text-decoration-skip: objects; +} + +a:active, +a:hover { + outline-width: 0; +} + + + +/* TEXT-LEVEL SEMANTICS +############################################################################ */ + +abbr[title] { + border-bottom: none; + text-decoration: underline; + text-decoration: underline dotted; +} + +b, +strong { + font-weight: bolder; +} + +h1 { + font-size: 2em; + margin: 0.67em 0; +} + +small { + font-size: 80%; +} + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} +sub { + bottom: -0.25em; +} +sup { + top: -0.5em; +} + + + +/* EMBEDDED CONTENT +############################################################################ */ + +svg:not(:root) { + overflow: hidden; +} + + + +/* GROUPING CONTENT +############################################################################ */ + +code, +kbd, +pre, +samp { + font-family: monospace, monospace; + font-size: 1em; +} + +hr { + box-sizing: content-box; + height: 0; + overflow: visible; +} + +dl { + -webkit-margin-before: 0px; + -webkit-margin-after: 0px; +} + +/* FORMS +############################################################################ */ + +button, +input, +select, +textarea { + font: inherit; + margin: 0; +} + +optgroup { + font-weight: bold; +} + +button, +input { + overflow: visible; +} + +button, +select { + text-transform: none; +} + +button, +html [type="button"], +[type="reset"], +[type="submit"] { + -webkit-appearance: button; +} + +button::-moz-focus-inner, +[type="button"]::-moz-focus-inner, +[type="reset"]::-moz-focus-inner, +[type="submit"]::-moz-focus-inner { + border-style: none; + padding: 0; +} + +button:-moz-focusring, +[type="button"]:-moz-focusring, +[type="reset"]:-moz-focusring, +[type="submit"]:-moz-focusring { + outline: 1px dotted ButtonText; +} + +fieldset { + border: 1px solid #c0c0c0; + margin: 0 2px; + padding: 0.35em 0.625em 0.75em; +} + +legend { + box-sizing: border-box; + color: inherit; + display: table; + max-width: 100%; + padding: 0; + white-space: normal; +} + +textarea { + overflow: auto; +} + +[type="number"]::-webkit-inner-spin-button, +[type="number"]::-webkit-outer-spin-button { + height: auto; +} + +[type="search"] { + -webkit-appearance: textfield; + outline-offset: -2px; +} + +[type="search"]::-webkit-search-cancel-button, +[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +::-webkit-input-placeholder { + color: inherit; + opacity: 0.54; +} + +::-webkit-file-upload-button { + -webkit-appearance: button; + font: inherit; +} diff --git a/skyquake/framework/style/common.scss b/skyquake/framework/style/common.scss new file mode 100644 index 000000000..db161b26d --- /dev/null +++ b/skyquake/framework/style/common.scss @@ -0,0 +1,127 @@ + +/* + * + * Copyright 2016 RIFT.IO Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +@import '../../node_modules/open-iconic/font/css/open-iconic.css'; +@import './_colors.scss'; + +$green: #31D2B1; +$black: #2D3036; +$white: #F8F9FC; +$headers: #EFF5FE; +$grey: #ABB6C6; + +/* Mixins */ + +@mixin arrow($size, $color) { + width: 0; + height:0; + &--up { + border-left: $size solid transparent; + border-right: $size solid transparent; + + border-bottom: $size solid $color; + } + &--down { + border-left: $size solid transparent; + border-right: $size solid transparent; + + border-top: $size solid $color; + } + &--left { + border-top: $size solid transparent; + border-bottom: $size solid transparent; + + border-right: $size solid $color; + } + &--right { + border-top: $size solid transparent; + border-bottom: $size solid transparent; + + border-left: $size solid $color; + } +} + + +/* General */ + + +.crouton { + color: #FFF; + line-height: 140%; + border-radius: 3px; + position: fixed; + top: 0px; + left: 0px; + right: 0px; + max-width: 700px; + width: 90%; + text-align: center; + z-index: 99999; + margin: 0 auto; + div { + padding: 20px; + &.info { + background-color: #1F8DD6; + } + &.success { + background-color: #50CD84; + } + &.warning { + background-color: #FF8859; + } + &.error { + background-color: #FF5F5F; + } + span { + display: block; + margin-bottom: 0; + margin-left: 0px; + } + .buttons { + text-align: right; + button { + &.btn, &.retry, &.close, &.cancel, &.ignore { + padding: 0px; + border: 1px solid transparent; + border-radius: 3px; + } + &:hover { + opacity: .75; + } + &.retry { + border-color: #FFF; + } + &.btn { + &.close { + border-color: #FF5F5F; + margin-left: 10px; + } + } + } + } + } +} +.noticeSubText { + padding-bottom:0.5rem; + color:$darker-gray; + font-size:0.75rem; + &_right { + text-align:right; + } +} diff --git a/skyquake/framework/style/core.css b/skyquake/framework/style/core.css new file mode 100644 index 000000000..2a64a8c57 --- /dev/null +++ b/skyquake/framework/style/core.css @@ -0,0 +1,2062 @@ +/* + * + * Copyright 2016 RIFT.IO Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +@import "./vendor/css-reset-2.0/css-reset.css"; +.has-drop-shadow { + box-shadow: 2px 2px rgba(0, 0, 0, .15) +} +em.circle { + border: 1px solid #333; + border-radius: 50%; + display: inline-block; + padding: 0 .225rem +} +@font-face { + font-family: roboto-regular; + src: url(./fonts/Roboto-Regular-webfont.eot); + src: url(./fonts/Roboto-Regular-webfont.eot?#iefix) format('embedded-opentype'), url(./fonts/Roboto-Regular-webfont.woff) format('woff'), url(./fonts/Roboto-Regular-webfont.ttf) format('truetype'), url(./fonts/Roboto-Regular-webfont.svg#robotoregular) format('svg') +} +@font-face { + font-family: roboto-condensed-bold; + src: url(./fonts/RobotoCondensed-Bold-webfont.eot); + src: url(./fonts/RobotoCondensed-Bold-webfont.eot?#iefix) format('embedded-opentype'), url(./fonts/RobotoCondensed-Bold-webfont.woff) format('woff'), url(./fonts/RobotoCondensed-Bold-webfont.ttf) format('truetype'), url(./fonts/RobotoCondensed-Bold-webfont.svg#roboto_condensedbold) format('svg') +} +@font-face { + font-family: roboto-light; + src: url(./fonts/Roboto-Light-webfont.ttf) +} +body { + font-family: roboto-regular, Helvetica, Arial, sans-serif; + line-height: 1.2 +} +h1 { + font-size: 1.625rem; + font-weight: 400 +} +h2 { + font-size: 1.375rem +} +em.large { + font-size: 1.75rem +} +.all-caps { + font-weight: 700; + text-transform: uppercase +} +.small-caps { + font-size: .6875rem; + font-weight: 700; + text-transform: uppercase +} +.unit { + font-size: .5625rem; + font-weight: 700; + text-transform: uppercase +} +.label { + font-size: .625rem; + font-weight: 700; + text-transform: uppercase +} +body { + background: #f1f1f1; + margin: 0; + padding: 0 +} +.flex-row { + display: flex; + flex-direction: row +} +.flex-center { + align-items: center; + display: flex; + justify-content: center +} +.header-app { + background-color: #f1f1f1; + height: 91px; + padding: 20px 0; + position: relative +} +.header-app h1 { + background: url(./img/header-logo.png) no-repeat left center; + height: 51px; + line-height: 51px; + margin-left: 80px; + padding-left: 118px; + position: absolute; + left: 0; + text-transform: uppercase +} +.header-app nav.launchpad-nav { + box-sizing: border-box; + height: 28px; + line-height: 28px; + position: absolute; + right: 80px; + top: 32px +} +.header-app nav.launchpad-nav:before, +.header-app nav.launchpad-nav:after { + content: " "; + display: block +} +.header-app nav.launchpad-nav:after { + clear: both +} +.header-app nav.launchpad-nav a { + border-left: 1px solid #e5e5e5; + display: block; + float: left; + height: 28px; + margin-left: 20px; + padding-left: 20px; + color:black; + text-decoration:none; +} +.header-app nav.launchpad-nav h4 { + display: inline-block; + font-size: 11px; + margin-right: 8px; + text-transform: uppercase +} +.header-app nav.launchpad-nav img { + width: 28px +} +.header-app nav.viewport-nav { + position: absolute; + right: 0 +} +.header-app nav.viewport-nav a { + text-decoration: none +} +.header-app nav.viewport-nav h2 { + color: #30baef; + font-size: 100%; + height: 54px; + line-height: 54px; + margin-right: 24px; + padding-right: 55px; + text-transform: uppercase +} +.header-app nav.viewport-nav.top h2 { + background: url(./img/viewport-nav-top.png) no-repeat right center +} +.header-app nav.viewport-nav.right h2 { + background: url(./img/viewport-nav-right.png) no-repeat right center +} +.header-app nav.viewport-nav.bottom h2 { + background: url(./img/viewport-nav-bottom.png) no-repeat right center +} +.header-app nav.viewport-nav.left h2 { + background: url(./img/viewport-nav-left.png) no-repeat right center +} +.header-app nav.viewport-nav.center h2 { + background: url(./img/viewport-nav-center.png) no-repeat right center +} +.app-body { + margin-bottom: 12px +} +.app-body:before, +.app-body:after { + content: " "; + display: block +} +.app-body:after { + clear: both +} +.has-corner-accents { + position: relative +} +.corner-accent { + border: 1px solid #000; + display: block; + height: 4px; + position: absolute; + width: 4px +} +.corner-accent.top { + border-bottom: 0; + top: -1px +} +.corner-accent.right { + border-left: 0; + right: -1px +} +.corner-accent.bottom { + border-top: 0; + bottom: -1px +} +.corner-accent.left { + border-right: 0; + left: -1px +} +.has-diag-corners { + position: relative +} +.has-diag-corners.top:before { + border-left: 149px solid #dadada; + border-top: 134px solid #ccc; + content: ''; + position: absolute; + left: 0; + top: 0; + width: 0 +} +.has-diag-corners.top:after { + border-right: 149px solid #dadada; + border-top: 134px solid #ccc; + content: ''; + position: absolute; + right: 0; + top: 0; + width: 0 +} +.has-diag-corners.bottom:before { + border-right: 149px solid #ccc; + border-top: 134px solid #dadada; + content: ''; + position: absolute; + left: 0; + top: 0; + width: 0 +} +.has-diag-corners.bottom:after { + border-left: 149px solid #ccc; + border-top: 134px solid #dadada; + content: ''; + position: absolute; + right: 0; + top: 0; + width: 0 +} +.active { + background-color: #00acee!important; + border-color: #00acee!important; + color: #fff!important +} +.active-text { + color: #00acee!important +} +.status-clear { + background: #93cb43!important; + color: #fff +} +.status-clear-text { + color: #93cb43 +} +.status-info { + background: #30baef!important; + color: #fff +} +.status-info-text { + color: #30baef +} +.dropdown-set { + align-items: center; + display: flex; + justify-content: center +} +.dropdown { + position: relative +} +.dropdown>a { + background-color: #00acee; + color: #fff; + display: block; + margin: 0 .875rem; + padding: .625rem; + position: relative; + width: 16rem; + box-shadow: 2px 2px rgba(0, 0, 0, .15) +} +.dropdown>a i { + font-size: 1.375rem; + position: absolute; + right: .5rem; + top: .5rem +} +.dropdown>ul { + display: none; + position: absolute; + left: 0; + top: 0 +} +.fleet-card { + background-color: rgba(203, 209, 209, .5); + flex: 0 1 48%; + height: 403px; + margin: 12px 0 12px 20px +} +.fleet-card .layout-col.left { + flex: 0 1 69% +} +.fleet-card .layout-col.right { + flex: 0 1 31% +} +.fleet-card-header { + color: #fff +} +.fleet-card-pwr-status { + background-color: #93cb43; + position: relative +} +.fleet-card-pwr-status:before, +.fleet-card-pwr-status:after { + content: " "; + display: block +} +.fleet-card-pwr-status:after { + clear: both +} +.fleet-card-pwr-status a[href] { + display: inline-block; + height: 53px; + width: 89% +} +.fleet-card-pwr-status h2 { + color: #fff; + float: left; + font-size: 16px; + font-weight: 400; + margin: 21px 0 0 24px; + text-transform: uppercase +} +.fleet-card-pwr-status .button-open-viewport { + position: absolute; + right: 12px; + top: 19px; + width: 20px +} +.fleet-card-pwr-btn { + background-color: #333; + box-shadow: 2px 2px rgba(0, 0, 0, .35); + color: #93cb43; + cursor: pointer; + display: inline-block; + float: left; + padding: 12px +} +.fleet-card-env-status { + align-items: center; + background-color: #7cc118; + font-size: 10px; + height: 75px; + display: flex; + justify-content: flex-start +} +.fleet-card-env-status h3 { + font-size: 12px; + text-transform: uppercase +} +.fleet-card-env-status i { + float: left; + font-size: 30px; + margin: 12px +} +.fleet-card-env-status>div { + float: left; + margin-left: 22px +} +.fleet-card-graph { + border: 2px solid rgba(252, 252, 252, .5); + border-right: 0; + border-bottom: 0; + flex: 0 1 50%; + padding: 24px 0; + text-align: center +} +.fleet-card-graph:first-child { + border-left: 0 +} +.fleet-card-graph h3 { + font-size: 12px; + font-weight: 700; + text-transform: uppercase +} +.fleet-card-graph img { + margin-top: 18px; + width: 75% +} +.fleet-card-graph-container { + height: 271px +} +.fleet-card-params { + background-color: #e5e5e5; + border-left: 2px solid rgba(252, 252, 252, .5); + font-size: 11px; + text-align: center +} +.fleet-card-params dt { + background-color: #ccc; + padding: 8px 0 4px; + text-transform: uppercase +} +.fleet-card-params dd { + background-color: #ccc; + border-bottom: 2px solid #f1f1f1; + padding: 4px 0 8px +} +.fleet-card-params-detail { + background: #fff; + box-sizing: border-box; + font-size: 11px; + text-align: center +} +.fleet-card-params-detail.sla-panel, +.fleet-card-params-detail.nfvi-panel { + cursor: pointer; + height: 403px; + padding: 0 20px +} +.fleet-card-params-detail h4 { + padding-top: 24px; + text-transform: uppercase +} +.fleet-card-params-detail h4 i { + font-size: 16px; + margin-left: 18px +} +.fleet-card-params-detail li { + border-top: 1px solid #e5e5e5; + margin-top: 20px; + padding-top: 20px +} +.fleet-card-params-detail li:first-child { + border: 0 +} +.fleet-card-params-detail.trafgen-panel { + height: 403px; + position: relative +} +.fleet-card-params-detail.trafgen-panel h4 { + padding-top: 24px; + text-transform: uppercase +} +.fleet-card-params-detail.trafgen-panel h4 i { + font-size: 16px; + margin-left: 18px +} +.fleet-card-params-detail.trafgen-panel .slider-container { + font-size: 12px; + padding: 0 50px; + margin-top: 48px; + justify-content: space-between +} +.fleet-card-params-detail.trafgen-panel .slider-vert { + flex: 0 1 25%; + margin: 0 +} +.fleet-card-params-detail.trafgen-panel .slider-vert-track { + width: 30px +} +.fleet-card-params-detail.trafgen-panel .slider-vert-header { + font-size: 10px; + margin-bottom: 6px +} +.fleet-card-params-detail.trafgen-panel .slider-vert-start-val { + margin-bottom: 3px +} +.fleet-card-params-detail.trafgen-panel .slider-vert-end-val { + margin-top: 3px +} +.fleet-card-params-detail.trafgen-panel .packet-size-slider .slider-vert-header { + margin-left: -16px +} +.fleet-card-params-detail.trafgen-panel .packet-size-slider .slider-vert-position { + top: 130px +} +.fleet-card-params-detail.trafgen-panel .packet-size-slider .slider-vert-position.left { + width: 69p +} +.fleet-card-params-detail.trafgen-panel .rate-slider .slider-vert-position { + top: 40px +} +.fleet-card-toggle-set { + border-bottom: 2px solid #f1f1f1; + padding: 18px +} +.fleet-card-toggle-set a[role=button] { + box-shadow: 2px 2px rgba(0, 0, 0, .15) +} +.fleet-card-toggle-set a:last-child { + margin-bottom: 0 +} +.fleet-card-actions { + background-color: #ccc; + padding: 22px 18px +} +.fleet-card-actions .button-set { + margin-top: 6px +} +.fleet-card-actions a[role=button] { + display: block; + float: left; + text-align: center; + text-transform: uppercase; + box-shadow: 2px 2px rgba(0, 0, 0, .15) +} +.fleet-card-actions .trafgen-start { + background-color: #333; + color: #fff; + flex: 0 1 80%; + margin-right: 2px; + padding: 8px 0 +} +.fleet-card-actions .trafgen-start i { + margin-right: 6px +} +.fleet-card-actions .trafgen-toggle { + background-color: #fff; + color: #333; + flex: 0 1 20%; + padding: 2px 8px 2px 11px; + font-size: 21px +} +.fleet-card-toggle { + background-color: #fff; + box-shadow: 4px 4px #e5e5e5; + color: #30baef; + cursor: pointer; + display: block; + margin-bottom: 12px; + padding: 20px +} +.fleet-card-toggle h4 { + color: #000; + margin-bottom: 6px; + text-transform: uppercase +} +.fleet-card.empty-card { + font-size: 12px; + text-align: center; + text-transform: uppercase +} +.fleet-card.empty-card a { + color: #000; + display: block; + font-weight: 700; + height: 100%; + text-decoration: none; + width: 100% +} +.fleet-card.empty-card a img { + margin-top: 130px +} +.fleet-card.empty-card a p { + margin-top: 24px +} +.navpanel-container { + background-color: #dadada; + box-sizing: border-box; + display: flex; + flex-flow: row wrap +} +.navpanel { + background-color: #ccc; + box-sizing: border-box; + color: #000; + padding: 1.5rem 0; + text-align: center; + text-decoration: none +} +.navpanel.top { + flex: 1 0 100%; + order: 1 +} +.navpanel.right { + order: 4 +} +.navpanel.bottom { + align-self: baseline; + flex: 0 1 100%; + order: 5 +} +.navpanel.left { + order: 2 +} +.navpanel.left, +.navpanel.right { + background-color: #dadada; + flex: 0 1 auto; + padding: 0 1.5rem; + width: 148px +} +.mainpanel { + flex: 2 1; + order: 3 +} +.progress-bar { + background: #fff; + height: 30px; + line-height: 30px; + padding-right: 12px; + position: relative; + text-align: right +} +.progress-bar span { + background: #30baef; + color: #fff; + display: block; + font-size: .75rem; + height: 30px; + padding: 0 .75em; + position: absolute; + left: 0; + top: 0 +} +.config-parent { + cursor: pointer +} +.config-list { + display: block; + margin-left: 10px +} +.config-list.closed { + display: none +} +.slider-container { + text-align: center +} +.slider-vert { + float: left; + width: 42px; + margin-left: 47px!important +} +.slider-vert-header { + font-size: 11px; + margin-bottom: 4px; + text-transform: uppercase; + white-space: nowrap +} +.slider-vert-start-val, +.slider-vert-end-val { + font-size: 14px +} +.slider-vert-track { + border-left: 1px solid #ccc; + border-right: 1px solid #ccc; + height: 200px; + position: relative; + width: 30px +} +.slider-vert-handle { + background-color: #00acee; + box-shadow: 2px 2px rgba(0, 0, 0, .2); + display: inline-block; + height: 16px; + width: 34px +} +.slider-vert-position { + color: #30baef; + font-size: 12px; + position: absolute; + white-space: nowrap!important +} +.slider-vert-position.left { + left: -38px +} +.slider-vert-position.left .slider-vert-handle { + float: right; + margin-left: 6px +} +.slider-vert-position.right { + left: -3px +} +.slider-vert-position.right .slider-vert-handle { + float: left; + margin-right: 6px +} +.slider-toggle { + float: left; + margin: 12px 27px 12px 0; + white-space: nowrap; + width: 43% +} +.slider-toggle:before, +.slider-toggle:after { + content: " "; + display: block +} +.slider-toggle:after { + clear: both +} +.slider-toggle label { + display: block; + font-size: 13px; + margin-bottom: 8px; + text-transform: uppercase +} +.slider-toggle-track { + box-shadow: 2px 2px rgba(0, 0, 0, .15) +} +.slider-toggle-track:before, +.slider-toggle-track:after { + content: " "; + display: block +} +.slider-toggle-track:after { + clear: both +} +.slider-toggle-option { + background-color: #fff; + color: #dadada; + float: left; + font-size: 12px; + margin-top: -4px; + padding: 8px; + text-align: center; + width: 50% +} +a[role=button] { + cursor: pointer +} +.button-action-toggle { + background-color: #333; + box-shadow: 4px 4px #e5e5e5; + color: #fff; + cursor: pointer; + display: block; + padding: 12px 0; + text-align: center; + text-transform: uppercase +} +.button-action-toggle i { + margin-right: 6px +} +.button-half-border { + border: 2px solid #000; + border-top: 0; + box-shadow: 2px 2px rgba(0, 0, 0, .35); + padding: 4px 24px; + position: relative; + font-size: .6875rem; + font-weight: 700; + text-transform: uppercase +} +.button-half-border:before, +.button-half-border:after { + background: #ccc; + content: ''; + display: block; + height: 10px; + position: absolute; + top: 0; + width: 4px +} +.button-half-border:before { + left: -4px +} +.button-half-border:after { + right: -4px +} +.step-control { + position: relative; + text-align: center +} +.step-control .step { + cursor: pointer; + display: block; + position: absolute; + top: 0 +} +.step-control .step.decrease { + left: 0 +} +.step-control .step.increase { + right: 0 +} +.toggle-set { + align-items: center; + display: flex; + justify-content: center +} +.toggle-control { + align-content: center; + display: flex; + width: 18.75rem; + box-shadow: 2px 2px rgba(0, 0, 0, .15) +} +.toggle-control>a { + background-color: #fff; + color: #333; + flex: 0 1 9.375rem; + padding: .625rem 0; + align-items: center; + display: flex; + justify-content: center +} +.toggle-control>a.selected { + background-color: #00acee; + color: #fff +} +.splitterContainer { + -webkit-flex: 1; + -moz-flex: 1; + -ms-flex: 1; + flex: 1; + display: flex; + -webkit-flex-direction: column; + -moz-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column +} +.splitterContainer .content { + margin-right: 60px +} +.splitterContainer #handle { + z-index: 3; + position: relative; + cursor: row-resize; + min-height: 20px +} +.splitterContainer .splitter { + height: 32px; + width: 100%; + box-shadow: none; + background-image: none; + align-items: center; + padding: 4px; + border-top: 1px solid #121212; + border-bottom: 1px solid #121212; + background-color: #454545; + z-index: 999 +} +.splitterContainer .splitter .adjust { + position: absolute; + right: 0; + top: 0; + margin-top: 5px +} +.splitterContainer .splitter .adjust .splitterButtons { + position: relative; + float: left; + border: 1px solid #fff; + border-radius: 8px; + fill: #fff; + height: 15px; + width: 15px; + cursor: pointer; + color: #fff +} +.splitterContainer .vpane-top { + flex: 1; + overflow-y: scroll +} +.splitterContainer .vpane-bottom { + display: block; + overflow-y: scroll; + padding: 10px; + position: relative +} +.launchpad { + flex-wrap: wrap +} +.create-fleet-header { + color: #00acee; + display: inline-block; + margin: 0 0 20px 24px; + position: relative +} +.create-fleet-header img { + position: absolute; + right: -24px; + bottom: 0; + width: 16px +} +.create-fleet-services, +.create-fleet-pool { + background-color: #e5e5e5 +} +.create-fleet>.flex-row { + justify-content: space-around +} +.create-fleet>.flex-row>li { + flex: 0 1 30% +} +.create-fleet>.flex-row>li h3 { + background-color: #fff; + padding: 12px 18px; + text-transform: uppercase +} +.create-fleet .list-fleet-services { + flex-wrap: wrap; + text-align: center; + margin: 24px auto; + width: 90% +} +.create-fleet .list-fleet-services a { + background-color: #fff; + flex: 0 1 32%; + margin: 0 4px 4px 0; + padding: 4px; + box-shadow: 2px 2px rgba(0, 0, 0, .15) +} +.create-fleet .list-fleet-services a h5 { + font-size: 10px; + margin: 5px 0 0 5px; + text-align: left +} +.create-fleet .list-fleet-services a img { + margin-top: 10px; + width: 70% +} +.create-fleet .list-pools { + padding: 24px +} +.create-fleet .list-pools li a { + background-color: #fff; + display: block; + font-size: 12px; + margin-bottom: 24px; + padding: 12px; + text-align: center; + box-shadow: 2px 2px rgba(0, 0, 0, .15) +} +.create-fleet .list-pools li h4 { + text-align: left +} +.create-fleet .list-pools li img { + margin-top: 24px; + width: 65% +} +.create-fleet .parameter-controls { + background-color: #e5e5e5; + padding: 18px 0 18px 24px +} +.create-fleet .parameter-controls:before, +.create-fleet .parameter-controls:after { + content: " "; + display: block +} +.create-fleet .parameter-controls:after { + clear: both +} +.create-fleet .parameter-controls.epa label { + display: block; + float: left; + margin: 6px 0 0 +} +.create-fleet .parameter-controls.epa .slider-toggle { + width: 100% +} +.create-fleet .parameter-controls.epa .slider-toggle-track { + float: right; + margin-right: 27px; + width: 43% +} +.create-fleet .parameter-controls.epa .slider-toggle-option { + color: #333 +} +.create-fleet .parameter-controls.vmf li { + font-size: 13px; + margin-bottom: 12px +} +.create-fleet .parameter-controls.vmf li:before, +.create-fleet .parameter-controls.vmf li:after { + content: " "; + display: block +} +.create-fleet .parameter-controls.vmf li:after { + clear: both +} +.create-fleet .parameter-controls.vmf label { + display: block; + float: left; + padding: 10px 0 6px; + text-transform: uppercase +} +.create-fleet .parameter-controls.vmf .step-control { + background-color: #fff; + color: #30baef; + float: right; + margin-right: 27px; + padding: 10px 0 6px; + text-align: center; + width: 175px +} +.create-fleet .parameter-controls.vmf .step-control .step { + font-size: 16px; + height: 31px; + width: 31px; + align-items: center; + display: flex; + justify-content: center +} +.create-fleet .parameter-controls.vmf .step-control .step.decrease { + background-color: #ccc; + border-right: 2px solid #e5e5e5; + color: #666 +} +.create-fleet .parameter-controls.vmf .step-control .step.increase { + background-color: #fff; + border-left: 2px solid #e5e5e5; + color: #333; + box-shadow: 2px 2px rgba(0, 0, 0, .15) +} +.create-fleet .adv-toggle h4 { + background-color: #fff; + color: #333 +} +.create-fleet-params h4 { + background-color: #00acee; + color: #fff; + font-size: 12px; + padding: 6px 22px; + position: relative; + text-transform: uppercase; + box-shadow: 2px 2px rgba(0, 0, 0, .15) +} +.create-fleet-params h4 i { + font-size: 18px; + position: absolute; + right: 12px; + top: 6px +} +.create-fleet-actions { + clear: both; + margin-top: 36px; + text-align: center +} +.create-fleet-actions a { + color: #000; + display: inline-block; + font-size: 12px; + padding: 8px 64px; + text-decoration: none; + text-transform: uppercase; + box-shadow: 2px 2px rgba(0, 0, 0, .15) +} +.create-fleet-actions a.save { + background-color: #fff; + border: 1px solid #ccc; + border-top: 0; + cursor: pointer; + margin-right: 48px +} +.create-fleet-actions a.launch { + background-color: #333; + border: 1px solid #000; + border-top: 0; + color: #fff +} +.create-fleet-actions a.launch:hover, +.create-fleet-actions a.launch:active { + background: #00acee; + color: #fff +} +.viewport-dash { + font-size: .75rem +} +.viewport-dash .mainpanel { + background: #f1f1f1; + border-left: .1875rem solid #c2c2c2; + border-right: .1875rem solid #c2c2c2; + box-sizing: border-box; + padding: 2.25rem +} +.viewport-dash .mainpanel.flex-row { + align-items: flex-start; + flex-wrap: wrap +} +.viewport-dash .mainpanel.flex-row .flex-left { + flex: 0 1 45% +} +.viewport-dash .mainpanel.flex-row .flex-right { + flex: 0 1 55% +} +.viewport-dash .mainpanel.flex-row .flex-right .flex-row { + align-items: stretch; + flex-wrap: wrap +} +.viewport-dash .mainpanel.flex-row .flex-right .flex-row .sla-container { + flex: 0 1 75% +} +.viewport-dash .mainpanel.flex-row .flex-right .flex-row .network-container { + flex: 0 1 25% +} +.viewport-dash .mainpanel.flex-row .flex-right .flex-row .mgmt-container { + flex: 0 1 100% +} +.viewport-dash .mainpanel h2 { + background-color: #fff; + margin-bottom: .125rem; + font-size: .75rem; + padding: 1.125rem; + font-weight: 700; + text-transform: uppercase +} +.viewport-dash .mainpanel table { + text-align: center +} +.viewport-dash .mainpanel table th { + background: #dadada; + padding: .375rem; + font-size: .6875rem; + font-weight: 700; + text-transform: uppercase +} +.viewport-dash .mainpanel table td { + background: #e5e5e5; + border-right: .0625rem solid #e5e5e5; + padding: .5625rem 1.5rem +} +.viewport-dash .mainpanel table td .status-clear-text { + margin-bottom: .75rem +} +.viewport-dash .mano { + margin-bottom: 1.5rem +} +.viewport-dash .mano dl { + align-items: center; + background: #7cc118; + color: #fff +} +.viewport-dash .mano dl:before, +.viewport-dash .mano dl:after { + content: " "; + display: block +} +.viewport-dash .mano dl:after { + clear: both +} +.viewport-dash .mano dl dt, +.viewport-dash .mano dl dd { + display: flex; + padding: .375rem 1.125rem +} +.viewport-dash .mano dl dt { + float: left; + font-size: .6875rem; + font-weight: 700; + text-transform: uppercase +} +.viewport-dash .mano dl dd { + float: right +} +.viewport-dash .mano table { + width: 100% +} +.viewport-dash .mano table td:last-child { + border: 0 +} +.viewport-dash .vcs table { + border-collapse: separate; + border-spacing: .0625rem solid; + width: 100% +} +.viewport-dash .vcs table thead th { + background-color: #e5e5e5; + border-right: 1px solid #dadada; + padding: 1.125rem 0 +} +.viewport-dash .vcs table thead th:last-child { + border: 0 +} +.viewport-dash .vcs table tbody td { + width: 30%; + border-bottom: 1px solid #e5e5e5 +} +.viewport-dash .vcs table tbody th { + padding-left: 1.125rem; + text-align: left; + width: 40% +} +.viewport-dash .vcs table tbody tr:nth-child(even) th { + background-color: #e5e5e5 +} +.viewport-dash .sla { + flex: 1 1 60%; + flex-wrap: nowrap; + margin: 0 1.5rem +} +.viewport-dash .sla h3 { + font-size: .6875rem; + font-weight: 700; + text-transform: uppercase +} +.viewport-dash .sla .wp-pool-manager { + flex: 0 1 30% +} +.viewport-dash .sla .wp { + background: #dadada; + margin-bottom: .125rem; + padding: .75rem; + text-align: center +} +.viewport-dash .sla .wp h3 { + text-align: left +} +.viewport-dash .sla .wp img { + margin: .875rem 0; + width: 90% +} +.viewport-dash .sla .pool-manager { + background: #dadada; + padding: .6875rem +} +.viewport-dash .sla .pool-manager .progress-bar { + margin: .75rem 0 +} +.viewport-dash .sla table.stats { + border-left: 2px solid #f1f1f1; + flex: 0 1 70%; + text-align: left; + width: 70% +} +.viewport-dash .sla table.stats th, +.viewport-dash .sla table.stats td { + border-bottom: 1px solid #f1f1f1; + border-right: 1px solid #f1f1f1; + padding: .375rem; + vertical-align: middle; + width: 50% +} +.viewport-dash .sla table.stats td { + text-align: right +} +.viewport-dash .sla table.stats th { + background: #dadada +} +.viewport-dash .sla table.stats th.alt { + background: #e5e5e5 +} +.viewport-dash .network li { + background-color: #dadada; + font-size: 1.625rem; + margin-top: .125rem; + padding: 1.125rem .875rem; + text-align: center +} +.viewport-dash .network li:nth-child(even) { + background-color: #e5e5e5 +} +.viewport-dash .network li:last-child { + border: 0 +} +.viewport-dash .network li .label { + display: block; + margin-bottom: .6rem +} +.viewport-dash .mgmt { + margin: 1.5rem 0 0 1.5rem +} +.viewport-dash .mgmt:before, +.viewport-dash .mgmt:after { + content: " "; + display: block +} +.viewport-dash .mgmt:after { + clear: both +} +.viewport-dash .mgmt ul { + width: 100% +} +.viewport-dash .mgmt ul:before, +.viewport-dash .mgmt ul:after { + content: " "; + display: block +} +.viewport-dash .mgmt ul:after { + clear: both +} +.viewport-dash .mgmt h2 { + padding: 0 +} +.viewport-dash .mgmt li { + background: #dadada; + flex: 1 1 20.5%; + font-size: 1.625rem; + height: 5.938rem; + margin-left: .125rem; + padding: .5rem 0; + text-align: center +} +.viewport-dash .mgmt li:first-child { + background-color: #fff; + flex: 0 1 10%; + align-items: center; + display: flex; + justify-content: center +} +.viewport-dash .mgmt li:nth-child(even) { + background-color: #e5e5e5 +} +.viewport-dash .mgmt li .label { + display: block; + margin-bottom: .875rem +} +.viewport-dash .mgmt li .unit { + display: block +} +.viewport-dash .navpanel .service-orch { + display: flex; + align-items: center; + margin-left: 11.88rem +} +.viewport-dash .navpanel .service-orch .button-half-border { + float: left; + margin-right: 7.75rem +} +.viewport-dash .navpanel .service-orch li { + background: #e5e5e5; + float: left; + height: 5.313rem; + padding: .75rem 1.5rem; + position: relative; + margin-right: 1.5rem; + width: 7.75rem; + align-items: center; + display: flex; + justify-content: center; + font-size: .6875rem; + font-weight: 700; + text-transform: uppercase +} +.viewport-dash .navpanel .service-orch li:after { + border-top: 1px solid #333; + content: ''; + position: absolute; + right: -24px; + top: 50%; + width: 24px +} +.viewport-dash .navpanel .service-orch li:last-child { + margin: 0 +} +.viewport-dash .navpanel .service-orch li:last-child:after { + border: 0 +} +.viewport-dash .navpanel .vnf .button-half-border, +.viewport-dash .navpanel .analytics .button-half-border { + margin: 1rem 0 1.5rem +} +.viewport-dash .navpanel .vnf .button-half-border:before, +.viewport-dash .navpanel .analytics .button-half-border:before, +.viewport-dash .navpanel .vnf .button-half-border:after, +.viewport-dash .navpanel .analytics .button-half-border:after { + background: #dadada +} +.viewport-dash .navpanel .vnf dt, +.viewport-dash .navpanel .analytics dt, +.viewport-dash .navpanel .vnf dd, +.viewport-dash .navpanel .analytics dd { + margin-bottom: .1875rem; + padding: .5rem +} +.viewport-dash .navpanel .vnf dt, +.viewport-dash .navpanel .analytics dt { + background: #fff; + font-size: .6875rem; + font-weight: 700; + text-transform: uppercase +} +.viewport-dash .navpanel .vnf dd, +.viewport-dash .navpanel .analytics dd { + background: #ccc; + font-size: 1.375rem +} +.viewport-dash .navpanel .vnf .unit, +.viewport-dash .navpanel .analytics .unit { + display: block; + margin-top: .5rem +} +.viewport-dash .navpanel .vim { + display: flex; + align-items: center; + margin-left: 11.88rem +} +.viewport-dash .navpanel .vim .button-half-border { + float: left; + margin-right: 2.25rem +} +.viewport-dash .navpanel .vim table { + border-collapse: separate; + border-spacing: .125rem; + float: left; + text-align: center +} +.viewport-dash .navpanel .vim table:last-child { + margin-left: 1.5rem +} +.viewport-dash .navpanel .vim table th { + background: #fff; + padding: .5rem .75rem; + text-align: left; + font-size: .6875rem; + font-weight: 700; + text-transform: uppercase +} +.viewport-dash .navpanel .vim table td { + background: #e5e5e5; + font-size: 1.375rem; + padding: .375rem 1rem .375rem +} +.viewport-dash .navpanel .vim table td .label { + display: block; + margin-bottom: .5rem +} +.viewport-so .vpane-bottom { + display: flex +} +.viewport-so .panel-half { + padding: 10px +} +.viewport-vim { + font-size: .75rem +} +.viewport-vim .mainpanel { + background: #f1f1f1; + padding: 1.5rem +} +.viewport-vim .mainpanel .vim-controls { + margin-bottom: 3rem +} +.viewport-vim .mainpanel .vim-controls>* { + flex: 1 1 auto; + justify-content: flex-end +} +.viewport-vim .mainpanel .vim-controls h2 { + margin-top: .375rem +} +.viewport-vim .switch-table-set { + justify-content: space-around; + margin-bottom: 3rem +} +.viewport-vim .switch-table { + flex: 0 1 30% +} +.viewport-vim .switch-table table { + width: 100% +} +.viewport-vim .switch-table th, +.viewport-vim .switch-table td { + border-bottom: 2px solid #f1f1f1; + vertical-align: middle; + padding: .5625rem .875rem +} +.viewport-vim .switch-table th.switch h3 { + background: url(./img/icon-switch.png) no-repeat left top; + height: 1.7rem; + padding-left: 4rem; + padding-top: .55rem +} +.viewport-vim .switch-table th.host { + background: #ccc url(./img/icon-host.png) no-repeat 21px center; + height: 4rem; + padding: 0 1.25rem 0 3.375rem +} +.viewport-vim .switch-table thead th { + background-color: #fff; + font-size: .875rem; + font-weight: 700 +} +.viewport-vim .switch-table thead th:last-child { + text-align: left +} +.viewport-vim .switch-table thead th small { + display: block; + font-size: .75rem; + font-weight: 400; + margin-top: .375rem +} +.viewport-vim .switch-table tbody th { + background-color: #ccc; + color: #fff; + padding: .5625rem .875rem .875rem 0 +} +.viewport-vim .switch-table tbody td { + background-color: #e5e5e5; + font-size: .625rem +} +.viewport-vim .switch-table tbody td em { + margin-right: .5rem +} +.viewport-vim .switch-table tbody td i { + font-size: .5625rem; + margin-right: .125rem +} +.viewport-vim .switch-table tbody td i.inactive { + color: #ccc +} +.viewport-vim .switch-table tbody tr.selected th { + background-color: #00acee +} +.viewport-vim .switch-table tbody tr.selected td { + background-color: #fff +} +.viewport-vim .switch-table tbody tr.selected td:last-child { + background: #fff url(./img/table-cell-bg.png) repeat-y right top +} +.viewport-vim .vim-legend { + display: inline-flex +} +.viewport-vim .vim-legend div { + background-color: #e5e5e5; + padding: .75rem 1.5rem; + margin-right: .125rem +} +.viewport-vim .vim-legend div:last-of-type { + margin-right: 0 +} +.viewport-vim .vim-legend ul { + float: left +} +.viewport-vim .vim-legend ul:first-child { + margin-right: 2.25rem +} +.viewport-vim .vim-legend ol li:first-child { + align-items: center; + background: url(./img/icon-host-sm.png) no-repeat left center; + display: flex; + height: 2.188rem; + margin-bottom: -.5rem; + padding-left: 1.75rem +} +.viewport-vim .vim-legend li { + margin: .75rem 0 +} +.viewport-vim .vim-legend li em, +.viewport-vim .vim-legend li i { + margin-right: .75rem +} +.viewport-vim .navpanel.top { + background-color: #ccc; + height: 30px; + padding: 0; + align-items: center; + display: flex; + justify-content: center +} +.viewport-vim .navpanel.top:before { + border-left: 32px solid #dadada; + border-top: 30px solid #ccc +} +.viewport-vim .navpanel.top:after { + border-right: 32px solid #dadada; + border-top: 30px solid #ccc +} +.viewport-vim .navpanel.left, +.viewport-vim .navpanel.right { + background-color: #f1f1f1; + padding: 0; + position: relative; + width: 38px +} +.viewport-vim .navpanel.left .tilt, +.viewport-vim .navpanel.right .tilt { + background-color: #dadada; + height: 100%; + position: absolute; + left: 24px; + top: -2px; + transform: rotate(-2deg); + width: 38px +} +.viewport-vim .navpanel.left .tilt { + left: -24px; + transform: rotate(2deg) +} +.viewport-vnf h2 { + padding-bottom: 2rem +} +.viewport-vnf h3 { + background: #fff; + font-size: .875rem; + padding: 1.125rem; + font-weight: 700; + text-transform: uppercase +} +.viewport-vnf h4 { + font-size: .75rem; + margin-bottom: .5625rem; + font-weight: 700; + text-transform: uppercase +} +.viewport-vnf.navpanel-container { + background-color: #f1f1f1 +} +.viewport-vnf .flex-row { + align-items: flex-start; + position: relative; + width: 100% +} +.viewport-vnf .flex-row .link-diagram { + position: absolute; + bottom: -3.813rem; + left: 13.5% +} +.viewport-vnf .flex-row .link-diagram img { + width: 80% +} +.viewport-vnf .mainpanel { + margin: 0 0 5rem 5rem +} +.viewport-vnf .vnf-card { + flex: 0 1 30.5%; + margin-right: 2rem; + background-color: #e5e5e5 +} +.viewport-vnf .vnf-card:first-child { + margin-left: 0 +} +.viewport-vnf .vnf-card:last-child { + margin-right: 0 +} +.viewport-vnf .vnf-card table { + width: 100% +} +.viewport-vnf .vnf-card td { + border-bottom: 2px solid #f1f1f1; + border-right: 2px solid #f1f1f1; + padding: 1.125em 0; + text-align: center; + width: 50% +} +.viewport-vnf .vnf-card td:last-child { + border-right: 0 +} +.viewport-vnf .vnf-card td img { + width: 75% +} +.viewport-vnf .vnf-card td small { + display: block; + margin-bottom: 1.5em +} +.viewport-vnf .vnf-card tr:first-child td { + background-color: #e5e5e5 +} +.viewport-vnf .vnf-card tr:last-child td { + background-color: #dadada +} +.viewport-vnf .vnf-card>ul { + display: flex; + flex-direction: row; + flex-wrap: nowrap; + width: 99.8%; + border-bottom: 2px solid #f1f1f1 +} +.viewport-vnf .vnf-card>ul li { + background-color: #e5e5e5; + flex: 1 1 auto; + margin-right: .125rem; + padding: 1.5em .75em; + text-align: center +} +.viewport-vnf .vnf-card>ul li:first-child { + flex: 0 1 18% +} +.viewport-vnf .vnf-card>ul li:last-child { + margin-right: 0 +} +.viewport-vnf .vnf-card>div { + background-color: #dadada; + box-sizing: border-box; + margin-top: .125rem; + padding: .75rem 1.5rem 1.5rem; + text-align: center; + width: 99.8% +} +.viewport-vnf .vnf-card>div .button-action-toggle { + font-size: .75rem +} +.viewport-vnf .vnf-card.vlb .bindings { + padding: 1.125rem 1.5rem +} +.viewport-vnf .vnf-card.vlb .bindings em { + display: block; + margin: 1.063rem 0 +} +.viewport-vnf .vnf-card.vlb .bindings img { + width: .75rem +} +.viewport-vnf .navpanel.top, +.viewport-vnf .navpanel.bottom { + background-color: #f1f1f1; + height: 38px; + overflow: hidden; + padding: 0; + position: relative +} +.viewport-vnf .navpanel.top .tilt, +.viewport-vnf .navpanel.bottom .tilt { + background-color: #dadada; + height: 38px; + position: absolute; + left: 0; + top: -25px; + transform: rotate(1.4deg); + width: 100% +} +.viewport-vnf .navpanel.top:before, +.viewport-vnf .navpanel.bottom:before { + border: 0 +} +.viewport-vnf .navpanel.top:after { + border-right: 48px solid #ccc; + border-top: 38px solid #dadada +} +.viewport-vnf .navpanel.bottom { + transform: scaleY(-1) +} +.viewport-vnf .navpanel.bottom:after { + border-left: 48px solid #dadada; + border-top: 38px solid #ccc; + transform: scaleY(-1) +} +.viewport-vnf .navpanel.right { + background-color: #ccc; + width: 48px; + align-items: center; + display: flex; + justify-content: center +} +.wag-app>h1 { + height: 63px; + line-height: 51px; + margin-left: 80px; + padding-left: 118px +} +.wag h2 { + padding-bottom: 2rem +} +.wag h3 { + background: #fff; + font-size: .875rem; + padding: 1.125rem; + font-weight: 700; + text-transform: uppercase +} +.wag h4 { + font-size: .75rem; + margin-bottom: .5625rem; + font-weight: 700; + text-transform: uppercase +} +.wag .slider-container { + width: 50%!important; + margin: auto +} +.wag.navpanel-container { + background-color: #f1f1f1 +} +.wag .flex-row { + align-items: flex-start; + position: relative; + width: 100% +} +.wag .mainpanel { + margin: 0 5rem 5rem 5rem +} +.wag .wag-outer-container { + display: flex +} +.wag .wag-inner-container { + flex-direction: column; + flex: 3 1 auto +} +.wag .wag-inner-inner-container { + display: flex; + flex: 1 1 auto; + margin-bottom: 40px +} +.wag .wag-inner-inner-container .slider-vert-header { + margin: 105px 0 0 -16px +} +.wag .wag-top-row tbody { + height: 614px +} +.wag .wag-top-row-half tbody { + height: 307px +} +.wag .wag-analytics tbody { + display: flex; + height: 360px +} +.wag .wag-analytics tr, +.wag .wag-top-row tr, +.wag .wag-top-row-half tr { + flex: 1 1 auto +} +.wag .wag-analytics tr:last-child { + flex: 20 1 auto +} +.wag .vnf-card { + flex: 1 1 auto; + margin-right: 2rem +} +.wag .vnf-card:first-child { + margin-left: 0; + flex: 1 1 auto +} +.wag .vnf-card:last-child { + margin-right: 0 +} +.wag .vnf-card table { + width: 100% +} +.wag .vnf-card td { + border-bottom: 2px solid #f1f1f1; + border-right: 2px solid #f1f1f1; + padding: 1.125em 0; + text-align: center; + width: 50% +} +.wag .vnf-card td:last-child { + border-right: 0 +} +.wag .vnf-card td img { + width: 75% +} +.wag .vnf-card td small { + display: block; + margin-bottom: 1.5em +} +.wag .vnf-card tr:nth-child(even) td { + background-color: #e5e5e5 +} +.wag .vnf-card tr:nth-child(odd) td { + background-color: #dadada +} +.wag .vnf-card tr.lighter td { + background-color: #e5e5e5 +} +.wag .vnf-card>ul { + display: flex; + flex-direction: row; + flex-wrap: nowrap; + width: 99.8% +} +.wag .vnf-card>ul li { + background-color: #e5e5e5; + flex: 1 1 auto; + margin-right: .125rem; + padding: 1.5em .75em; + text-align: center +} +.wag .vnf-card>ul li:first-child { + flex: 0 1 18% +} +.wag .vnf-card>ul li:last-child { + margin-right: 0 +} +.wag .vnf-card>div { + background-color: #dadada; + box-sizing: border-box; + margin-top: .125rem; + padding: .75rem 1.5rem 1.5rem; + text-align: center; + width: 99.8% +} +.wag .vnf-card>div .button-action-toggle { + font-size: .75rem +} +.wag .vnf-card.vlb .bindings { + padding: 1.125rem 1.5rem +} +.wag .vnf-card.vlb .bindings em { + display: block; + margin: 1.063rem 0 +} +.wag .vnf-card.vlb .bindings img { + width: .75rem +} +.wag .navpanel.top, +.wag .navpanel.bottom { + background-color: #f1f1f1; + height: 38px; + overflow: hidden; + padding: 0; + position: relative +} +.wag .navpanel.top .tilt, +.wag .navpanel.bottom .tilt { + background-color: #dadada; + height: 38px; + position: absolute; + left: 0; + top: -25px; + transform: rotate(1.4deg); + width: 100% +} +.wag .navpanel.top:before, +.wag .navpanel.bottom:before { + border: 0 +} +.wag .navpanel.top:after { + border-right: 48px solid #ccc; + border-top: 38px solid #dadada +} +.wag .navpanel.bottom { + transform: scaleY(-1) +} +.wag .navpanel.bottom:after { + border-left: 48px solid #dadada; + border-top: 38px solid #ccc; + transform: scaleY(-1) +} +.wag .navpanel.right { + background-color: #ccc; + width: 48px; + align-items: center; + display: flex; + justify-content: center +} +.wag #map { + height: 295px +} +.wag .wag-marker { + width: 20px; + height: 20px; + border: 1px solid #088; + border-radius: 10px; + background-color: #0FF; + opacity: .5 +} +.viewport-dash { + position: relative +} +.viewport-dash .vim-link { + display: block; + height: 133px; + position: absolute; + bottom: 0; + right: 0; + width: 100% +} +.viewport-dash .vnf-link { + display: block; + height: 100%; + position: absolute; + left: 0; + top: 0; + width: 133px +} +.viewport-dash .so-link { + display: block; + height: 133px; + position: absolute; + top: 0; + right: 0; + width: 100% +} +.viewport-dash .platform-link { + margin-bottom: 20px; + flex: 1 100% +} +.viewport-dash .platform-link .button-half-border:before, +.viewport-dash .platform-link .button-half-border:after { + background: #f1f1f1; + content: ''; + display: block; + height: 10px; + position: absolute; + top: 0; + width: 4px +} +.viewport-dash .platform-link a:link { + color: #000; + text-decoration: none +} +.viewport-dash .platform-link a:visited { + color: #000; + text-decoration: none +} +.viewport-dash .platform-link a:hover { + color: #000; + text-decoration: none +} +.viewport-dash .platform-link a:active { + color: #000; + text-decoration: none +} +.viewport-vim { + position: relative +} +.viewport-vim .dash-link { + height: 40px; + position: absolute; + left: 0; + top: 0; + width: 100% +} +.viewport-vnf { + position: relative +} +.viewport-vnf .dash-link { + height: 100%; + position: absolute; + right: 0; + top: 0; + width: 40px +} + +.login-cntnr { + width: 100%; + height: 100%; + text-align: center; + font-family: roboto-thin, Helvetica, Arial, sans-serif; + font-size: 20px +} +.login-cntnr .logo { + width: 154px; + height: 102px; + margin-left: auto; + margin-right: auto; + margin-top: 150px; + margin-bottom: 20px; + background-size: 154px 102px; + background-image: url(./img/header-logo.png) +} +.login-cntnr .riftio { + margin-bottom: 40px; + font-size: 1.625rem; + font-weight: 400; +} +.login-cntnr input { + width: 525px; + padding-left: 25px +} +.login-cntnr input, +.login-cntnr .sign-in { + min-width: 0; + width: 550px; + height: 65px; + min-width: auto; + margin-bottom: 40px; + box-shadow: inset 0 1px 2px rgba(0, 0, 0, .39), 0 -1px 1px #fff, 0 1px 0 #fff; + font-size: 20px +} +.login-cntnr .sign-in { + display: inline-block; + // height: 45px; + // padding-top: 20px; + -webkit-box-shadow: 4px 4px 1px 0 #d9d9d9; + -moz-box-shadow: 4px 4px 1px 0 #d9d9d9; + box-shadow: 4px 4px 1px 0 #d9d9d9; + background-color: #333; + color: #fff; + text-decoration: none +} +.login-cntnr .create-account { + font-size: 18px; + color: #393939 +} +.login-cntnr .create-account a { + font-weight: 700 +} + diff --git a/skyquake/framework/style/fonts/Roboto-Light-webfont.ttf b/skyquake/framework/style/fonts/Roboto-Light-webfont.ttf new file mode 100755 index 000000000..664e1b2f9 Binary files /dev/null and b/skyquake/framework/style/fonts/Roboto-Light-webfont.ttf differ diff --git a/skyquake/framework/style/fonts/Roboto-Regular-webfont.eot b/skyquake/framework/style/fonts/Roboto-Regular-webfont.eot new file mode 100755 index 000000000..9b5e8e413 Binary files /dev/null and b/skyquake/framework/style/fonts/Roboto-Regular-webfont.eot differ diff --git a/skyquake/framework/style/fonts/Roboto-Regular-webfont.svg b/skyquake/framework/style/fonts/Roboto-Regular-webfont.svg new file mode 100755 index 000000000..de7d77fea --- /dev/null +++ b/skyquake/framework/style/fonts/Roboto-Regular-webfont.svg @@ -0,0 +1,621 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/skyquake/framework/style/fonts/Roboto-Regular-webfont.ttf b/skyquake/framework/style/fonts/Roboto-Regular-webfont.ttf new file mode 100755 index 000000000..44dd78d5e Binary files /dev/null and b/skyquake/framework/style/fonts/Roboto-Regular-webfont.ttf differ diff --git a/skyquake/framework/style/fonts/Roboto-Regular-webfont.woff b/skyquake/framework/style/fonts/Roboto-Regular-webfont.woff new file mode 100755 index 000000000..bfa05d53f Binary files /dev/null and b/skyquake/framework/style/fonts/Roboto-Regular-webfont.woff differ diff --git a/skyquake/framework/style/fonts/RobotoCondensed-Bold-webfont.eot b/skyquake/framework/style/fonts/RobotoCondensed-Bold-webfont.eot new file mode 100755 index 000000000..bbc67d825 Binary files /dev/null and b/skyquake/framework/style/fonts/RobotoCondensed-Bold-webfont.eot differ diff --git a/skyquake/framework/style/fonts/RobotoCondensed-Bold-webfont.svg b/skyquake/framework/style/fonts/RobotoCondensed-Bold-webfont.svg new file mode 100755 index 000000000..417a2a9e3 --- /dev/null +++ b/skyquake/framework/style/fonts/RobotoCondensed-Bold-webfont.svg @@ -0,0 +1,643 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/skyquake/framework/style/fonts/RobotoCondensed-Bold-webfont.ttf b/skyquake/framework/style/fonts/RobotoCondensed-Bold-webfont.ttf new file mode 100755 index 000000000..87256d3a3 Binary files /dev/null and b/skyquake/framework/style/fonts/RobotoCondensed-Bold-webfont.ttf differ diff --git a/skyquake/framework/style/fonts/RobotoCondensed-Bold-webfont.woff b/skyquake/framework/style/fonts/RobotoCondensed-Bold-webfont.woff new file mode 100755 index 000000000..235c963d6 Binary files /dev/null and b/skyquake/framework/style/fonts/RobotoCondensed-Bold-webfont.woff differ diff --git a/skyquake/framework/style/fonts/SourceCodePro-Black.otf.woff b/skyquake/framework/style/fonts/SourceCodePro-Black.otf.woff new file mode 100755 index 000000000..d08a98446 Binary files /dev/null and b/skyquake/framework/style/fonts/SourceCodePro-Black.otf.woff differ diff --git a/skyquake/framework/style/fonts/SourceCodePro-BlackIt.otf.woff b/skyquake/framework/style/fonts/SourceCodePro-BlackIt.otf.woff new file mode 100755 index 000000000..1061185d5 Binary files /dev/null and b/skyquake/framework/style/fonts/SourceCodePro-BlackIt.otf.woff differ diff --git a/skyquake/framework/style/fonts/SourceCodePro-Bold.otf.woff b/skyquake/framework/style/fonts/SourceCodePro-Bold.otf.woff new file mode 100755 index 000000000..cf960992f Binary files /dev/null and b/skyquake/framework/style/fonts/SourceCodePro-Bold.otf.woff differ diff --git a/skyquake/framework/style/fonts/SourceCodePro-BoldIt.otf.woff b/skyquake/framework/style/fonts/SourceCodePro-BoldIt.otf.woff new file mode 100755 index 000000000..e8702f9d1 Binary files /dev/null and b/skyquake/framework/style/fonts/SourceCodePro-BoldIt.otf.woff differ diff --git a/skyquake/framework/style/fonts/SourceCodePro-ExtraLight.otf.woff b/skyquake/framework/style/fonts/SourceCodePro-ExtraLight.otf.woff new file mode 100755 index 000000000..54e6b36ec Binary files /dev/null and b/skyquake/framework/style/fonts/SourceCodePro-ExtraLight.otf.woff differ diff --git a/skyquake/framework/style/fonts/SourceCodePro-ExtraLightIt.otf.woff b/skyquake/framework/style/fonts/SourceCodePro-ExtraLightIt.otf.woff new file mode 100755 index 000000000..c57443cbe Binary files /dev/null and b/skyquake/framework/style/fonts/SourceCodePro-ExtraLightIt.otf.woff differ diff --git a/skyquake/framework/style/fonts/SourceCodePro-It.otf.woff b/skyquake/framework/style/fonts/SourceCodePro-It.otf.woff new file mode 100755 index 000000000..cc67c99e6 Binary files /dev/null and b/skyquake/framework/style/fonts/SourceCodePro-It.otf.woff differ diff --git a/skyquake/framework/style/fonts/SourceCodePro-Light.otf.woff b/skyquake/framework/style/fonts/SourceCodePro-Light.otf.woff new file mode 100755 index 000000000..c62373f78 Binary files /dev/null and b/skyquake/framework/style/fonts/SourceCodePro-Light.otf.woff differ diff --git a/skyquake/framework/style/fonts/SourceCodePro-LightIt.otf.woff b/skyquake/framework/style/fonts/SourceCodePro-LightIt.otf.woff new file mode 100755 index 000000000..c4cd6464f Binary files /dev/null and b/skyquake/framework/style/fonts/SourceCodePro-LightIt.otf.woff differ diff --git a/skyquake/framework/style/fonts/SourceCodePro-Medium.otf.woff b/skyquake/framework/style/fonts/SourceCodePro-Medium.otf.woff new file mode 100755 index 000000000..826a854ab Binary files /dev/null and b/skyquake/framework/style/fonts/SourceCodePro-Medium.otf.woff differ diff --git a/skyquake/framework/style/fonts/SourceCodePro-MediumIt.otf.woff b/skyquake/framework/style/fonts/SourceCodePro-MediumIt.otf.woff new file mode 100755 index 000000000..5c538dee5 Binary files /dev/null and b/skyquake/framework/style/fonts/SourceCodePro-MediumIt.otf.woff differ diff --git a/skyquake/framework/style/fonts/SourceCodePro-Regular.otf.woff b/skyquake/framework/style/fonts/SourceCodePro-Regular.otf.woff new file mode 100755 index 000000000..395436eb8 Binary files /dev/null and b/skyquake/framework/style/fonts/SourceCodePro-Regular.otf.woff differ diff --git a/skyquake/framework/style/fonts/SourceCodePro-Semibold.otf.woff b/skyquake/framework/style/fonts/SourceCodePro-Semibold.otf.woff new file mode 100755 index 000000000..83ec113f2 Binary files /dev/null and b/skyquake/framework/style/fonts/SourceCodePro-Semibold.otf.woff differ diff --git a/skyquake/framework/style/fonts/SourceCodePro-SemiboldIt.otf.woff b/skyquake/framework/style/fonts/SourceCodePro-SemiboldIt.otf.woff new file mode 100755 index 000000000..be0bd900e Binary files /dev/null and b/skyquake/framework/style/fonts/SourceCodePro-SemiboldIt.otf.woff differ diff --git a/skyquake/framework/style/fonts/SourceSansPro-Black.otf.woff b/skyquake/framework/style/fonts/SourceSansPro-Black.otf.woff new file mode 100755 index 000000000..f1a663a7e Binary files /dev/null and b/skyquake/framework/style/fonts/SourceSansPro-Black.otf.woff differ diff --git a/skyquake/framework/style/fonts/SourceSansPro-BlackIt.otf.woff b/skyquake/framework/style/fonts/SourceSansPro-BlackIt.otf.woff new file mode 100755 index 000000000..1d7dfbdb2 Binary files /dev/null and b/skyquake/framework/style/fonts/SourceSansPro-BlackIt.otf.woff differ diff --git a/skyquake/framework/style/fonts/SourceSansPro-Bold.otf.woff b/skyquake/framework/style/fonts/SourceSansPro-Bold.otf.woff new file mode 100755 index 000000000..6700893c2 Binary files /dev/null and b/skyquake/framework/style/fonts/SourceSansPro-Bold.otf.woff differ diff --git a/skyquake/framework/style/fonts/SourceSansPro-BoldIt.otf.woff b/skyquake/framework/style/fonts/SourceSansPro-BoldIt.otf.woff new file mode 100755 index 000000000..d5e4a0ff9 Binary files /dev/null and b/skyquake/framework/style/fonts/SourceSansPro-BoldIt.otf.woff differ diff --git a/skyquake/framework/style/fonts/SourceSansPro-ExtraLight.otf.woff b/skyquake/framework/style/fonts/SourceSansPro-ExtraLight.otf.woff new file mode 100755 index 000000000..559b74081 Binary files /dev/null and b/skyquake/framework/style/fonts/SourceSansPro-ExtraLight.otf.woff differ diff --git a/skyquake/framework/style/fonts/SourceSansPro-ExtraLightIt.otf.woff b/skyquake/framework/style/fonts/SourceSansPro-ExtraLightIt.otf.woff new file mode 100755 index 000000000..e8fbeb8fe Binary files /dev/null and b/skyquake/framework/style/fonts/SourceSansPro-ExtraLightIt.otf.woff differ diff --git a/skyquake/framework/style/fonts/SourceSansPro-It.otf.woff b/skyquake/framework/style/fonts/SourceSansPro-It.otf.woff new file mode 100755 index 000000000..4b8af4154 Binary files /dev/null and b/skyquake/framework/style/fonts/SourceSansPro-It.otf.woff differ diff --git a/skyquake/framework/style/fonts/SourceSansPro-Light.otf.woff b/skyquake/framework/style/fonts/SourceSansPro-Light.otf.woff new file mode 100755 index 000000000..10490ec57 Binary files /dev/null and b/skyquake/framework/style/fonts/SourceSansPro-Light.otf.woff differ diff --git a/skyquake/framework/style/fonts/SourceSansPro-LightIt.otf.woff b/skyquake/framework/style/fonts/SourceSansPro-LightIt.otf.woff new file mode 100755 index 000000000..13532d7d3 Binary files /dev/null and b/skyquake/framework/style/fonts/SourceSansPro-LightIt.otf.woff differ diff --git a/skyquake/framework/style/fonts/SourceSansPro-Regular.otf.woff b/skyquake/framework/style/fonts/SourceSansPro-Regular.otf.woff new file mode 100755 index 000000000..04739e7fb Binary files /dev/null and b/skyquake/framework/style/fonts/SourceSansPro-Regular.otf.woff differ diff --git a/skyquake/framework/style/fonts/SourceSansPro-Semibold.otf.woff b/skyquake/framework/style/fonts/SourceSansPro-Semibold.otf.woff new file mode 100755 index 000000000..17d744d1b Binary files /dev/null and b/skyquake/framework/style/fonts/SourceSansPro-Semibold.otf.woff differ diff --git a/skyquake/framework/style/fonts/SourceSansPro-SemiboldIt.otf.woff b/skyquake/framework/style/fonts/SourceSansPro-SemiboldIt.otf.woff new file mode 100755 index 000000000..a5b5e1e1d Binary files /dev/null and b/skyquake/framework/style/fonts/SourceSansPro-SemiboldIt.otf.woff differ diff --git a/skyquake/framework/style/icons/svg-sprite-action-symbol.svg b/skyquake/framework/style/icons/svg-sprite-action-symbol.svg new file mode 100755 index 000000000..e2497d9ee --- /dev/null +++ b/skyquake/framework/style/icons/svg-sprite-action-symbol.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/skyquake/framework/style/icons/svg-sprite-alert-symbol.svg b/skyquake/framework/style/icons/svg-sprite-alert-symbol.svg new file mode 100755 index 000000000..5c350e8bd --- /dev/null +++ b/skyquake/framework/style/icons/svg-sprite-alert-symbol.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/skyquake/framework/style/icons/svg-sprite-av-symbol.svg b/skyquake/framework/style/icons/svg-sprite-av-symbol.svg new file mode 100755 index 000000000..84aaa88c4 --- /dev/null +++ b/skyquake/framework/style/icons/svg-sprite-av-symbol.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/skyquake/framework/style/icons/svg-sprite-communication-symbol.svg b/skyquake/framework/style/icons/svg-sprite-communication-symbol.svg new file mode 100755 index 000000000..7b0a620e0 --- /dev/null +++ b/skyquake/framework/style/icons/svg-sprite-communication-symbol.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/skyquake/framework/style/icons/svg-sprite-content-symbol.svg b/skyquake/framework/style/icons/svg-sprite-content-symbol.svg new file mode 100755 index 000000000..65dd1a95e --- /dev/null +++ b/skyquake/framework/style/icons/svg-sprite-content-symbol.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/skyquake/framework/style/icons/svg-sprite-device.svg b/skyquake/framework/style/icons/svg-sprite-device.svg new file mode 100755 index 000000000..d86f1fc63 --- /dev/null +++ b/skyquake/framework/style/icons/svg-sprite-device.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/skyquake/framework/style/icons/svg-sprite-editor-symbol.svg b/skyquake/framework/style/icons/svg-sprite-editor-symbol.svg new file mode 100755 index 000000000..5a0a57583 --- /dev/null +++ b/skyquake/framework/style/icons/svg-sprite-editor-symbol.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/skyquake/framework/style/icons/svg-sprite-file-symbol.svg b/skyquake/framework/style/icons/svg-sprite-file-symbol.svg new file mode 100755 index 000000000..01d7d4d27 --- /dev/null +++ b/skyquake/framework/style/icons/svg-sprite-file-symbol.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/skyquake/framework/style/icons/svg-sprite-hardware-symbol.svg b/skyquake/framework/style/icons/svg-sprite-hardware-symbol.svg new file mode 100755 index 000000000..7f5d14ce1 --- /dev/null +++ b/skyquake/framework/style/icons/svg-sprite-hardware-symbol.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/skyquake/framework/style/icons/svg-sprite-image-symbol.svg b/skyquake/framework/style/icons/svg-sprite-image-symbol.svg new file mode 100755 index 000000000..54313587d --- /dev/null +++ b/skyquake/framework/style/icons/svg-sprite-image-symbol.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/skyquake/framework/style/icons/svg-sprite-maps-symbol.svg b/skyquake/framework/style/icons/svg-sprite-maps-symbol.svg new file mode 100755 index 000000000..31a70d126 --- /dev/null +++ b/skyquake/framework/style/icons/svg-sprite-maps-symbol.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/skyquake/framework/style/icons/svg-sprite-navigation-symbol.svg b/skyquake/framework/style/icons/svg-sprite-navigation-symbol.svg new file mode 100755 index 000000000..780e5fbc8 --- /dev/null +++ b/skyquake/framework/style/icons/svg-sprite-navigation-symbol.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/skyquake/framework/style/icons/svg-sprite-notification-symbol.svg b/skyquake/framework/style/icons/svg-sprite-notification-symbol.svg new file mode 100755 index 000000000..97f3ae5b1 --- /dev/null +++ b/skyquake/framework/style/icons/svg-sprite-notification-symbol.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/skyquake/framework/style/icons/svg-sprite-places-symbol.svg b/skyquake/framework/style/icons/svg-sprite-places-symbol.svg new file mode 100755 index 000000000..8a242c048 --- /dev/null +++ b/skyquake/framework/style/icons/svg-sprite-places-symbol.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/skyquake/framework/style/icons/svg-sprite-social-symbol.svg b/skyquake/framework/style/icons/svg-sprite-social-symbol.svg new file mode 100755 index 000000000..d9f371db0 --- /dev/null +++ b/skyquake/framework/style/icons/svg-sprite-social-symbol.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/skyquake/framework/style/icons/svg-sprite-toggle-symbol.svg b/skyquake/framework/style/icons/svg-sprite-toggle-symbol.svg new file mode 100755 index 000000000..5d4705b86 --- /dev/null +++ b/skyquake/framework/style/icons/svg-sprite-toggle-symbol.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/skyquake/framework/style/img/avatar.png b/skyquake/framework/style/img/avatar.png new file mode 100644 index 000000000..c40cb8c98 Binary files /dev/null and b/skyquake/framework/style/img/avatar.png differ diff --git a/skyquake/framework/style/img/bargraph.png b/skyquake/framework/style/img/bargraph.png new file mode 100644 index 000000000..2b25d7ff0 Binary files /dev/null and b/skyquake/framework/style/img/bargraph.png differ diff --git a/skyquake/framework/style/img/bearer-plane-diagram.png b/skyquake/framework/style/img/bearer-plane-diagram.png new file mode 100644 index 000000000..9eb06a4c5 Binary files /dev/null and b/skyquake/framework/style/img/bearer-plane-diagram.png differ diff --git a/skyquake/framework/style/img/bg-tile-cross-small.png b/skyquake/framework/style/img/bg-tile-cross-small.png new file mode 100644 index 000000000..0ab6c949e Binary files /dev/null and b/skyquake/framework/style/img/bg-tile-cross-small.png differ diff --git a/skyquake/framework/style/img/catalog-default.svg b/skyquake/framework/style/img/catalog-default.svg new file mode 100644 index 000000000..ef6efc792 --- /dev/null +++ b/skyquake/framework/style/img/catalog-default.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/skyquake/framework/style/img/catalog-nsd-default.svg b/skyquake/framework/style/img/catalog-nsd-default.svg new file mode 100644 index 000000000..e2a4a8972 --- /dev/null +++ b/skyquake/framework/style/img/catalog-nsd-default.svg @@ -0,0 +1 @@ + diff --git a/skyquake/framework/style/img/catalog-vnfd-default.svg b/skyquake/framework/style/img/catalog-vnfd-default.svg new file mode 100644 index 000000000..4dafa7869 --- /dev/null +++ b/skyquake/framework/style/img/catalog-vnfd-default.svg @@ -0,0 +1 @@ + diff --git a/skyquake/framework/style/img/create-account.png b/skyquake/framework/style/img/create-account.png new file mode 100644 index 000000000..16aeeb44c Binary files /dev/null and b/skyquake/framework/style/img/create-account.png differ diff --git a/skyquake/framework/style/img/create-fleet-params-temp.png b/skyquake/framework/style/img/create-fleet-params-temp.png new file mode 100644 index 000000000..d5b74981a Binary files /dev/null and b/skyquake/framework/style/img/create-fleet-params-temp.png differ diff --git a/skyquake/framework/style/img/create-fleet-pool-temp.png b/skyquake/framework/style/img/create-fleet-pool-temp.png new file mode 100644 index 000000000..2a330ec67 Binary files /dev/null and b/skyquake/framework/style/img/create-fleet-pool-temp.png differ diff --git a/skyquake/framework/style/img/create-fleet-services-temp.png b/skyquake/framework/style/img/create-fleet-services-temp.png new file mode 100644 index 000000000..2b4578b46 Binary files /dev/null and b/skyquake/framework/style/img/create-fleet-services-temp.png differ diff --git a/skyquake/framework/style/img/diameter-openflow-lte-icon.png b/skyquake/framework/style/img/diameter-openflow-lte-icon.png new file mode 100644 index 000000000..39156d15e Binary files /dev/null and b/skyquake/framework/style/img/diameter-openflow-lte-icon.png differ diff --git a/skyquake/framework/style/img/firewall-icon.png b/skyquake/framework/style/img/firewall-icon.png new file mode 100644 index 000000000..c65caf492 Binary files /dev/null and b/skyquake/framework/style/img/firewall-icon.png differ diff --git a/skyquake/framework/style/img/gbps-10.png b/skyquake/framework/style/img/gbps-10.png new file mode 100644 index 000000000..1327ab91d Binary files /dev/null and b/skyquake/framework/style/img/gbps-10.png differ diff --git a/skyquake/framework/style/img/gbps-50.png b/skyquake/framework/style/img/gbps-50.png new file mode 100644 index 000000000..9cfb805d6 Binary files /dev/null and b/skyquake/framework/style/img/gbps-50.png differ diff --git a/skyquake/framework/style/img/green-page-icon.png b/skyquake/framework/style/img/green-page-icon.png new file mode 100644 index 000000000..87bc437c1 Binary files /dev/null and b/skyquake/framework/style/img/green-page-icon.png differ diff --git a/skyquake/framework/style/img/header-logo.png b/skyquake/framework/style/img/header-logo.png new file mode 100644 index 000000000..09b47c7b2 Binary files /dev/null and b/skyquake/framework/style/img/header-logo.png differ diff --git a/skyquake/framework/style/img/host-icon.png b/skyquake/framework/style/img/host-icon.png new file mode 100644 index 000000000..0c508a17e Binary files /dev/null and b/skyquake/framework/style/img/host-icon.png differ diff --git a/skyquake/framework/style/img/icon-host-sm.png b/skyquake/framework/style/img/icon-host-sm.png new file mode 100644 index 000000000..1254c220c Binary files /dev/null and b/skyquake/framework/style/img/icon-host-sm.png differ diff --git a/skyquake/framework/style/img/icon-host.png b/skyquake/framework/style/img/icon-host.png new file mode 100644 index 000000000..0c508a17e Binary files /dev/null and b/skyquake/framework/style/img/icon-host.png differ diff --git a/skyquake/framework/style/img/icon-open-viewport.png b/skyquake/framework/style/img/icon-open-viewport.png new file mode 100644 index 000000000..4ed206e90 Binary files /dev/null and b/skyquake/framework/style/img/icon-open-viewport.png differ diff --git a/skyquake/framework/style/img/icon-switch.png b/skyquake/framework/style/img/icon-switch.png new file mode 100644 index 000000000..88a77152f Binary files /dev/null and b/skyquake/framework/style/img/icon-switch.png differ diff --git a/skyquake/framework/style/img/iot-industry-icon.png b/skyquake/framework/style/img/iot-industry-icon.png new file mode 100644 index 000000000..1e10ac015 Binary files /dev/null and b/skyquake/framework/style/img/iot-industry-icon.png differ diff --git a/skyquake/framework/style/img/iot-medical-icon.png b/skyquake/framework/style/img/iot-medical-icon.png new file mode 100644 index 000000000..352f787b3 Binary files /dev/null and b/skyquake/framework/style/img/iot-medical-icon.png differ diff --git a/skyquake/framework/style/img/iot-transportation-icon-active.png b/skyquake/framework/style/img/iot-transportation-icon-active.png new file mode 100644 index 000000000..6eb2c19ac Binary files /dev/null and b/skyquake/framework/style/img/iot-transportation-icon-active.png differ diff --git a/skyquake/framework/style/img/ip-lte-icon.png b/skyquake/framework/style/img/ip-lte-icon.png new file mode 100644 index 000000000..b90ca152e Binary files /dev/null and b/skyquake/framework/style/img/ip-lte-icon.png differ diff --git a/skyquake/framework/style/img/ip-softgre-icon.png b/skyquake/framework/style/img/ip-softgre-icon.png new file mode 100644 index 000000000..0a7f5f947 Binary files /dev/null and b/skyquake/framework/style/img/ip-softgre-icon.png differ diff --git a/skyquake/framework/style/img/latency.png b/skyquake/framework/style/img/latency.png new file mode 100644 index 000000000..fd3371ee9 Binary files /dev/null and b/skyquake/framework/style/img/latency.png differ diff --git a/skyquake/framework/style/img/latency_graph.png b/skyquake/framework/style/img/latency_graph.png new file mode 100644 index 000000000..03e6de76d Binary files /dev/null and b/skyquake/framework/style/img/latency_graph.png differ diff --git a/skyquake/framework/style/img/launchpad-add-fleet-icon.png b/skyquake/framework/style/img/launchpad-add-fleet-icon.png new file mode 100644 index 000000000..ab89e5258 Binary files /dev/null and b/skyquake/framework/style/img/launchpad-add-fleet-icon.png differ diff --git a/skyquake/framework/style/img/launchpad-graph-temp.png b/skyquake/framework/style/img/launchpad-graph-temp.png new file mode 100644 index 000000000..04d91a309 Binary files /dev/null and b/skyquake/framework/style/img/launchpad-graph-temp.png differ diff --git a/skyquake/framework/style/img/launchpad-graphs-temp/10.09.png b/skyquake/framework/style/img/launchpad-graphs-temp/10.09.png new file mode 100644 index 000000000..6e7e370f1 Binary files /dev/null and b/skyquake/framework/style/img/launchpad-graphs-temp/10.09.png differ diff --git a/skyquake/framework/style/img/launchpad-graphs-temp/17.23.png b/skyquake/framework/style/img/launchpad-graphs-temp/17.23.png new file mode 100644 index 000000000..1bbe9fe98 Binary files /dev/null and b/skyquake/framework/style/img/launchpad-graphs-temp/17.23.png differ diff --git a/skyquake/framework/style/img/launchpad-graphs-temp/20.05.png b/skyquake/framework/style/img/launchpad-graphs-temp/20.05.png new file mode 100644 index 000000000..2d78371f8 Binary files /dev/null and b/skyquake/framework/style/img/launchpad-graphs-temp/20.05.png differ diff --git a/skyquake/framework/style/img/launchpad-graphs-temp/23.08.png b/skyquake/framework/style/img/launchpad-graphs-temp/23.08.png new file mode 100644 index 000000000..ee05baf29 Binary files /dev/null and b/skyquake/framework/style/img/launchpad-graphs-temp/23.08.png differ diff --git a/skyquake/framework/style/img/launchpad-graphs-temp/25.03.png b/skyquake/framework/style/img/launchpad-graphs-temp/25.03.png new file mode 100644 index 000000000..87931d8a7 Binary files /dev/null and b/skyquake/framework/style/img/launchpad-graphs-temp/25.03.png differ diff --git a/skyquake/framework/style/img/launchpad-graphs-temp/30.56.png b/skyquake/framework/style/img/launchpad-graphs-temp/30.56.png new file mode 100644 index 000000000..8c8be8844 Binary files /dev/null and b/skyquake/framework/style/img/launchpad-graphs-temp/30.56.png differ diff --git a/skyquake/framework/style/img/launchpad-graphs-temp/7.08.png b/skyquake/framework/style/img/launchpad-graphs-temp/7.08.png new file mode 100644 index 000000000..8cc9c1ab8 Binary files /dev/null and b/skyquake/framework/style/img/launchpad-graphs-temp/7.08.png differ diff --git a/skyquake/framework/style/img/launchpad-graphs-temp/9.24.png b/skyquake/framework/style/img/launchpad-graphs-temp/9.24.png new file mode 100644 index 000000000..e72430625 Binary files /dev/null and b/skyquake/framework/style/img/launchpad-graphs-temp/9.24.png differ diff --git a/skyquake/framework/style/img/link.png b/skyquake/framework/style/img/link.png new file mode 100644 index 000000000..33210cc63 Binary files /dev/null and b/skyquake/framework/style/img/link.png differ diff --git a/skyquake/framework/style/img/loadbalance-icon.png b/skyquake/framework/style/img/loadbalance-icon.png new file mode 100644 index 000000000..4164786de Binary files /dev/null and b/skyquake/framework/style/img/loadbalance-icon.png differ diff --git a/skyquake/framework/style/img/lte-mme-icon.png b/skyquake/framework/style/img/lte-mme-icon.png new file mode 100644 index 000000000..6eb898b8a Binary files /dev/null and b/skyquake/framework/style/img/lte-mme-icon.png differ diff --git a/skyquake/framework/style/img/osm_header.png b/skyquake/framework/style/img/osm_header.png new file mode 100644 index 000000000..c67dca3fa Binary files /dev/null and b/skyquake/framework/style/img/osm_header.png differ diff --git a/skyquake/framework/style/img/osm_header_253x50.png b/skyquake/framework/style/img/osm_header_253x50.png new file mode 100644 index 000000000..3055122bf Binary files /dev/null and b/skyquake/framework/style/img/osm_header_253x50.png differ diff --git a/skyquake/framework/style/img/osm_header_506x100.png b/skyquake/framework/style/img/osm_header_506x100.png new file mode 100644 index 000000000..4b7a34477 Binary files /dev/null and b/skyquake/framework/style/img/osm_header_506x100.png differ diff --git a/skyquake/framework/style/img/page_loader.gif b/skyquake/framework/style/img/page_loader.gif new file mode 100644 index 000000000..07b9f3ffc Binary files /dev/null and b/skyquake/framework/style/img/page_loader.gif differ diff --git a/skyquake/framework/style/img/platform-nav-temp.png b/skyquake/framework/style/img/platform-nav-temp.png new file mode 100644 index 000000000..06f351079 Binary files /dev/null and b/skyquake/framework/style/img/platform-nav-temp.png differ diff --git a/skyquake/framework/style/img/platform-pages-temp/1a-fleet-platform-resources.png b/skyquake/framework/style/img/platform-pages-temp/1a-fleet-platform-resources.png new file mode 100644 index 000000000..f7f28669c Binary files /dev/null and b/skyquake/framework/style/img/platform-pages-temp/1a-fleet-platform-resources.png differ diff --git a/skyquake/framework/style/img/platform-pages-temp/1a-fleet-platform-traffic.png b/skyquake/framework/style/img/platform-pages-temp/1a-fleet-platform-traffic.png new file mode 100644 index 000000000..041325dd5 Binary files /dev/null and b/skyquake/framework/style/img/platform-pages-temp/1a-fleet-platform-traffic.png differ diff --git a/skyquake/framework/style/img/platform-pages-temp/1b-fleet-platform-resources.png b/skyquake/framework/style/img/platform-pages-temp/1b-fleet-platform-resources.png new file mode 100644 index 000000000..f7f28669c Binary files /dev/null and b/skyquake/framework/style/img/platform-pages-temp/1b-fleet-platform-resources.png differ diff --git a/skyquake/framework/style/img/platform-pages-temp/1b-fleet-platform-traffic.png b/skyquake/framework/style/img/platform-pages-temp/1b-fleet-platform-traffic.png new file mode 100644 index 000000000..041325dd5 Binary files /dev/null and b/skyquake/framework/style/img/platform-pages-temp/1b-fleet-platform-traffic.png differ diff --git a/skyquake/framework/style/img/platform-pages-temp/2a-fleet-platform-resources.png b/skyquake/framework/style/img/platform-pages-temp/2a-fleet-platform-resources.png new file mode 100644 index 000000000..f7f28669c Binary files /dev/null and b/skyquake/framework/style/img/platform-pages-temp/2a-fleet-platform-resources.png differ diff --git a/skyquake/framework/style/img/platform-pages-temp/2a-fleet-platform-traffic.png b/skyquake/framework/style/img/platform-pages-temp/2a-fleet-platform-traffic.png new file mode 100644 index 000000000..041325dd5 Binary files /dev/null and b/skyquake/framework/style/img/platform-pages-temp/2a-fleet-platform-traffic.png differ diff --git a/skyquake/framework/style/img/platform-pages-temp/2b-fleet-platform-resources.png b/skyquake/framework/style/img/platform-pages-temp/2b-fleet-platform-resources.png new file mode 100644 index 000000000..f7f28669c Binary files /dev/null and b/skyquake/framework/style/img/platform-pages-temp/2b-fleet-platform-resources.png differ diff --git a/skyquake/framework/style/img/platform-pages-temp/2b-fleet-platform-traffic.png b/skyquake/framework/style/img/platform-pages-temp/2b-fleet-platform-traffic.png new file mode 100644 index 000000000..041325dd5 Binary files /dev/null and b/skyquake/framework/style/img/platform-pages-temp/2b-fleet-platform-traffic.png differ diff --git a/skyquake/framework/style/img/platform-pages-temp/3a-fleet-platform-resources.png b/skyquake/framework/style/img/platform-pages-temp/3a-fleet-platform-resources.png new file mode 100644 index 000000000..f7f28669c Binary files /dev/null and b/skyquake/framework/style/img/platform-pages-temp/3a-fleet-platform-resources.png differ diff --git a/skyquake/framework/style/img/platform-pages-temp/3a-fleet-platform-traffic.png b/skyquake/framework/style/img/platform-pages-temp/3a-fleet-platform-traffic.png new file mode 100644 index 000000000..041325dd5 Binary files /dev/null and b/skyquake/framework/style/img/platform-pages-temp/3a-fleet-platform-traffic.png differ diff --git a/skyquake/framework/style/img/platform-pages-temp/3b-fleet-platform-resources.png b/skyquake/framework/style/img/platform-pages-temp/3b-fleet-platform-resources.png new file mode 100644 index 000000000..f7f28669c Binary files /dev/null and b/skyquake/framework/style/img/platform-pages-temp/3b-fleet-platform-resources.png differ diff --git a/skyquake/framework/style/img/platform-pages-temp/3b-fleet-platform-traffic.png b/skyquake/framework/style/img/platform-pages-temp/3b-fleet-platform-traffic.png new file mode 100644 index 000000000..041325dd5 Binary files /dev/null and b/skyquake/framework/style/img/platform-pages-temp/3b-fleet-platform-traffic.png differ diff --git a/skyquake/framework/style/img/router-icon.png b/skyquake/framework/style/img/router-icon.png new file mode 100644 index 000000000..93282df82 Binary files /dev/null and b/skyquake/framework/style/img/router-icon.png differ diff --git a/skyquake/framework/style/img/so-pages-temp/ipTrafTemp.png b/skyquake/framework/style/img/so-pages-temp/ipTrafTemp.png new file mode 100644 index 000000000..1a6566d08 Binary files /dev/null and b/skyquake/framework/style/img/so-pages-temp/ipTrafTemp.png differ diff --git a/skyquake/framework/style/img/so-pages-temp/securityTemp.png b/skyquake/framework/style/img/so-pages-temp/securityTemp.png new file mode 100644 index 000000000..f22abf4a9 Binary files /dev/null and b/skyquake/framework/style/img/so-pages-temp/securityTemp.png differ diff --git a/skyquake/framework/style/img/svg/launch-fleet-icn-close.svg b/skyquake/framework/style/img/svg/launch-fleet-icn-close.svg new file mode 100644 index 000000000..d364ff8b0 --- /dev/null +++ b/skyquake/framework/style/img/svg/launch-fleet-icn-close.svg @@ -0,0 +1,10 @@ + + + + + + + diff --git a/skyquake/framework/style/img/svg/launch-fleet-icn-edit.svg b/skyquake/framework/style/img/svg/launch-fleet-icn-edit.svg new file mode 100644 index 000000000..b62dc8a4e --- /dev/null +++ b/skyquake/framework/style/img/svg/launch-fleet-icn-edit.svg @@ -0,0 +1,13 @@ + + + + + + + + + + diff --git a/skyquake/framework/style/img/svg/launch-fleet-network-01-active.svg b/skyquake/framework/style/img/svg/launch-fleet-network-01-active.svg new file mode 100755 index 000000000..f3de69a4e --- /dev/null +++ b/skyquake/framework/style/img/svg/launch-fleet-network-01-active.svg @@ -0,0 +1,20 @@ + + + + + + + + + + diff --git a/skyquake/framework/style/img/svg/launch-fleet-network-01-inactive.svg b/skyquake/framework/style/img/svg/launch-fleet-network-01-inactive.svg new file mode 100755 index 000000000..19700d265 --- /dev/null +++ b/skyquake/framework/style/img/svg/launch-fleet-network-01-inactive.svg @@ -0,0 +1,20 @@ + + + + + + + + + + diff --git a/skyquake/framework/style/img/svg/launch-fleet-network-02-active.svg b/skyquake/framework/style/img/svg/launch-fleet-network-02-active.svg new file mode 100755 index 000000000..c29934b82 --- /dev/null +++ b/skyquake/framework/style/img/svg/launch-fleet-network-02-active.svg @@ -0,0 +1,23 @@ + + + + + + + + + + diff --git a/skyquake/framework/style/img/svg/launch-fleet-network-02-inactive.svg b/skyquake/framework/style/img/svg/launch-fleet-network-02-inactive.svg new file mode 100755 index 000000000..4d15e2b92 --- /dev/null +++ b/skyquake/framework/style/img/svg/launch-fleet-network-02-inactive.svg @@ -0,0 +1,23 @@ + + + + + + + + + + diff --git a/skyquake/framework/style/img/svg/launch-fleet-network-03-active.svg b/skyquake/framework/style/img/svg/launch-fleet-network-03-active.svg new file mode 100755 index 000000000..d1b1aec18 --- /dev/null +++ b/skyquake/framework/style/img/svg/launch-fleet-network-03-active.svg @@ -0,0 +1,19 @@ + + + + + + + + + + diff --git a/skyquake/framework/style/img/svg/launch-fleet-network-03-inactive.svg b/skyquake/framework/style/img/svg/launch-fleet-network-03-inactive.svg new file mode 100755 index 000000000..03c74fd71 --- /dev/null +++ b/skyquake/framework/style/img/svg/launch-fleet-network-03-inactive.svg @@ -0,0 +1,19 @@ + + + + + + + + + + diff --git a/skyquake/framework/style/img/svg/launch-fleet-network-04-active.svg b/skyquake/framework/style/img/svg/launch-fleet-network-04-active.svg new file mode 100755 index 000000000..774653a35 --- /dev/null +++ b/skyquake/framework/style/img/svg/launch-fleet-network-04-active.svg @@ -0,0 +1,20 @@ + + + + + + + + + + diff --git a/skyquake/framework/style/img/svg/launch-fleet-network-04-inactive.svg b/skyquake/framework/style/img/svg/launch-fleet-network-04-inactive.svg new file mode 100755 index 000000000..3897a1e5e --- /dev/null +++ b/skyquake/framework/style/img/svg/launch-fleet-network-04-inactive.svg @@ -0,0 +1,20 @@ + + + + + + + + + + diff --git a/skyquake/framework/style/img/svg/launch-fleet-network-05-active.svg b/skyquake/framework/style/img/svg/launch-fleet-network-05-active.svg new file mode 100755 index 000000000..774653a35 --- /dev/null +++ b/skyquake/framework/style/img/svg/launch-fleet-network-05-active.svg @@ -0,0 +1,20 @@ + + + + + + + + + + diff --git a/skyquake/framework/style/img/svg/launch-fleet-network-05-inactive.svg b/skyquake/framework/style/img/svg/launch-fleet-network-05-inactive.svg new file mode 100755 index 000000000..3897a1e5e --- /dev/null +++ b/skyquake/framework/style/img/svg/launch-fleet-network-05-inactive.svg @@ -0,0 +1,20 @@ + + + + + + + + + + diff --git a/skyquake/framework/style/img/svg/launch-fleet-network-06-active.svg b/skyquake/framework/style/img/svg/launch-fleet-network-06-active.svg new file mode 100755 index 000000000..31d7ae34b --- /dev/null +++ b/skyquake/framework/style/img/svg/launch-fleet-network-06-active.svg @@ -0,0 +1,25 @@ + + + + + + + + + + diff --git a/skyquake/framework/style/img/svg/launch-fleet-network-06-inactive.svg b/skyquake/framework/style/img/svg/launch-fleet-network-06-inactive.svg new file mode 100755 index 000000000..e5a845bf3 --- /dev/null +++ b/skyquake/framework/style/img/svg/launch-fleet-network-06-inactive.svg @@ -0,0 +1,25 @@ + + + + + + + + + + diff --git a/skyquake/framework/style/img/svg/launch-fleet-network-07-active.svg b/skyquake/framework/style/img/svg/launch-fleet-network-07-active.svg new file mode 100755 index 000000000..4888d818a --- /dev/null +++ b/skyquake/framework/style/img/svg/launch-fleet-network-07-active.svg @@ -0,0 +1,20 @@ + + + + + + + + + + diff --git a/skyquake/framework/style/img/svg/launch-fleet-network-07-inactive.svg b/skyquake/framework/style/img/svg/launch-fleet-network-07-inactive.svg new file mode 100755 index 000000000..81ee3cccd --- /dev/null +++ b/skyquake/framework/style/img/svg/launch-fleet-network-07-inactive.svg @@ -0,0 +1,20 @@ + + + + + + + + + + diff --git a/skyquake/framework/style/img/svg/launch-fleet-network-08-active.svg b/skyquake/framework/style/img/svg/launch-fleet-network-08-active.svg new file mode 100755 index 000000000..9ce2e1671 --- /dev/null +++ b/skyquake/framework/style/img/svg/launch-fleet-network-08-active.svg @@ -0,0 +1,24 @@ + + + + + + + + + + diff --git a/skyquake/framework/style/img/svg/launch-fleet-network-08-inactive.svg b/skyquake/framework/style/img/svg/launch-fleet-network-08-inactive.svg new file mode 100755 index 000000000..02525a634 --- /dev/null +++ b/skyquake/framework/style/img/svg/launch-fleet-network-08-inactive.svg @@ -0,0 +1,24 @@ + + + + + + + + + + diff --git a/skyquake/framework/style/img/svg/launch-fleet-network-09-active.svg b/skyquake/framework/style/img/svg/launch-fleet-network-09-active.svg new file mode 100755 index 000000000..a703d2eac --- /dev/null +++ b/skyquake/framework/style/img/svg/launch-fleet-network-09-active.svg @@ -0,0 +1,21 @@ + + + + + + + + + + diff --git a/skyquake/framework/style/img/svg/launch-fleet-network-09-inactive.svg b/skyquake/framework/style/img/svg/launch-fleet-network-09-inactive.svg new file mode 100755 index 000000000..8e1d7021b --- /dev/null +++ b/skyquake/framework/style/img/svg/launch-fleet-network-09-inactive.svg @@ -0,0 +1,21 @@ + + + + + + + + + + diff --git a/skyquake/framework/style/img/svg/launch-fleet-network-10-active.svg b/skyquake/framework/style/img/svg/launch-fleet-network-10-active.svg new file mode 100755 index 000000000..a9b54960f --- /dev/null +++ b/skyquake/framework/style/img/svg/launch-fleet-network-10-active.svg @@ -0,0 +1,27 @@ + + + + + + + + + + diff --git a/skyquake/framework/style/img/svg/launch-fleet-network-10-inactive.svg b/skyquake/framework/style/img/svg/launch-fleet-network-10-inactive.svg new file mode 100755 index 000000000..91b2b5922 --- /dev/null +++ b/skyquake/framework/style/img/svg/launch-fleet-network-10-inactive.svg @@ -0,0 +1,27 @@ + + + + + + + + + + diff --git a/skyquake/framework/style/img/svg/launch-fleet-network-11-active.svg b/skyquake/framework/style/img/svg/launch-fleet-network-11-active.svg new file mode 100644 index 000000000..e18114c65 --- /dev/null +++ b/skyquake/framework/style/img/svg/launch-fleet-network-11-active.svg @@ -0,0 +1,22 @@ + + + + + + + + + + diff --git a/skyquake/framework/style/img/svg/launch-fleet-network-11-inactive.svg b/skyquake/framework/style/img/svg/launch-fleet-network-11-inactive.svg new file mode 100644 index 000000000..173d3b17d --- /dev/null +++ b/skyquake/framework/style/img/svg/launch-fleet-network-11-inactive.svg @@ -0,0 +1,22 @@ + + + + + + + + + + diff --git a/skyquake/framework/style/img/svg/launch-fleet-pool-01-active.svg b/skyquake/framework/style/img/svg/launch-fleet-pool-01-active.svg new file mode 100644 index 000000000..b55508703 --- /dev/null +++ b/skyquake/framework/style/img/svg/launch-fleet-pool-01-active.svg @@ -0,0 +1,27 @@ + + + + + + + + + + diff --git a/skyquake/framework/style/img/svg/launch-fleet-pool-01-inactive.svg b/skyquake/framework/style/img/svg/launch-fleet-pool-01-inactive.svg new file mode 100644 index 000000000..c8041db78 --- /dev/null +++ b/skyquake/framework/style/img/svg/launch-fleet-pool-01-inactive.svg @@ -0,0 +1,27 @@ + + + + + + + + + + diff --git a/skyquake/framework/style/img/svg/launch-fleet-pool-02-active.svg b/skyquake/framework/style/img/svg/launch-fleet-pool-02-active.svg new file mode 100644 index 000000000..229a45103 --- /dev/null +++ b/skyquake/framework/style/img/svg/launch-fleet-pool-02-active.svg @@ -0,0 +1,27 @@ + + + + + + + + + + diff --git a/skyquake/framework/style/img/svg/launch-fleet-pool-02-inactive.svg b/skyquake/framework/style/img/svg/launch-fleet-pool-02-inactive.svg new file mode 100644 index 000000000..4e45a01a7 --- /dev/null +++ b/skyquake/framework/style/img/svg/launch-fleet-pool-02-inactive.svg @@ -0,0 +1,27 @@ + + + + + + + + + + diff --git a/skyquake/framework/style/img/svg/launchpad-icn-create-environment-large.svg b/skyquake/framework/style/img/svg/launchpad-icn-create-environment-large.svg new file mode 100644 index 000000000..f1ef10226 --- /dev/null +++ b/skyquake/framework/style/img/svg/launchpad-icn-create-environment-large.svg @@ -0,0 +1,10 @@ + + + + + + + + diff --git a/skyquake/framework/style/img/svg/launchpad-icn-newTab.svg b/skyquake/framework/style/img/svg/launchpad-icn-newTab.svg new file mode 100644 index 000000000..ab00cfa4e --- /dev/null +++ b/skyquake/framework/style/img/svg/launchpad-icn-newTab.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + diff --git a/skyquake/framework/style/img/svg/launchpad-icn-play.svg b/skyquake/framework/style/img/svg/launchpad-icn-play.svg new file mode 100644 index 000000000..696e698ea --- /dev/null +++ b/skyquake/framework/style/img/svg/launchpad-icn-play.svg @@ -0,0 +1,9 @@ + + + + + + + + diff --git a/skyquake/framework/style/img/svg/launchpad-icn-sliders.svg b/skyquake/framework/style/img/svg/launchpad-icn-sliders.svg new file mode 100644 index 000000000..103c9ba05 --- /dev/null +++ b/skyquake/framework/style/img/svg/launchpad-icn-sliders.svg @@ -0,0 +1,14 @@ + + + + + + + + + + diff --git a/skyquake/framework/style/img/svg/riftio_logo.svg b/skyquake/framework/style/img/svg/riftio_logo.svg new file mode 100644 index 000000000..d2955f498 --- /dev/null +++ b/skyquake/framework/style/img/svg/riftio_logo.svg @@ -0,0 +1 @@ + diff --git a/skyquake/framework/style/img/svg/riftio_logo_white.svg b/skyquake/framework/style/img/svg/riftio_logo_white.svg new file mode 100644 index 000000000..d456d76ca --- /dev/null +++ b/skyquake/framework/style/img/svg/riftio_logo_white.svg @@ -0,0 +1 @@ + diff --git a/skyquake/framework/style/img/switch-icon.png b/skyquake/framework/style/img/switch-icon.png new file mode 100644 index 000000000..88a77152f Binary files /dev/null and b/skyquake/framework/style/img/switch-icon.png differ diff --git a/skyquake/framework/style/img/table-cell-bg.png b/skyquake/framework/style/img/table-cell-bg.png new file mode 100644 index 000000000..8f4c8a86d Binary files /dev/null and b/skyquake/framework/style/img/table-cell-bg.png differ diff --git a/skyquake/framework/style/img/traffic-sim-diagram.png b/skyquake/framework/style/img/traffic-sim-diagram.png new file mode 100644 index 000000000..db63b6eb6 Binary files /dev/null and b/skyquake/framework/style/img/traffic-sim-diagram.png differ diff --git a/skyquake/framework/style/img/tunnels.png b/skyquake/framework/style/img/tunnels.png new file mode 100644 index 000000000..b4fe6d9e8 Binary files /dev/null and b/skyquake/framework/style/img/tunnels.png differ diff --git a/skyquake/framework/style/img/viewport-dash-temp.png b/skyquake/framework/style/img/viewport-dash-temp.png new file mode 100644 index 000000000..0690c8ecd Binary files /dev/null and b/skyquake/framework/style/img/viewport-dash-temp.png differ diff --git a/skyquake/framework/style/img/viewport-dash-v2-temp.png b/skyquake/framework/style/img/viewport-dash-v2-temp.png new file mode 100644 index 000000000..144bdc35f Binary files /dev/null and b/skyquake/framework/style/img/viewport-dash-v2-temp.png differ diff --git a/skyquake/framework/style/img/viewport-dash-v3-temp.png b/skyquake/framework/style/img/viewport-dash-v3-temp.png new file mode 100644 index 000000000..cbdfd6ec4 Binary files /dev/null and b/skyquake/framework/style/img/viewport-dash-v3-temp.png differ diff --git a/skyquake/framework/style/img/viewport-nav-bottom.png b/skyquake/framework/style/img/viewport-nav-bottom.png new file mode 100644 index 000000000..d9f7669d3 Binary files /dev/null and b/skyquake/framework/style/img/viewport-nav-bottom.png differ diff --git a/skyquake/framework/style/img/viewport-nav-center.png b/skyquake/framework/style/img/viewport-nav-center.png new file mode 100644 index 000000000..2edeb3fc5 Binary files /dev/null and b/skyquake/framework/style/img/viewport-nav-center.png differ diff --git a/skyquake/framework/style/img/viewport-nav-left.png b/skyquake/framework/style/img/viewport-nav-left.png new file mode 100644 index 000000000..9b017de4b Binary files /dev/null and b/skyquake/framework/style/img/viewport-nav-left.png differ diff --git a/skyquake/framework/style/img/viewport-nav-right.png b/skyquake/framework/style/img/viewport-nav-right.png new file mode 100644 index 000000000..5fc43b4ce Binary files /dev/null and b/skyquake/framework/style/img/viewport-nav-right.png differ diff --git a/skyquake/framework/style/img/viewport-nav-top.png b/skyquake/framework/style/img/viewport-nav-top.png new file mode 100644 index 000000000..7c0717ddd Binary files /dev/null and b/skyquake/framework/style/img/viewport-nav-top.png differ diff --git a/skyquake/framework/style/img/viewport-platform-temp.png b/skyquake/framework/style/img/viewport-platform-temp.png new file mode 100644 index 000000000..cbdfd6ec4 Binary files /dev/null and b/skyquake/framework/style/img/viewport-platform-temp.png differ diff --git a/skyquake/framework/style/img/viewport-sla-graph.svg b/skyquake/framework/style/img/viewport-sla-graph.svg new file mode 100644 index 000000000..cd120ccf3 --- /dev/null +++ b/skyquake/framework/style/img/viewport-sla-graph.svg @@ -0,0 +1,45 @@ + + + + + + + 100 + % + + + diff --git a/skyquake/framework/style/img/viewport-vim-temp.png b/skyquake/framework/style/img/viewport-vim-temp.png new file mode 100644 index 000000000..0f8709fab Binary files /dev/null and b/skyquake/framework/style/img/viewport-vim-temp.png differ diff --git a/skyquake/framework/style/img/viewport-vnf-10.svg b/skyquake/framework/style/img/viewport-vnf-10.svg new file mode 100644 index 000000000..4b7c77c1c --- /dev/null +++ b/skyquake/framework/style/img/viewport-vnf-10.svg @@ -0,0 +1,3 @@ + + + diff --git a/skyquake/framework/style/img/viewport-vnf-50.svg b/skyquake/framework/style/img/viewport-vnf-50.svg new file mode 100644 index 000000000..895fff8ba --- /dev/null +++ b/skyquake/framework/style/img/viewport-vnf-50.svg @@ -0,0 +1,3 @@ + + + diff --git a/skyquake/framework/style/img/viewport-vnf-temp.png b/skyquake/framework/style/img/viewport-vnf-temp.png new file mode 100644 index 000000000..2debb1b35 Binary files /dev/null and b/skyquake/framework/style/img/viewport-vnf-temp.png differ diff --git a/skyquake/framework/style/img/vim-icon-corners.png b/skyquake/framework/style/img/vim-icon-corners.png new file mode 100644 index 000000000..3fdea44d9 Binary files /dev/null and b/skyquake/framework/style/img/vim-icon-corners.png differ diff --git a/skyquake/framework/style/img/vim-icon-diamond.png b/skyquake/framework/style/img/vim-icon-diamond.png new file mode 100644 index 000000000..76c2cd312 Binary files /dev/null and b/skyquake/framework/style/img/vim-icon-diamond.png differ diff --git a/skyquake/framework/style/img/vim-icon-halfcircle-bottom.png b/skyquake/framework/style/img/vim-icon-halfcircle-bottom.png new file mode 100644 index 000000000..4440a9ac6 Binary files /dev/null and b/skyquake/framework/style/img/vim-icon-halfcircle-bottom.png differ diff --git a/skyquake/framework/style/img/vim-icon-halfcircle-top.png b/skyquake/framework/style/img/vim-icon-halfcircle-top.png new file mode 100644 index 000000000..25d8e1d47 Binary files /dev/null and b/skyquake/framework/style/img/vim-icon-halfcircle-top.png differ diff --git a/skyquake/framework/style/img/vim-icon-plus.png b/skyquake/framework/style/img/vim-icon-plus.png new file mode 100644 index 000000000..88c7f8358 Binary files /dev/null and b/skyquake/framework/style/img/vim-icon-plus.png differ diff --git a/skyquake/framework/style/img/vim-icon-soliddiamond.png b/skyquake/framework/style/img/vim-icon-soliddiamond.png new file mode 100644 index 000000000..7d6ca714c Binary files /dev/null and b/skyquake/framework/style/img/vim-icon-soliddiamond.png differ diff --git a/skyquake/framework/style/img/vim-icon-solidsquare.png b/skyquake/framework/style/img/vim-icon-solidsquare.png new file mode 100644 index 000000000..03466e451 Binary files /dev/null and b/skyquake/framework/style/img/vim-icon-solidsquare.png differ diff --git a/skyquake/framework/style/img/vim-icon-solidstar.png b/skyquake/framework/style/img/vim-icon-solidstar.png new file mode 100644 index 000000000..a756b55c8 Binary files /dev/null and b/skyquake/framework/style/img/vim-icon-solidstar.png differ diff --git a/skyquake/framework/style/img/vim-icon-triangle.png b/skyquake/framework/style/img/vim-icon-triangle.png new file mode 100644 index 000000000..fa53ce453 Binary files /dev/null and b/skyquake/framework/style/img/vim-icon-triangle.png differ diff --git a/skyquake/framework/style/layout.scss b/skyquake/framework/style/layout.scss new file mode 100644 index 000000000..358564c76 --- /dev/null +++ b/skyquake/framework/style/layout.scss @@ -0,0 +1,149 @@ + +/* + * + * Copyright 2016 RIFT.IO Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +html, body { + height:100%; + width:100%; +} +.flex { + display:-ms-flexbox; + display:flex; + &-column { + display:-ms-flexbox; + display:flex; + -ms-flex-direction:column; + flex-direction:column; + } + &.space-between { + -ms-flex-pack:justify; + justify-content:space-between; + } +} +.layout-col { + display: -ms-flexbox; + display: flex; + -ms-flex-direction: column; + flex-direction: column; + box-sizing: border-box; + -ms-flex: 1 1 auto; + flex: 1 1 auto; + &_left { + -ms-flex: 1 1 70%; + flex: 1 1 70%; + } + &_right { + -ms-flex: 0 1 30%; + flex: 0 1 30%; + } + } + +.app-body { + width:100%; + /*height:100%;*/ + display:-ms-flexbox; + display:flex; + -ms-flex-direction:column; + flex-direction:column; + -ms-flex-pack:start; + justify-content:flex-start; + -ms-flex-align:stretch; + align-items:stretch; + -ms-flex-line-pack: stretch; + align-content: stretch; + margin:0px; +} + +.header-app h1 { + /* background: url('../../../../assets/img/header-logo.png') no-repeat left center !important;*/ + height: 51px; + line-height: 51px; + margin-left: 80px; + padding-left: 118px; + position: absolute; + left: 0; + text-transform: uppercase; +} + + +html, body, .application{ + margin:0px; + height:100%; +} + +.wrap { + height: 100%; + display: -ms-flexbox; + display: flex; + .content-wrapper { + height:100%; + overflow-y:auto; + } +} + +.application{ + display:-ms-flexbox; + display:flex; + -ms-flex:1 1 auto; + flex:1 1 auto; + -ms-flex-direction:column; + flex-direction:column; + height:auto; + overflow:auto; +} + + +.content { + display:-ms-flexbox; + display:flex; + -ms-flex-direction:column; + flex-direction:column; +} +.panel { + display:-ms-flexbox; + display:flex; + margin:0.25rem; + border:1px solid black; + border-offset: -1px; + /*flex:1 1 auto;*/ + &--container { + padding:0.25rem; + } + &--column { + -ms-flex-direction:column; + flex-direction:column; + } + &--grow { + -ms-flex:1; + flex:1; + } + &--wrap { + -ms-flex-wrap:wrap; + flex-wrap:wrap; + } +} +.card { + +} +.size-2 { + -ms-flex: 1 1 20%; + flex: 1 1 20%; +} +.size-5 { + -ms-flex: 1 1 50%; + flex: 1 1 50%; +} diff --git a/skyquake/framework/style/variables.scss b/skyquake/framework/style/variables.scss new file mode 100644 index 000000000..a1d6cbd34 --- /dev/null +++ b/skyquake/framework/style/variables.scss @@ -0,0 +1,13 @@ +/* TRANSITIONS +############################################################################ */ + +$transition: all 300ms cubic-bezier(0.230, 1.000, 0.320, 1.000); + + + +/* SHADOW +############################################################################ */ + +$shadow: 0 8px 6px -6px hsla(0,0,0,.35); + +// $focus-shadow: 0 0 6px 0 $focus; diff --git a/skyquake/framework/style/vendor/css-reset-2.0/css-reset.css b/skyquake/framework/style/vendor/css-reset-2.0/css-reset.css new file mode 100644 index 000000000..ba3903207 --- /dev/null +++ b/skyquake/framework/style/vendor/css-reset-2.0/css-reset.css @@ -0,0 +1,5 @@ +/* http://meyerweb.com/eric/tools/css/reset/ + v2.0 | 20110126 + License: none (public domain) +*/ +html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kb,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video{border:0;font-size:100%;font:inherit;vertical-align:baseline;margin:0;padding:0}article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section{display:block}body{line-height:1}ol,ul{list-style:none}blockquote,q{quotes:none}blockquote:before,blockquote:after,q:before,q:after{content:none}table{border-collapse:collapse;border-spacing:0} diff --git a/skyquake/framework/utils/guid.js b/skyquake/framework/utils/guid.js new file mode 100644 index 000000000..b266c3793 --- /dev/null +++ b/skyquake/framework/utils/guid.js @@ -0,0 +1,30 @@ + +/* + * + * Copyright 2016 RIFT.IO Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +/** + * @return {string} + */ +function GUID() { + // http://byronsalau.com/blog/how-to-create-a-guid-uuid-in-javascript/ + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { + var r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8); + return v.toString(16); + }); +} + +module.exports = GUID; diff --git a/skyquake/framework/utils/rw.js b/skyquake/framework/utils/rw.js new file mode 100644 index 000000000..1cca8f2c4 --- /dev/null +++ b/skyquake/framework/utils/rw.js @@ -0,0 +1,924 @@ + +/* + * + * Copyright 2016 RIFT.IO Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// rw.js will no longer be necessary when Angular dependency no longer exists +/** + * reset values in an array, useful when an array instance is + * being observed for changes and simply setting array reference + * to a new array instance is not a great option. + * + * Example: + * x = ['a', 'b'] + * setValues(x, ['c', 'd']) + * x + * ['c', 'd'] + */ + +var rw = rw || { + // Angular specific for now but can be modified in one location if need ever be + BaseController: function() { + var self = this; + + // handles factories with detach/cancel methods and listeners with cancel method + if (this.$scope) { + this.$scope.$on('$stateChangeStart', function() { + var properties = Object.getOwnPropertyNames(self); + + properties.forEach(function(key) { + + var propertyValue = self[key]; + + if (propertyValue) { + if (Array.isArray(propertyValue)) { + propertyValue.forEach(function(item) { + if (item.off && typeof item.off == 'function') { + item.off(null, null, self); + } + }); + propertyValue.length = 0; + } else { + if (propertyValue.detached && typeof propertyValue.detached == 'function') { + propertyValue.detached(); + } else if (propertyValue.cancel && typeof propertyValue.cancel == 'function') { + propertyValue.cancel(); + } else if (propertyValue.off && typeof propertyValue.off == 'function') { + propertyValue.off(null, null, self); + } + } + } + }); + }); + }; + + // call in to do additional cleanup + if (self.doCleanup && typeof self.doCleanup == 'function') { + self.doCleanup(); + }; + }, + getSearchParams: function (url) { + var a = document.createElement('a'); + a.href = url; + var params = {}; + var items = a.search.replace('?', '').split('&'); + for (var i = 0; i < items.length; i++) { + if (items[i].length > 0) { + var key_value = items[i].split('='); + params[key_value[0]] = key_value[1]; + } + } + return params; + }, + + inplaceUpdate : function(ary, values) { + var args = [0, ary.length]; + Array.prototype.splice.apply(ary, args.concat(values)); + } +}; + +// explore making this configurable somehow +// api_server = 'http://localhost:5050'; +rw.search_params = rw.getSearchParams(window.location.href); +// MONKEY PATCHING +if (Element.prototype == null) { + Element.prototype.uniqueId = 0; +} + +Element.prototype.generateUniqueId = function() { + Element.prototype.uniqueId++; + return 'uid' + Element.prototype.uniqueId; +}; + +Element.prototype.empty = Element.prototype.empty || function() { + while(this.firstChild) { + this.removeChild(this.firstChild); + } +}; + +/** + * Merge one object into another. No circular reference checking so if there + * is there might be infinite recursion. + */ +rw.merge = function(obj1, obj2) { + for (prop in obj2) { + if (typeof(obj2[prop]) == 'object') { + if (prop in obj1) { + this.merge(obj1[prop], obj2[prop]); + } else { + obj1[prop] = obj2[prop]; + } + } else { + obj1[prop] = obj2[prop]; + } + } +} + +Element.prototype.getElementByTagName = function(tagName) { + for (var i = this.children.length - 1; i >= 0; i--) { + if (this.children[i].localName == tagName) { + return this.children[i]; + } + } +}; + +rw.ui = { + + computedWidth : function(elem, defaultValue) { + var s = window.getComputedStyle(elem); + var w = s['width']; + if (w && w != 'auto') { + // I've never seen this case, but here anyway + return w; + } + w = s['min-width']; + if (w) { + return w; + } + return defaultValue; + }, + + computedHeight : function(elem, defaultValue) { + var s = window.getComputedStyle(elem); + var w = s['height']; + if (w && w != 'auto') { + // I've never seen this case, but here anyway + return w; + } + w = s['min-height']; + if (w) { + return w; + } + return defaultValue; + }, + + computedStyle : function(elem, property, defaultValue) { + var s = window.getComputedStyle(elem); + if (s[property]) { + return s[property]; + } + return defaultValue; + }, + + odd : function(n) { + return Math.abs(n) % 2 == 1 ? 'odd' : ''; + }, + + status : function(s) { + return s == 'OK' ? 'yes' : 'no'; + }, + + capitalize: function(s) { + return s ? s.charAt(0).toUpperCase() + s.slice(1) : ''; + }, + + fmt: function(n, fmtStr) { + return numeral(n).format(fmtStr); + }, + + // assumes values are in megabytes! + bytes: function(n, capacity) { + if (n === undefined || isNaN(n)) { + return ''; + } + var units = false; + if (capacity === undefined) { + capacity = n; + units = true; + } + var suffixes = [ + ['KB' , 1000], + ['MB' , 1000000], + ['GB' , 1000000000], + ['TB' , 1000000000000], + ['PB' , 1000000000000000] + ]; + for (var i = 0; i < suffixes.length; i++) { + if (capacity < suffixes[i][1]) { + return (numeral((n * 1000) / suffixes[i][1]).format('0,0') + (units ? suffixes[i][0] : '')); + } + } + return n + (units ? 'B' : ''); + }, + + // assumes values are already in megabits! + bits: function(n, capacity) { + if (n === undefined || isNaN(n)) { + return ''; + } + var units = false; + if (capacity === undefined) { + capacity = n; + units = true; + } + var suffixes = [ + ['Mbps' , 1000], + ['Gbps' , 1000000], + ['Tbps' , 1000000000], + ['Pbps' , 1000000000000] + ]; + for (var i = 0; i < suffixes.length; i++) { + if (capacity < suffixes[i][1]) { + return (numeral((n * 1000) / suffixes[i][1]).format('0,0') + (units ? suffixes[i][0] : '')); + } + } + return n + (units ? 'Bps' : ''); + }, + + ppsUtilization: function(pps) { + return pps ? numeral(pps / 1000000).format('0.0') : ''; + }, + ppsUtilizationMax: function(item) { + var rate = item.rate / 10000; + var max = item.max * 0.0015; + return rate/max; + }, + bpsAsPps: function(speed) { + return parseInt(speed) * 0.0015; + }, + + upperCase: function(s) { + return s.toUpperCase() + }, + + mbpsAsPps: function(mbps) { + var n = parseInt(mbps); + return isNaN(n) ? 0 : rw.ui.fmt(rw.ui.bpsAsPps(n * 1000000), '0a').toUpperCase(); + }, + + k: function(n) { + return rw.ui.fmt(rw.ui.noNaN(n), '0a'); + }, + + noNaN: function(n) { + return isNaN(n) ? 0 : n; + }, + + // Labels used in system + l10n : { + vnf: { + 'trafsimclient': 'Traf Sim Client', + 'trafsimserver': 'Traf Sim Server', + 'ltemmesim': 'MME', + 'ltegwsim': 'SAE Gateway', + 'trafgen': 'Traf Gen Client', + 'trafsink': 'Traf Gen Server', + 'loadbal': 'Load Balancer', + 'slbalancer': 'Scriptable Load Balancer' + } + } +}; + +rw.math = { + editXml : function(xmlTemplate, domEditor) { + var str2dom = new DOMParser(); + var dom = str2dom.parseFromString(xmlTemplate, 'text/xml'); + if (domEditor) { + domEditor(dom); + } + var dom2str = new XMLSerializer(); + return dom2str.serializeToString(dom); + }, + + num : function(el, tag) { + return parseInt(this.str(el, tag), 10); + }, + + str : function(el, tag) { + var tags = el.getElementsByTagName(tag); + return tags.length > 0 ? tags[0].textContent.trim() : ''; + }, + + sum : function(total, i, key, value) { + if (_.isNumber(value)) { + total[key] = (i === 0 ? value : (total[key] + value)); + } + }, + + sum2 : function(key) { + return function(prev, cur, i) { + var value = cur[key]; + if (_.isNumber(value)) { + if (typeof(prev) === 'undefined') { + return value; + } else { + return prev + value; + } + } + return prev; + }; + }, + + max : function(key) { + return function(prev, cur, i) { + var value = cur[key]; + if (_.isNumber(value)) { + if (typeof(prev) === 'undefined') { + return value; + } else if (prev < value) { + return value; + } + } + return prev; + }; + }, + + avg2 : function(key) { + var sum = rw.math.sum2(key); + return function(prev, cur, i, ary) { + var s = sum(prev, cur, i); + if (i === ary.length - 1) { + return s / ary.length; + } + return s; + }; + }, + + avg : function(rows, key) { + var total = XmlMath.total(rows, key); + return total / rows.length; + }, + + total : function(rows, key) { + var total = 0; + for (var i = rows.length - 1; i >= 0; i--) { + var n = parseInt(rows[i][key]); + if (!isNaN(n)) { + total += n; + } + } + return total; + }, + + run : function(total, rows, operation) { + var i; + var f = function(value, key) { + operation(total, i, key, value); + }; + for (i = 0; i < rows.length; i++) { + _.each(rows[i], f); + } + } +}; + + +rw.db = { + open: function (name, onInit, onOpen) { + var self = this; + + var open = window.indexedDB.open(name, 2); + + open.onerror = function (e) { + console.log('Could not open database', name, e.target.error.message); + }; + + open.onsuccess = function (e) { + var db = e.target.result; + onOpen(db); + }; + + open.onupgradeneeded = function (e) { + var db = e.target.result; + onInit(db); + }; + } +}; + +rw.db.Offline = function(name) { + this.name = name; + this.datastore = 'offline'; +}; + +rw.db.Offline.prototype = { + + open : function(onOpen) { + rw.db.open(this.name, this.init.bind(this), onOpen); + }, + + getItem : function(url, onData) { + var self = this; + this.open(function(db) { + var query = db.transaction(self.datastore) + .objectStore(self.datastore) + .get(url); + query.onsuccess = function(e) { + if (e.target.result) { + onData(e.target.result.data); + } else { + console.log('No data found for ' + url + '. You may need to rebuild your offline database'); + } + } + }); + }, + + init : function(db) { + var self = this; + if (!db.objectStoreNames.contains(this.datastore)) { + var create = db.createObjectStore(this.datastore, {keyPath: 'url'}); + create.onerror = function(e) { + console.log('Could not create object store ' + this.datastore); + } + } + }, + + saveStore : function(store) { + var self = this; + this.open(function(db) { + for (var i = 0; i < store.length; i++) { + var save = db.transaction(self.datastore, "readwrite") + .objectStore(self.datastore) + .put(store[i]); + } + }); + } +}; + +rw.api = { + nRequests : 0, + tRequestsMs: 0, + tRequestMaxMs: 0, + nRequestsByUrl: {}, + statsId: null, + + resetStats: function() { + rw.api.nRequests = 0; + rw.api.tRequestsMs = 0; + rw.api.tRequestMaxMs = 0; + rw.api.nRequestsByUrl = {}; + }, + + handleAjaxError : function (req, status, err) { + console.log('failed', req, status, err); + }, + + json: function(url) { + return this.get(url, 'application/json') + }, + + get: function(url, accept) { + var deferred = jQuery.Deferred(); + if (rw.api.offline) { + rw.api.offline.getItem(url, function (data) { + deferred.resolve(data); + }); + } else { + var startTime = new Date().getTime(); + jQuery.ajax(rw.api.server + url, { + type: 'GET', + dataType: 'json', + error: rw.api.handleAjaxError, + headers: rw.api.headers(accept), + success: function (data) { + rw.api.recordUrl(url, startTime); + deferred.resolve(data); + }, + error: function(e) { + deferred.reject(e); + } + }); + } + return deferred.promise(); + }, + + headers: function(accept, contentType) { + var h = { + Accept: accept + }; + if (rw.api.statsId != null) { + h['x-stats'] = rw.api.statsId; + } + if (contentType) { + h['Content-Type'] = contentType; + } + return h; + }, + + recordUrl:function(url, startTime) { + var elapsed = new Date().getTime() - startTime; + rw.api.tRequestsMs += elapsed; + rw.api.nRequests += 1; + rw.api.tRequestMaxMs = Math.max(rw.api.tRequestMaxMs, elapsed); + if (url in rw.api.nRequestsByUrl) { + var metric = rw.api.nRequestsByUrl[url]; + metric.url = url; + metric.n += 1; + metric.max = Math.max(metric.max, elapsed); + } else { + rw.api.nRequestsByUrl[url] = {url: url, n: 1, max: elapsed}; + } + }, + + put: function(url, data, contentType) { + return this.push('PUT', url, data, contentType); + }, + + post: function(url, data, contentType) { + return this.push('POST', url, data, contentType); + }, + + rpc: function(url, data, error) { + if(error === undefined){ + error = function(a,b,c){ + } + } + return this.push('POST', url, data, 'application/vnd.yang.data+json', error); + }, + + push: function(method, url, data, contentType, errorFn) { + var deferred = jQuery.Deferred(); + if (rw.api.offline) { + // eating offline put request + if(contentType == 'application/vnd.yang.data+json'){ + var payload = data; + rw.api.offline.getItem(url, function (data) { + deferred.resolve(data); + }); + } + deferred.resolve({}); + } else { + var startTime = new Date().getTime(); + jQuery.ajax(rw.api.server + url, { + type: method, + error: rw.api.handleAjaxError, + dataType: 'json', + headers: rw.api.headers('application/json', contentType), + data: JSON.stringify(data), + success: function (response) { + rw.api.recordUrl(url, startTime); + deferred.resolve(response); + }, + error: errorFn + }); + } + return deferred.promise(); + }, + + setOffline: function(name) { + if (name) { + rw.api.offline = new rw.db.Offline(name); + } else { + rw.api.offline = false; + } + }, + + // When passing things to ConfD ('/api/...') then '/' needs to be + // %252F(browser) --> %2F(Rest) --> / ConfD + encodeUrlParam: function(value) { + var once = rw.api.singleEncodeUrlParam(value); + return once.replace(/%/g, '%25'); + }, + + // UrlParam cannot have '/' and encoding it using %2F gets double-encoded in flask + singleEncodeUrlParam: function(value) { + return value.replace(/\//g, '%2F'); + } +}; + +rw.api.SocketSubscriber = function(url) { + this.url = url; + + this.id = ++rw.api.SocketSubscriber.uniqueId; + + this.subscribed = false; + this.offlineRateMs = 2000; +}, + +rw.api.SocketSubscriber.uniqueId = 0; + +rw.api.SocketSubscriber.prototype = { + + // does not support PUT/PORT with payloads requests yet + websubscribe : function(webUrl, onload, offline) { + this.subscribeMeta(onload, { + url: webUrl + }, offline); + }, + + subscribeMeta : function(onload, meta, offline) { + var self = this; + + if (this.subscribed) { + this.unsubscribe(); + } + + var m = meta || {}; + if (rw.api.offline) { + this.subscribeOffline(onload, m, offline); + } else { + this.subscribeOnline(onload, m); + } + }, + + subscribeOffline: function(onload, meta, offline) { + var self = this; + rw.api.json(meta.url).done(function(data) { + var _update = function() { + if (offline) { + offline(data); + } else { + onload(data); + } + }; + + this.offlineTimer = setInterval(_update, self.offlineRateMs); + }); + }, + + subscribeOnline: function(onload, meta) { + var self = this; + var _subscribe = function() { + meta.widgetId = self.id; + meta.accept = meta.accept || 'application/json'; + document.websocket().emit(self.url, meta); + self.subscribed = true; + }; + + var _register = function() { + document.websocket().on(self.url + '/' + self.id, function(dataString) { + var data = dataString; + + // auto convert to object to make backward compatible + if (meta.accept.match(/[+\/]json$/) && dataString != '') { + data = JSON.parse(dataString); + } + onload(data); + }); + _subscribe(); + }; + document.websocket().on('error',function(d){ + console.log('socket error', d) + }); + document.websocket().on('close',function(d){ + console.log('socket close', d) + }) + document.websocket().on('connect', _register); + + // it's possible this call is not nec. and will always be called + // as part of connect statement above + _register(); + }, + + unsubscribe : function() { + if (rw.api.offline) { + if (this.offlineTimer) { + clearInterval(this.offlineTimer); + this.offlineTimer = null; + } + } else { + var unsubscribe = { widgetId: this.id, enable: false }; + document.websocket().emit(this.url, unsubscribe); + this.subscribed = false; + } + } +}; + +rw.api.server = rw.search_params['api_server'] || ''; + +document.websocket = function() { + if ( ! this.socket ) { + //io.reconnection = true; + var wsServer = rw.api.server || 'http://' + document.domain + ':' + location.port; + var wsUrl = wsServer + '/rwapp'; + this.socket = io.connect(wsUrl, {reconnection:true}); + } + return this.socket; +}; + +rw.api.setOffline(rw.search_params['offline']); + +rw.vnf = { + ports: function(service) { + return _.flatten(jsonPath.eval(service, '$.connector[*].interface[*].port')); + }, + fabricPorts: function(service) { + return _.flatten(jsonPath.eval(service, '$.vm[*].fabric.port')); + } +}; + +rw.VcsVisitor = function(enter, leave) { + this.enter = enter; + this.leave = leave; +} + +rw.VcsVisitor.prototype = { + + visit: function(node, parent, i, listType) { + var hasChildren = this.enter(parent, node, i, listType); + if (hasChildren) { + switch (rw.vcs.nodeType(node)) { + case 'rwsector': + this.visitChildren(node, node.collection, 'collection'); + break; + case 'rwcolony': + this.visitChildren(node, node.collection, 'collection'); + this.visitChildren(node, node.vm, 'vm'); + break; + case 'rwcluster': + this.visitChildren(node, node.vm, 'vm'); + break; + case 'RWVM': + this.visitChildren(node, node.process, 'process'); + break; + case 'RWPROC': + this.visitChildren(node, node.tasklet, 'tasklet'); + break; + } + } + if (this.leave) { + this.leave(parent, node, obj, i, listType); + } + }, + + visitChildren : function(parent, children, listType) { + if (!children) { + return; + } + var i = 0; + var self = this; + _.each(children, function(child) { + self.visit.call(self, child, parent, i, listType); + i += 1; + }); + } +}; + +rw.vcs = { + + allVms : function() { + return _.flatten([this.jpath('$.collection[*].vm'), this.jpath('$.collection[*].collection[*].vm')], true); + }, + + vms: function(n) { + if (n == undefined || n === null || n === this) { + return this.allVms(); + } + switch (rw.vcs.nodeType(n)) { + case 'rwcolony': + return this.jpath('$.collection[*].vm[*]', n); + case 'rwcluster': + return this.jpath('$.vm[*]', n); + case 'RWVM': + return [n]; + default: + return null; + } + }, + + nodeType: function(node) { + if (node.component_type === 'RWCOLLECTION') { + return node.collection_info['collection-type']; + } + return node.component_type; + }, + + allClusters : function() { + return this.jpath('$.collection[*].collection'); + }, + + allColonies: function() { + return this.jpath('$.collection'); + }, + + allPorts:function(n) { + switch (rw.vcs.nodeType(n)) { + case 'rwsector': + return this.jpath('$.collection[*].collection[*].vm[*].port[*]', n); + case 'rwcolony': + return this.jpath('$.collection[*].vm[*].port[*]', n); + case 'rwcluster': + return this.jpath('$.vm[*].port[*]', n); + case 'RWVM': + return this.jpath('$.port[*]', n); + default: + return null; + } + }, + + allFabricPorts:function(n) { + switch (rw.vcs.nodeType(n)) { + case 'rwsector': + return this.jpath('$.collection[*].collection[*].vm[*].fabric.port[*]', n); + case 'rwcolony': + return this.jpath('$.collection[*].vm[*].fabric.port[*]', n); + case 'rwcluster': + return this.jpath('$.vm[*].fabric.port[*]', n); + case 'RWVM': + return this.jpath('$.fabric.port[*]', n); + default: + return null; + } + }, + + getChildren: function(n) { + switch (rw.vcs.nodeType(n)) { + case 'rwcolony': + return 'vm' in n ? _.union(n.collection, n.vm) : n.collection; + case 'rwcluster': + return n.vm; + case 'RWVM': + return n.process; + case 'RWPROC': + return n.tasklet; + } + return []; + }, + + jpath : function(jpath, n) { + return _.flatten(jsonPath.eval(n || this, jpath), true); + } +}; + +rw.trafgen = { + startedActual : null, // true or false once server-side state is loaded + startedPerceived : false, + ratePerceived : 25, + rateActual : null, // non-null once server-side state is loaded + packetSizePerceived : 1024, + packetSizeActual: null +}; + +rw.trafsim = { + startedActual : null, // true or false once server-side state is loaded + startedPerceived : false, + ratePerceived : 50000, + rateActual : null, // non-null once server-side state is loaded + maxRate: 200000 +}; + +rw.aggregateControlPanel = null; + +rw.theme = { + // purple-2 and blue-2 + txBps : 'hsla(212, 57%, 50%, 1)', + txBpsTranslucent : 'hsla(212, 57%, 50%, 0.7)', + rxBps : 'hsla(212, 57%, 50%, 1)', + rxBpsTranslucent : 'hsla(212, 57%, 50%, 0.7)', + txPps : 'hsla(260, 35%, 50%, 1)', + txPpsTranslucent : 'hsla(260, 35%, 50%, 0.7)', + rxPps : 'hsla(260, 35%, 50%, 1)', + rxPpsTranslucent : 'hsla(260, 35%, 50%, 0.7)', + memory : 'hsla(27, 98%, 57%, 1)', + memoryTranslucent : 'hsla(27, 98%, 57%, 0.7)', + cpu : 'hsla(123, 45%, 50%, 1)', + cpuTranslucent : 'hsla(123, 45%, 50%, 0.7)', + storage : 'hsla(180, 78%, 25%, 1)', + storageTranslucent : 'hsla(180, 78%, 25%, 0.7)' +}; + +rw.RateTimer = function(onChange, onStop) { + this.rate = 0; + this.onChange = onChange; + this.onStop = onStop; + this.testFrequency = 500; + this.testWaveLength = 5000; + this.timer = null; + return this; +}; + +rw.RateTimer.prototype = { + start: function() { + this.rate = 0; + if (!this.timer) { + this.n = 0; + var strategy = this.smoothRateStrategy.bind(this); + this.testingStartTime = new Date().getTime(); + this.timer = setInterval(strategy, this.testFrequency); + } + }, + + stop: function() { + if (this.timer) { + clearInterval(this.timer); + this.timer = null; + this.onStop(); + } + }, + + smoothRateStrategy: function() { + var x = (new Date().getTime() - this.testingStartTime) / this.testWaveLength; + this.rate = Math.round(100 * 0.5 * (1 - Math.cos(x))); + // in theory you could use wavelength and frequency to determine stop but this + // is guaranteed to stop at zero. + this.onChange(this.rate); + this.n += 1; + if (this.n >= 10 && this.rate < 1) { + this.stop(); + } + } +}; + +module.exports = rw; diff --git a/skyquake/framework/utils/utils.js b/skyquake/framework/utils/utils.js new file mode 100644 index 000000000..d08fe5f83 --- /dev/null +++ b/skyquake/framework/utils/utils.js @@ -0,0 +1,287 @@ +/* + * + * Copyright 2016 RIFT.IO Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +//Login needs to be refactored. Too many cross dependencies +var AuthActions = require('../widgets/login/loginAuthActions.js'); +var $ = require('jquery'); +var rw = require('utils/rw.js'); +var API_SERVER = rw.getSearchParams(window.location).api_server; +var NODE_PORT = 3000; +var SockJS = require('sockjs-client'); + +var Utils = {}; + +Utils.DescriptorModelMeta = null; + +var INACTIVITY_TIMEOUT = 600000; + +Utils.getInactivityTimeout = function() { + return new Promise(function(resolve, reject) { + $.ajax({ + url: '/inactivity-timeout', + type: 'GET', + success: function(data) { + resolve(data); + }, + error: function(error) { + console.log("There was an error getting the inactivity-timeout: ", error); + reject(error); + } + }).fail(function(xhr) { + console.log('There was an xhr error getting the inactivity-timeout', xhr); + reject(xhr); + }); + }); +}; + +Utils.isMultiplexerLoaded = function() { + if (window.multiplexer) { + return true; + } + return false; +}; + +Utils.setupMultiplexClient = function() { + var sockjs_url = '/multiplex'; + + var sockjs = new SockJS(sockjs_url); + + var loadChecker = function() { + try { + window.multiplexer = new WebSocketMultiplex(sockjs); + console.log('WebSocketMultiplex loaded'); + } catch (e) { + // caught an error, retry in someTime + console.log('WebSocketMultiplex not loaded yet. will try again in 1 second:', e); + setTimeout(function() { + loadChecker(); + }, 1000); + } + } + loadChecker(); +}; + +Utils.checkAndResolveSocketRequest = function(data, resolve, reject) { + const checker = () => { + if (!Utils.isMultiplexerLoaded()) { + setTimeout(() => { + checker(); + }, 500); + } else { + resolve(data.id); + } + }; + + checker(); +}; + +Utils.bootstrapApplication = function() { + var self = this; + return new Promise(function(resolve, reject) { + Promise.all([self.getInactivityTimeout()]).then(function(results) { + INACTIVITY_TIMEOUT = results[0]['inactivity-timeout']; + resolve(); + }, function(error) { + console.log("Error bootstrapping application ", error); + reject(); + }); + }); +}; + +Utils.getDescriptorModelMeta = function() { + return new Promise(function(resolve, reject) { + if (!Utils.DescriptorModelMeta) { + $.ajax({ + url: '/descriptor-model-meta?api_server=' + API_SERVER, + type: 'GET', + beforeSend: Utils.addAuthorizationStub, + success: function(data) { + Utils.DescriptorModelMeta = data; + Utils.DescriptorModelMetaLoaded = true; + resolve(data); + }, + error: function(error) { + console.log("There was an error getting the schema: ", error); + reject(error); + } + }).fail(function(xhr) { + console.log("There was an error getting the schema: ", xhr); + Utils.checkAuthentication(xhr.status); + }); + } else { + resolve(Utils.DescriptorModelMeta); + } + }) +} + +Utils.addAuthorizationStub = function(xhr) { + var Auth = window.sessionStorage.getItem("auth"); + xhr.setRequestHeader('Authorization', 'Basic ' + Auth); +}; + +Utils.getByteDataWithUnitPrefix = function(number, precision) { + var toPrecision = precision || 3; + if (number < Math.pow(10, 3)) { + return [number, 'B']; + } else if (number < Math.pow(10, 6)) { + return [(number / Math.pow(10, 3)).toPrecision(toPrecision), 'KB']; + } else if (number < Math.pow(10, 9)) { + return [(number / Math.pow(10, 6)).toPrecision(toPrecision), 'MB']; + } else if (number < Math.pow(10, 12)) { + return [(number / Math.pow(10, 9)).toPrecision(toPrecision), 'GB']; + } else if (number < Math.pow(10, 15)) { + return [(number / Math.pow(10, 12)).toPrecision(toPrecision), 'TB']; + } else if (number < Math.pow(10, 18)) { + return [(number / Math.pow(10, 15)).toPrecision(toPrecision), 'PB']; + } else if (number < Math.pow(10, 21)) { + return [(number / Math.pow(10, 18)).toPrecision(toPrecision), 'EB']; + } else if (number < Math.pow(10, 24)) { + return [(number / Math.pow(10, 21)).toPrecision(toPrecision), 'ZB']; + } else if (number < Math.pow(10, 27)) { + return [(number / Math.pow(10, 24)).toPrecision(toPrecision), 'ZB']; + } else { + return [(number / Math.pow(10, 27)).toPrecision(toPrecision), 'YB']; + } +} + +Utils.getPacketDataWithUnitPrefix = function(number, precision) { + var toPrecision = precision || 3; + if (number < Math.pow(10, 3)) { + return [number, 'P']; + } else if (number < Math.pow(10, 6)) { + return [(number / Math.pow(10, 3)).toPrecision(toPrecision), 'KP']; + } else if (number < Math.pow(10, 9)) { + return [(number / Math.pow(10, 6)).toPrecision(toPrecision), 'MP']; + } else if (number < Math.pow(10, 12)) { + return [(number / Math.pow(10, 9)).toPrecision(toPrecision), 'GP']; + } else if (number < Math.pow(10, 15)) { + return [(number / Math.pow(10, 12)).toPrecision(toPrecision), 'TP']; + } else if (number < Math.pow(10, 18)) { + return [(number / Math.pow(10, 15)).toPrecision(toPrecision), 'PP']; + } else if (number < Math.pow(10, 21)) { + return [(number / Math.pow(10, 18)).toPrecision(toPrecision), 'EP']; + } else if (number < Math.pow(10, 24)) { + return [(number / Math.pow(10, 21)).toPrecision(toPrecision), 'ZP']; + } else if (number < Math.pow(10, 27)) { + return [(number / Math.pow(10, 24)).toPrecision(toPrecision), 'ZP']; + } else { + return [(number / Math.pow(10, 27)).toPrecision(toPrecision), 'YP']; + } +} +Utils.loginHash = "#/login"; +Utils.setAuthentication = function(username, password, cb) { + var self = this; + var AuthBase64 = btoa(username + ":" + password); + window.sessionStorage.setItem("auth", AuthBase64); + self.detectInactivity(); + if (cb) { + cb(); + } +} +Utils.clearAuthentication = function(callback) { + var self = this; + window.sessionStorage.removeItem("auth"); + AuthActions.notAuthenticated(); + window.sessionStorage.setItem("locationRefHash", window.location.hash); + if (callback) { + callback(); + } else { + window.location.hash = Utils.loginHash; + } +} +Utils.isNotAuthenticated = function(windowLocation, callback) { + var self = this; + self.detectInactivity(); + if (!window.sessionStorage.getItem("auth")) { + Utils.clearAuthentication(); + } +} +Utils.isDetecting = false; +Utils.detectInactivity = function(callback, duration) { + var self = this; + if (!self.isDetecting) { + var cb = function() { + self.clearAuthentication(); + if (callback) { + callback(); + } + }; + var isInactive; + var timeout = duration || INACTIVITY_TIMEOUT; + var setInactive = function() { + isInactive = setTimeout(cb, timeout); + }; + var reset = function() { + clearTimeout(isInactive); + setInactive(); + } + setInactive(); + window.addEventListener('mousemove', reset); + window.addEventListener("keypress", reset); + self.isDetecting = true; + } +} +Utils.checkAuthentication = function(statusCode, cb) { + var self = this; + if (statusCode == 401) { + if (cb) { + cb(); + } + window.sessionStorage.removeItem("auth") + self.isNotAuthenticated(window.location) + return true; + } + return false; +} + +Utils.isAuthenticationCached = function() { + var self = this; + if (window.sessionStorage.getItem("auth")) { + return true; + } + return false; +} + +Utils.getHostNameFromURL = function(url) { + var match = url.match(/^(https?\:)\/\/(([^:\/?#]*)(?:\:([0-9]+))?)([^?#]*)(\?[^#]*|)(#.*|)$/); + return match && match[3]; +} + +Utils.webSocketProtocol = function() { + if (this.wsProto) { + return this.wsProto; + } else { + if (window.location.protocol == 'http:') { + this.wsProto = 'ws:' + } else { + this.wsProto = 'wss:' + } + } + return this.wsProto; +} + +Utils.arrayIntersperse = (arr, sep) => { + if (arr.length === 0) { + return []; + } + + return arr.slice(1).reduce((xs, x, i) => { + return xs.concat([sep, x]); + }, [arr[0]]); +} + +module.exports = Utils; diff --git a/skyquake/framework/widgets/JSONViewer/JSONViewer.js b/skyquake/framework/widgets/JSONViewer/JSONViewer.js new file mode 100644 index 000000000..2f8c0a82c --- /dev/null +++ b/skyquake/framework/widgets/JSONViewer/JSONViewer.js @@ -0,0 +1,140 @@ +/** + * Created by onvelocity on 2/2/16. + */ + +import React from 'react' +import Prism from 'prismjs' +import PureRenderMixin from 'react-addons-pure-render-mixin' + +import './JSONViewer.scss' + +const YAML = require('json2yaml'); + +const cssString = ` + html, body { + padding:0; + margin:0; + } + /* + copied from node_modules/prismjs/themes/prismjs.css + */ + :not(pre) > code[class*="language-"], + pre[class*="language-"] { + font-size: 12px; + padding:1rem; + /* + border: 1px solid rgba(220, 220, 220, 0.5); + border-radius: 4px; + */ + background-color: rgba(255, 255, 255, .25); + } + + /* Inline code */ + :not(pre) > code[class*="language-"] { + padding: .1em; + } + + .token.comment, + .token.prolog, + .token.doctype, + .token.cdata { + color: slategray; + } + + .token.punctuation { + color: #333; + } + + .namespace { + opacity: .7; + } + + .token.property, + .token.tag, + .token.boolean, + .token.number, + .token.constant, + .token.symbol, + .token.deleted { + color: #905; + } + + .token.selector, + .token.attr-name, + .token.string, + .token.char, + .token.builtin, + .token.inserted { + color: #df5000; + } + + .token.operator, + .token.entity, + .token.url, + .language-css .token.string, + .style .token.string { + color: #a67f59; + } + + .token.atrule, + .token.attr-value, + .token.keyword { + color: #07a; + } + + .token.function { + color: #DD4A68; + } + + .token.regex, + .token.important, + .token.variable { + color: #e90; + } + + .token.important, + .token.bold { + font-weight: bold; + } + .token.italic { + font-style: italic; + } + + .token.entity { + cursor: help; + } +`; + +const JSONViewer = React.createClass({ + mixins: [PureRenderMixin], + getInitialState: function () { + return {}; + }, + getDefaultProps: function () { + return {}; + }, + componentWillMount: function () { + }, + componentDidMount: function () { + }, + componentDidUpdate: function () { + }, + componentWillUnmount: function () { + }, + render() { + const text = YAML.stringify(this.props.json, undefined, 12).replace(/ /g, ' '); + const result = Prism.highlight(text, Prism.languages.javascript, 'javascript'); + return ( +
+ + +
+ ); + } +}); + +export default JSONViewer; diff --git a/skyquake/framework/widgets/JSONViewer/JSONViewer.scss b/skyquake/framework/widgets/JSONViewer/JSONViewer.scss new file mode 100644 index 000000000..08fcefdd5 --- /dev/null +++ b/skyquake/framework/widgets/JSONViewer/JSONViewer.scss @@ -0,0 +1,104 @@ +.JSONViewer { + /* + copied from node_modules/prismjs/themes/prismjs.css +*/ + :not(pre) > code[class*="language-"], + pre[class*="language-"] { + font-size: 12px; + /* border: 1px solid rgba(220, 220, 220, 0.5);*/ + border-radius: 4px; + background-color: rgba(255, 255, 255, .25); + overflow:auto; + } + + /* Inline code */ + :not(pre) > code[class*="language-"] { + padding: .1em; + } + + .token.comment, + .token.prolog, + .token.doctype, + .token.cdata { + color: slategray; + } + + .token.punctuation { + color: #333; + } + + .namespace { + opacity: .7; + } + + .token.property, + .token.tag, + .token.boolean, + .token.number, + .token.constant, + .token.symbol, + .token.deleted { + color: #905; + } + + .token.selector, + .token.attr-name, + .token.string, + .token.char, + .token.builtin, + .token.inserted { + color: #df5000; + } + + .token.operator, + .token.entity, + .token.url, + .language-css .token.string, + .style .token.string { + color: #a67f59; + background: none !important; + } + + .token.atrule, + .token.attr-value, + .token.keyword { + color: #07a; + } + + .token.function { + color: #DD4A68; + } + + .token.regex, + .token.important, + .token.variable { + color: #e90; + } + + .token.important, + .token.bold { + font-weight: bold; + } + .token.italic { + font-style: italic; + } + + .token.entity { + cursor: help; + } +} + +.composerPopout { + background-color: #f1f1f1; + h1 { + height: 51px; + line-height: 51px; + margin-left: 80px; + padding-left: 118px; + position: absolute; + left: 0; + text-transform: uppercase; + font-size: 1.625rem; + font-weight: normal; + } +} diff --git a/skyquake/framework/widgets/bullet/bullet.js b/skyquake/framework/widgets/bullet/bullet.js new file mode 100644 index 000000000..1411f0fbe --- /dev/null +++ b/skyquake/framework/widgets/bullet/bullet.js @@ -0,0 +1,230 @@ + +/* + * + * Copyright 2016 RIFT.IO Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +var React = require('react'); +var mixin = require('../mixins/ButtonEventListener.js') +//TODO: Many values are hard coded, need to make bullet "smarter" +/** + * A bullet graph component. + * It's props values and a brief description below + * + * vertical: If true, the bar rises and falls vertically + * value: The value displayed + * min: The minimum value expected + * max: The maximum value expected + * bulletColor: The fill color of the value section of the graph + * radius: Radius of graph corners. + * containerMarginX: Container's margin along x-axis + * containerMarginY: Container's margin along y-axis + * width: Width of bullet graph in pixels + * height: Height of bullet graph in pixels + * fontSize: Font size of text in pixels + * textMarginX: margin for text on x-axis + * textMarginY: Margin for text on y-axis + * units: units displayed. Also changes whether a max value is displayed. + **/ +module.exports = React.createClass({ + displayName: 'Bullet', + mixins:mixin.MIXINS, + propTypes: { + vertical: React.PropTypes.bool, + value: React.PropTypes.number, + min: React.PropTypes.number, + max: React.PropTypes.number, + bulletColor: React.PropTypes.string, + radius: React.PropTypes.number, + containerMarginX: React.PropTypes.number, + containerMarginY: React.PropTypes.number, + bulletMargin: React.PropTypes.number, + width: React.PropTypes.number, + height: React.PropTypes.number, + markerX: React.PropTypes.number, + fontSize: React.PropTypes.number, + textMarginX: React.PropTypes.number, + textMarginY: React.PropTypes.number, + units: React.PropTypes.string + }, + + getDefaultProps: function() { + return { + vertical: false, + value: 0, + min: 0, + max: 100, + bulletColor: "blue", + radius: 4, + containerMarginX: 0, + containerMarginY: 0, + bulletMargin: 0, + width: 512, + height: 64, + markerX: -100, + fontSize: 16, + textMarginX: 5, + textMarginY: 42, + units:'' + } + }, + + /** + * Defines default state. + * value: The value displayed + */ + getInitialState: function() { + return { + value: this.props.value + } + }, + + /** + * Called when the props are updated. + * Syncs the state value with the prop value. + * @params nextProps + */ + componentWillReceiveProps: function(nextProps) { + this.setState({value:nextProps.value || this.state.value}) + }, + + /** + * Before the component will mount, the width value is recalculed based on browser. + */ + componentWillMount: function() { + var isIE = false || !!document.documentMode; + var range = this.props.max - this.props.min; + var normalizedValue = (this.state.value - this.props.min) / range; + this.isPercent = (!this.props.units || this.props.units == '')? true:false + if (isIE) { + this.bulletWidth = String(Math.round(100 * normalizedValue)) + "%"; + } else { + this.bulletWidth = this.props.width - (2 * this.props.containerMarginX); + } + this.displayValue = (this.isPercent)? String(Math.round(normalizedValue * 100)) + "%" : this.props.value + + }, + + /** + * When the component mounts, this function sets the animation for smooth transition on value change. This only + * happens if the user's browser is not IE. + */ + componentDidMount: function() { + var isIE = false || !!document.documentMode; + var range = this.props.max - this.props.min; + var normalizedValue = (this.state.value - this.props.min) / range; + if (!isIE) { + var transform = "scaleX(" + normalizedValue + ")"; + var bullet = React.findDOMNode(this.refs.bulletRef); + bullet.style.transform = transform; + bullet.style["-webkit-transform"] = transform; + } + }, + + /** + * On update, reaplies transformation and width changes made in componentWillMount and componentDidMount + * @param nextProps + * @param nextState + * @returns {boolean} + */ + shouldComponentUpdate: function(nextProps, nextState) { + + if (String(this.state.value) == String(nextState.value)) { + return false; + } else { + var isIE = false || !!document.documentMode; + var range = this.props.max - this.props.min; + var normalizedValue = (nextState.value - this.props.min) / range; + + if (isIE) { + this.bulletWidth = String(Math.round(100 * normalizedValue)) + "%"; + } else { + this.bulletWidth = this.props.width - (2 * this.props.containerMarginX); + var transform = "scaleX(" + normalizedValue + ")"; + var bullet = React.findDOMNode(this.refs.bulletRef); + bullet.style.transform = transform; + bullet.style["-webkit-transform"] = transform; + } + this.displayValue = (this.isPercent)? String(Math.round(normalizedValue * 100)) + "%" : nextState.value + + return true; + } + }, + + + + /** + * Renders the Bullet Component + * @returns {*} + */ + render: function() { + + // The text element that displays the difference between the max value and the current value. + var maxDOMEl = (!this.isPercent)? null : React.createElement("text", { + //X needs better logic + // x: this.props.width - this.props.height * 1.25, + x: this.props.width - (130 - this.props.textMarginX), + y: this.props.textMarginY, + fontFamily: "Verdana", + fontSize: this.props.fontSize, + fill: "#ffffff"}, String(this.props.max - this.state.value) + "%"); + + + // The main bullet element. Made up of a static black rect in the background, + // a moving colored rect overlayed on top and a text element for the current value. + var bulletDOM = React.createElement("svg", { + width: "100%", + height: "100%", + viewBox: "0 0 512 " + this.props.height, + preserveAspectRatio: "none"}, + React.createElement("rect", {className: "bullet-container", + width: this.props.width - (2 * this.props.containerMarginX), + height: this.props.height - (2 * this.props.containerMarginY), + x: this.props.containerMarginX, + y: this.props.containerMarginY, + rx: this.props.radius, + ry: this.props.radius}, null), + React.createElement("svg", { + x: this.props.containerMarginX, + y: this.props.bulletMargin}, + React.createElement("rect", { + className: "bullet", + ref:"bulletRef", + fill: this.props.bulletColor, + width: this.bulletWidth, + height: this.props.height - (2 * this.props.bulletMargin), + rx: this.props.radius, + ry: this.props.radius + }) + ), + React.createElement("line", {className: "bullet-marker", + x1: this.props.markerX, + x2: this.props.markerX, + y1: this.props.markerY1, + y2: this.props.markerY2}), + React.createElement("text", { + x: this.props.textMarginX, + y: this.props.textMarginY, + "fontFamily": "Verdana", + "fontSize": this.props.fontSize, + fill: "#ffffff"}, this.displayValue + ), + maxDOMEl + ); + + + return bulletDOM; + } +}); diff --git a/skyquake/framework/widgets/button/button.scss b/skyquake/framework/widgets/button/button.scss new file mode 100644 index 000000000..c972e147a --- /dev/null +++ b/skyquake/framework/widgets/button/button.scss @@ -0,0 +1,270 @@ + +/* + * + * Copyright 2016 RIFT.IO Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +@import '../../style/_colors.scss'; +@import '../../style/variables.scss'; + +button{ + color: #000000; + display: inline-block; + font-size: 0.75rem; + padding: 0.75rem 3rem; + text-decoration: none; + text-transform: uppercase; + box-shadow: 2px 2px rgba(0, 0, 0, 0.15); + cursor: pointer; + margin:0 1rem; + &.light { + background-color: #ffffff; + border: 1px solid #cccccc; + border-top: 0; + &.small { + padding:0.25rem 1rem; + } + } + + &.dark { + background-color: #333333; + border: 1px solid #000000; + border-top: 0; + color: #ffffff; + &:hover,&:active { + background: #00acee; + color: #ffffff; + } + } + + +} + +/* IMPORTS +############################################################################ */ + + + + +/* BUTTON +############################################################################ */ + +.SqButton { + align-items: center; + border-style: solid; + border-radius: 3px; + border-width: 0px; + cursor: pointer; + display: inline-flex; + font-size: 1rem; + height: 50px; + justify-content: center; + margin: 0 10px; + outline: none; + padding: 0 15px; + text-transform: uppercase; + transition: $transition; + -moz-user-select: none; + -ms-user-select: none; + -webkit-touch-callout: none; + -webkit-user-select: none; + user-select: none; + + /* Button Content */ + &-content { + overflow: hidden; + text-overflow: ellipsis; + transition: $transition; + white-space: nowrap; + } + + /* Button Icon */ + &-icon { + transition: $transition; + } + + /* Spacing between content and icon when icon is on the right */ + &-icon + &-content { + margin-left: 10px; + } + + /* Spacing between content and icon when icon is on the left */ + &-content + &-icon { + margin-left: 10px; + } + + /* Focus */ + &:focus { + // box-shadow: $focus-shadow; + border: 1px solid red; + } + + /* SIZES + ############################################################################ */ + + &--large { + width: 250px; + } + + &--medium { + width: 175px; + } + + &--small { + width: 85px; + } + + /* NORMAL + ############################################################################ */ + + /* Base */ + &--normal { + background: $normalBackground; + border-color: darken($normalBackground, 10%); + + .SqButton-content { + color: $normalForeground; + } + + .SqButton-icon { + fill: $normalForeground; + } + } + + /* Hover */ + &--normal:hover { + background: $normalHoverBackground; + border-color: darken($normalHoverBackground, 10%); + + .SqButton-content { + color: $normalHoverForeground; + } + + .SqButton-icon { + fill: $normalHoverForeground; + } + } + + /* Active */ + &--normal:active { + background: $normalActiveBackground; + border-color: darken($normalActiveBackground, 10%); + + .SqButton-content { + color: $normalActiveForeground; + } + + .SqButton-icon { + fill: $normalActiveForeground; + } + } + + /* Disabled */ + &--normal.is-disabled { + cursor: default; + opacity: .55; + } + + &--normal:hover.is-disabled, + &--normal:active.is-disabled { + background: $normalBackground; + border-color: darken($normalBackground, 10%); + + .SqButton-content { + color: $normalForeground; + } + + .SqButton-icon { + fill: $normalForeground; + } + } + + + /* PRIMARY + ############################################################################ */ + + /* Base */ + &--primary { + background: $primaryBackground; + border-color: darken($primaryBackground, 10%); + + .SqButton-content { + color: $primaryForeground; + } + + .SqButton-icon { + fill: $primaryForeground; + } + } + + /* Hover */ + &--primary:hover { + background: $primaryHoverBackground; + border-color: darken($primaryHoverBackground, 10%); + + .SqButton-content { + color: $primaryHoverForeground; + } + + .SqButton-icon { + fill: $primaryHoverForeground; + } + } + + /* Active */ + &--primary:active { + background: $primaryActiveBackground; + border-color: darken($primaryActiveBackground, 10%); + + .SqButton-content { + color: $primaryActiveForeground; + } + + .SqButton-icon { + fill: $primaryActiveForeground; + } + } + + /* Disabled */ + &--primary.is-disabled { + cursor: default; + opacity: .55; + } + + &--primary:hover.is-disabled, + &--primary:active.is-disabled { + background: $primaryBackground; + border-color: darken($primaryBackground, 10%); + + .SqButton-content { + color: $primaryForeground; + } + + .SqButton-icon { + fill: $primaryForeground; + } + } + + +} + + + + + + + + + diff --git a/skyquake/framework/widgets/button/rw.button.js b/skyquake/framework/widgets/button/rw.button.js new file mode 100644 index 000000000..41730eb86 --- /dev/null +++ b/skyquake/framework/widgets/button/rw.button.js @@ -0,0 +1,261 @@ + +/* + * + * Copyright 2016 RIFT.IO Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +import './button.scss'; +import Loader from '../loading-indicator/loadingIndicator.jsx'; +var React = require('react'); +var ButtonEventListenerMixin = require('../mixins/ButtonEventListener.js'); + + +/** + * A generic button component. + * It's props values and a brief description below + * + * Label: The label of the button. What it displays at any given time. + * Icon: A url for an icon that will be displayed on the button. Leave blank if no icon required. + * Class: Css Classes applied to the element. + * sizeOfButton: The preset sizes for the button (small, default, large, xlarge, expand). + * minWidth: Minimum Width of the button. + * maxWidth: Maximum Width of the button. + **/ +module.exports = React.createClass({ + displayName: "Button", + mixins:[ButtonEventListenerMixin], + propTypes: { + label: React.PropTypes.string, + icon: React.PropTypes.array, + className: React.PropTypes.string, + //sizeOfButton: React.PropTypes.string, + minWidth: React.PropTypes.string, + maxWidth: React.PropTypes.string, + //isActive: React.PropTypes.bool, + //isFocused: React.PropTypes.bool, + //isHovered: React.PropTypes.bool, + isDisabled: React.PropTypes.bool + }, + + + /** + * Defines default state. + * sizeOfButton: See Prop type definitions. + * class: See Prop type definitions. + * label: See Prop type definitions. + * isActive: Boolean to indicate if button is active. + * isHovered: Boolean to indicate if the button is being hovered over. + * isFocused: Boolean to indicate if the button has been focused. + * isDisabled: Boolean to indicate if button has been disabled. + * @returns {{sizeOfButton: (*|string), class: *, isActive: boolean, isHovered: boolean, + * isFocused: boolean, isDisabled: (*|boolean), label: *}} + */ + getInitialState: function() { + return { + //sizeOfButton: this.props.size || '', //There is no Medium value in CSS, default size is the absence of a value + className: this.props.className || 'rw-button-primary', //Default value is 'rw-button-primary' which is the primary one + label: this.props.label, + isActive: false, + isHovered: false, + isFocused: false, + isLoading: this.props.isLoading || false, + isDisabled: this.props.isDisabled || false + } + }, + + + /** + * If any of the state variables have changed, the component should update. + * "nextProps" and "nextState" hold the state and property variables as they will be after the update. + * "this.props" and "this.state" hold the state and property variables as they were before the update. + * returns true if the state have changed. Otherwise returns false. + * @param nextProps + * @param nextState + * @returns {boolean} + */ + shouldComponentUpdate: function(nextProps, nextState) { + var currentStateString = this.state.label + this.state.isDisabled + this.state.isActive + this.state.isFocused + + this.state.isHovered + this.props.isLoading; + var nextStateString = nextState.label + nextState.isDisabled + nextState.isActive + nextState.isFocused + + nextState.isHovered + nextProps.isLoading; + + if (currentStateString == nextStateString) { + return false; + } + return true; + }, + + + /** + * Returns a string reflecting the current state of the button. + * If the button state "isDisabled" is true, returns a string "disabled". + * Otherwise returns a string containing a word for each state that has been set to true. + * (ie "active focused" if the states active and focused are true, but hovered is false). + * @returns {string} + */ + setButtonState: function() { + var ret = ""; + if (this.state.isDisabled) { + return "disabled"; + } + if (this.state.isActive) { + ret += "active "; + } + if (this.state.isHovered) { + ret += "hovered "; + } + if (this.state.isFocused) { + ret += "focused "; + } + return ret; + }, + + + + /** + * Track the width if set and write into markup using Style attribute + * Returns the minWidth and maxWidth prop in a string + * @returns {{}} + */ + setButtonWidth:function(){
 + var width = {};

 + + if (this.props.minWidth) {
 + width.minWidth = String(this.props.minWidth);
 + }
 + if (this.props.maxWidth) {
 + width.maxWidth = String(this.props.maxWidth);
 + }
 + + return width;
 + }, + + + + /** + * Apply the size of the button to the icon directly + * Returns a string indicating the icon size. + * @returns {string} + */ + /* + setIconSize:function(){ + + var iconClass = "rw-icon"; + + if(this.props.size){ + iconClass += "-" + this.props.size; + } + return iconClass; + }, + */ + + + + /** + * Builds the list of classes. + * Returns a string holding each class seperated by a space. + * @returns {string} + */ + /* + setButtonClass:function() { + var buttonClass = ""; + var buttonClassType = "rw-button-primary"; + // If the size is declared, add it in + if (this.state.sizeOfButton) { + buttonClass += this.state.sizeOfButton; + } + // + if (typeof(this.props.className) != "undefined") { + this.props.className.push("rw-button-secondary"); + + // Run through the array and check all the values + for (var i = 0; i < this.props.className.length; i++) { + + if (this.props.className[i].indexOf("secondary") > -1) { + buttonClassType = "rw-button-secondary"; // If the value of the array is equal to the string "secondary", add a predefined string + } else { + buttonClass += " " + this.props.className[i]; // Else just write the value of the array + } + } + } + buttonClass += " " + buttonClassType; //Add the button style type either primary or secondary + return buttonClass; + }, + */ + + /** + * Builds an array of html elements for the icons and returns them. + * @returns {Array} + */ + setIconElement: function() { + var button_icon = []; + + if (typeof(this.props.icon) != "undefined") { + for (var i = 0; i < this.props.icon.length; i++) { + button_icon.push(React.createElement('svg', { + className: "rw-button__icon", + key: i, + dangerouslySetInnerHTML: {__html: ''} //Using a React method to drop in a hack since React does not support xlink:href yet + })); + } + } + return button_icon; + }, + + /** + * Renders the Button Component + * Returns a react component that constructs the html that houses the button component. + * @returns {*} + */ + render: function() { + var button = null; + var button_style = this.setButtonWidth(); + var button_state = this.setButtonState(); + var button_class = this.state.className; + var button_icon = this.setIconElement(); + var display = this.state.label; + if (this.props.isLoading) { + display = React.createElement(Loader, {show: true, size: '1rem', color: this.props.loadingColor}); + } + + button = React.createElement("button", { + className: button_class, + "data-state": button_state, + style: button_style, + + // onClick: this.onClick, + onClick: this.props.onClick, + onMouseUp: this.onMouseUp, + onMouseDown: this.onMouseDown, + onMouseOver: this.onMouseOver, + onMouseEnter: this.onMouseEnter, + onMouseLeave: this.onMouseLeave, + onMouseOut: this.onMouseOut, + onTouchCancel: this.onTouchCancel, + onTouchEnd: this.onTouchEnd, + onTouchMove: this.onTouchMove, + onTouchStart: this.onTouchStart, + onKeyDown: this.onKeyDown, + onKeyPress: this.onKeyPress, + onKeyUp: this.onKeyUp, + onFocus: this.onFocus, + onBlur: this.onBlur + }, + button_icon, + React.createElement("span", {className: "rw-button__label"}, display) + ); + return button; + } +}); diff --git a/skyquake/framework/widgets/button/sq-button.jsx b/skyquake/framework/widgets/button/sq-button.jsx new file mode 100644 index 000000000..ae9312845 --- /dev/null +++ b/skyquake/framework/widgets/button/sq-button.jsx @@ -0,0 +1,55 @@ +import React from 'react'; + +import 'style/base.scss'; +import './button.scss'; + +const icons = { + check: require("style/icons/svg-sprite-navigation-symbol.svg") + "#ic_check_24px" +} + +export default class SqButton extends React.Component { + constructor(props) { + super(props); + } + render() { + let {icon, primary, size, disabled, label, ...props} = this.props; + let svgHTML = null; + let Class = "SqButton"; + if(icon) { + svgHTML = + ; + } + if(primary) { + Class += " SqButton--primary"; + } else { + Class += " SqButton--normal"; + } + if(size && ( + size == 'small' + || size == 'medium' + || size == 'large' + ) + ) { + Class += " SqButton--" + size; + } + if(disabled) { + Class += " is-disabled"; + } + return ( +
+
+ {svgHTML} +
{label}
+
+
+ ) + } +} + +SqButton.defaultProps = { + icon: false, + primary: false, + disabled: false, + size: false, // 'small', 'medium', 'large' + label: 'Submit' +} diff --git a/skyquake/framework/widgets/components.js b/skyquake/framework/widgets/components.js new file mode 100644 index 000000000..1fe49bc5f --- /dev/null +++ b/skyquake/framework/widgets/components.js @@ -0,0 +1,380 @@ + +/* + * + * Copyright 2016 RIFT.IO Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +var React = require('react'); +//var Histogram = require('react-d3-histogram') +export default { + //test: require('./test/test.js'), + button: require('./button/rw.button.js'), + React: React, +// Histogram: Histogram, + Multicomponent: require('./multicomponent/multicomponent.js'), + Mixins: require('./mixins/ButtonEventListener.js'), + Gauge: require('./gauge/gauge.js'), + Bullet: require('./bullet/bullet.js') +}; + +// require('../../assets/js/n3-line-chart.js'); +// var Gauge = require('../../assets/js/gauge-modified.js'); +// var bulletController = function($scope, $element) { +// this.$element = $element; +// this.vertical = false; +// this.value = 0; +// this.min = 0; +// this.max = 100; +// //this.range = this.max - this.min; +// //this.percent = (this.value - this.min) / this.range; +// this.displayValue = this.value; +// this.isPercent = (this.units == '')? true:false; +// this.bulletColor = "#6BB814"; +// this.fontsize = 28; +// this.radius = 4; +// this.containerMarginX = 0; +// this.containerMarginY = 0; +// this.textMarginX = 5; +// this.textMarginY = 42; +// this.bulletMargin = 0; +// this.width = 512; +// this.height = 64; +// this.markerX = -100; // puts it off screen unless set +// var self = this; +// if (this.isPercent) { +// this.displayValue + "%"; +// } +// $scope.$watch( +// function() { +// return self.value; +// }, +// function() { +// self.valueChanged(); +// } +// ); + +// } + +// bulletController.prototype = { + +// valueChanged: function() { +// var range = this.max - this.min; +// var normalizedValue = (this.value - this.min) / range; +// if (this.isPercent) { +// this.displayValue = String(Math.round(normalizedValue * 100)) + "%"; +// } else { +// this.displayValue = this.value; +// } +// // All versions of IE as of Jan 2015 does not support inline CSS transforms on SVG +// if (platform.name == 'IE') { +// this.bulletWidth = Math.round(100 * normalizedValue) + '%'; +// } else { +// this.bulletWidth = this.width - (2 * this.containerMarginX); +// var transform = 'scaleX(' + normalizedValue + ')'; +// var bullet = $(this.$element).find('.bullet2'); +// bullet.css('transform', transform); +// bullet.css('-webkit-transform', transform); +// } +// }, + +// markerChanged: function() { +// var range = this.max - this.min; +// var w = this.width - (2 * this.containerMarginX); +// this.markerX = this.containerMarginX + ((this.marker - this.min) / range ) * w; +// this.markerY1 = 7; +// this.markerY2 = this.width - 7; +// } +// } + +// angular.module('components', ['n3-line-chart']) +// .directive('rwBullet', function() { +// return { +// restrict : 'E', +// templateUrl: 'modules/views/rw.bullet.tmpl.html', +// bindToController: true, +// controllerAs: 'bullet', +// controller: bulletController, +// replace: true, +// scope: { +// min : '@?', +// max : '@?', +// value : '@', +// marker: '@?', +// units: '@?', +// bulletColor: '@?', +// label: '@?' +// } +// }; +// }) +// .directive('rwSlider', function() { +// var controller = function($scope, $element, $timeout) { +// // Q: is there a way to force attributes to be ints? +// $scope.min = $scope.min || "0"; +// $scope.max = $scope.max || "100"; +// $scope.step = $scope.step || "1"; +// $scope.height = $scope.height || "30"; +// $scope.orientation = $scope.orientation || 'horizontal'; +// $scope.tooltipInvert = $scope.tooltipInvert || false; +// $scope.percent = $scope.percent || false; +// $scope.kvalue = $scope.kvalue || false; +// $scope.direction = $scope.direction || "ltr"; +// $($element).noUiSlider({ +// start: parseInt($scope.value), +// step: parseInt($scope.step), +// orientation: $scope.orientation, +// range: { +// min: parseInt($scope.min), +// max: parseInt($scope.max) +// }, +// direction: $scope.direction +// }); +// //$(".no-Ui-target").Link('upper').to('-inline-
') +// var onSlide = function(e, value) { +// $timeout(function(){ +// $scope.value = value; +// }) + +// }; +// $($element).on({ +// change: onSlide, +// slide: onSlide, +// set: $scope.onSet({value: $scope.value}) +// }); +// var val = String(Math.round($scope.value)); +// if ($scope.percent) { +// val += "%" +// } else if ($scope.kvalue) { +// val += "k" +// } +// $($element).height($scope.height); +// if ($scope.tooltipInvert) { +// $($element).find('.noUi-handle').append("
" + val + "
"); +// } else { +// $($element).find('.noUi-handle').append("
" + val + "
"); +// } +// $scope.$watch('value', function(value) { +// var val = String(Math.round($scope.value)); +// if ($scope.percent) { +// val += "%" +// } else if($scope.kvalue) { +// val += "k" +// } +// $($element).val(value); +// $($element).find('.tooltip').html(val); +// if ($scope.tooltipInvert) { +// $($element).find('.tooltip').css('right', $($element).find('.tooltip').innerWidth() * -1); +// } else { +// $($element).find('.tooltip').css('left', $($element).find('.tooltip').innerWidth() * -1); +// } +// }); +// }; + +// return { +// restrict : 'E', +// template: '
', +// controller : controller, +// replace: true, +// scope: { +// min : '@', +// max : '@', +// width: '@', +// height: '@', +// step : '@', +// orientation : '@', +// tooltipInvert: '@', +// percent: '@', +// kvalue: '@?', +// onSet:'&?', +// direction: '@?', +// value:'=?' +// } +// }; +// }) +// .directive('rwGauge', function() { +// return { +// restrict: 'AE', +// template: '', +// replace: true, +// scope: { +// min: '@?', +// max: '@?', +// size: '@?', +// color: '@?', +// value: '@?', +// resize: '@?', +// isAggregate: '@?', +// units: '@?', +// valueFormat: '=?', +// width: '@?' +// }, +// bindToController: true, +// controllerAs: 'gauge', +// controller: function($scope, $element) { +// var self = this; +// this.gauge = null; +// this.min = this.min || 0; +// this.max = this.max || 100; +// this.nSteps = 14; +// this.size = this.size || 300; +// this.units = this.units || ''; +// $scope.width = this.width || 240; +// this.color = this.color || 'hsla(212, 57%, 50%, 1)'; +// if (!this.valueFormat) { +// if (this.max > 1000 || this.value) { +// self.valueFormat = { +// "int": 1, +// "dec": 0 +// }; +// } else { +// self.valueFormat = { +// "int": 1, +// "dec": 2 +// }; +// } +// } +// this.isAggregate = this.isAggregate || false; +// this.resize = this.resize || false; +// if (this.format == 'percent') { +// self.valueFormat = { +// "int": 3, +// "dec": 0 +// }; +// } +// $scope.$watch(function() { +// return self.max; +// }, function(n, o) { +// if(n !== o) { +// renderGauge(); +// } +// }); +// $scope.$watch(function() { +// return self.valueFormat; +// }, function(n, o) { +// if(n != 0) { +// renderGauge(); +// } +// }); +// $scope.$watch(function() { +// return self.value; +// }, function() { +// if (self.gauge) { +// // w/o rounding gauge will unexplainably thrash round. +// self.valueFormat = determineValueFormat(self.value); +// self.gauge.setValue(Math.ceil(self.value * 100) / 100); +// //self.gauge.setValue(Math.round(self.value)); +// } +// }); +// angular.element($element).ready(function() { +// console.log('rendering') +// renderGauge(); +// }) +// window.testme = renderGauge; +// function determineValueFormat(value) { + +// if (value > 999 || self.units == "%") { +// return { +// "int": 1, +// "dec": 0 +// } +// } + +// return { +// "int": 1, +// "dec": 2 +// } +// } +// function renderGauge(calcWidth) { +// if (self.max == self.min) { +// self.max = 14; +// } +// var range = self.max - self.min; +// var step = Math.round(range / self.nSteps); +// var majorTicks = []; +// for (var i = 0; i <= self.nSteps; i++) { +// majorTicks.push(self.min + (i * step)); +// }; +// var redLine = self.min + (range * 0.9); +// var config = { +// isAggregate: self.isAggregate, +// renderTo: angular.element($element)[0], +// width: calcWidth || self.size, +// height: calcWidth || self.size, +// glow: false, +// units: self.units, +// title: false, +// minValue: self.min, +// maxValue: self.max, +// majorTicks: majorTicks, +// valueFormat: determineValueFormat(self.value), +// minorTicks: 0, +// strokeTicks: false, +// highlights: [], +// colors: { +// plate: 'rgba(0,0,0,0)', +// majorTicks: 'rgba(15, 123, 182, .84)', +// minorTicks: '#ccc', +// title: 'rgba(50,50,50,100)', +// units: 'rgba(50,50,50,100)', +// numbers: '#fff', +// needle: { +// start: 'rgba(255, 255, 255, 1)', +// end: 'rgba(255, 255, 255, 1)' +// } +// } +// }; +// var min = config.minValue; +// var max = config.maxValue; +// var N = 1000; +// var increment = (max - min) / N; +// for (i = 0; i < N; i++) { +// var temp_color = 'rgb(0, 172, 238)'; +// if (i > 0.5714 * N && i <= 0.6428 * N) { +// temp_color = 'rgb(0,157,217)'; +// } else if (i >= 0.6428 * N && i < 0.7142 * N) { +// temp_color = 'rgb(0,142,196)'; +// } else if (i >= 0.7142 * N && i < 0.7857 * N) { +// temp_color = 'rgb(0,126,175)'; +// } else if (i >= 0.7857 * N && i < 0.8571 * N) { +// temp_color = 'rgb(0,122,154)'; +// } else if (i >= 0.8571 * N && i < 0.9285 * N) { +// temp_color = 'rgb(0,96,133)'; +// } else if (i >= 0.9285 * N) { +// temp_color = 'rgb(0,80,112)'; +// } +// config.highlights.push({ +// from: i * increment, +// to: increment * (i + 2), +// color: temp_color +// }) +// } +// var updateSize = _.debounce(function() { +// config.maxValue = self.max; +// var clientWidth = self.parentNode.parentNode.clientWidth / 2; +// var calcWidth = (300 > clientWidth) ? clientWidth : 300; +// self.gauge.config.width = self.gauge.config.height = calcWidth; +// self.renderGauge(calcWidth); +// }, 500); +// if (self.resize) $(window).resize(updateSize) +// if (self.gauge) { +// self.gauge.updateConfig(config); +// } else { +// self.gauge = new Gauge(config); +// self.gauge.draw(); +// } +// }; +// }, +// } +// }); diff --git a/skyquake/framework/widgets/dashboard_card/dashboardCardHeader.jsx b/skyquake/framework/widgets/dashboard_card/dashboardCardHeader.jsx new file mode 100644 index 000000000..e44c4cc2e --- /dev/null +++ b/skyquake/framework/widgets/dashboard_card/dashboardCardHeader.jsx @@ -0,0 +1,26 @@ + +/* + * + * Copyright 2016 RIFT.IO Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +import React from 'react'; + +class DashboardCardHeader extends React.Component { + constructor(props) { + super(props); + + } +} diff --git a/skyquake/framework/widgets/dashboard_card/dashboard_card.jsx b/skyquake/framework/widgets/dashboard_card/dashboard_card.jsx new file mode 100644 index 000000000..4904dd27c --- /dev/null +++ b/skyquake/framework/widgets/dashboard_card/dashboard_card.jsx @@ -0,0 +1,105 @@ + +/* + * + * Copyright 2016 RIFT.IO Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +import React from 'react'; +import './dashboard_card.scss'; + +var cardClass = 'dashboardCard'//classSet(this.props.class); + +var CardHeader = React.createClass({ + render() { + var cardClassHeader = cardClass + '_header'; + if(this.props.className) { + cardClassHeader += ' ' + this.props.className + '_header'; + } + return ( +
+

+ {this.props.title} +

+
+ ) + } +}); + CardHeader.defaultProps = { + title: ' Loading...' + } + + + +var dashboardCard = React.createClass({ + componentDidMount: function() { + + }, + getDefaultProps: function() { + return { + isHidden: false + } + }, + render() { + var cardClassWrapper = cardClass; + var cardClassContent = cardClass + '_content'; + var cardClassContentBody = cardClassContent + '-body'; + var hasHeader; + var cardClasses = []; + if(this.props.className) { + cardClasses = this.props.className.split(' '); + cardClasses.map(function(c, i) { + cardClassWrapper += ' ' + c; + cardClassContent += ' ' + c + '_content'; + cardClassContentBody += ' ' + c + '-body'; + }) + + } + let closeCard = null; + if (this.props.showHeader) { + hasHeader = ; + }; + if (this.props.closeCard) { + closeCard = this.props.closeCard; + } + return ( +
+ {closeCard} + + + {hasHeader} +
+
+ {this.props.children} +
+
+ + +
+ ) + } +}) + + +// class DashboardCard extends React.Component { +// constructor(props) { +// super(props) +// } +// render() { + +// } +// } + + +export default dashboardCard; diff --git a/skyquake/framework/widgets/dashboard_card/dashboard_card.scss b/skyquake/framework/widgets/dashboard_card/dashboard_card.scss new file mode 100644 index 000000000..9a33dc505 --- /dev/null +++ b/skyquake/framework/widgets/dashboard_card/dashboard_card.scss @@ -0,0 +1,57 @@ + +/* + * + * Copyright 2016 RIFT.IO Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +@import '../../style/_colors.scss'; +//Needs to be refactored +.dashboardCard { + &_wrapper { + display: flex; + flex-wrap: wrap; + padding: 0.5rem; + } + + background-color: $body-color; + position: relative; + // height: 750px; + width: 693px; + margin: 0.5rem 1rem; + align-content: flex-start; + flex-direction: column; + &_header { + display: flex; + align-items: center; + padding-left: 1rem; + background-color: $secondary-header; + text-transform: uppercase; + + h3 { + padding: 1.5rem; + } + } + &_content { + display: flex; + flex-direction: row; + flex: 1; + overflow:hidden; + &-body{ + display:flex; + flex-direction:column; + flex:1; + } + } +} diff --git a/skyquake/framework/widgets/filter/filter.jsx b/skyquake/framework/widgets/filter/filter.jsx new file mode 100644 index 000000000..0ee7af57c --- /dev/null +++ b/skyquake/framework/widgets/filter/filter.jsx @@ -0,0 +1,84 @@ + +/* + * + * Copyright 2016 RIFT.IO Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +var React = require('react'); +var Slider = require('react-slick'); +// require('../../components/gauge/gauge.js'); +// require('../../components/text-area/rw.text-area.js'); +// require('../../components/test/multicomponent.js'); +import button from '../../components/components.js' + +require('./carousel.css'); +var SimpleSlider = React.createClass({ + propTypes: { + component_list: React.PropTypes.array.isRequired, + slideno: React.PropTypes.number + }, + handleClick: function() { + this.setState({}); + }, + getInitialState: function() { + return { + } + + }, + shouldComponentUpdate: function(nextProps) { + + if (nextProps.slideno != this.props.slideno) { + return true; + } + return false; + }, + render: function () { + // var settings = { + // dots: true, + // infinite: false, + // speed: 500, + // slidesToShow: 1, + // slidesToScroll: 1, + // centerMode: true, + // initialSlide: this.props.slideno || 2 + // }; + var settings = { + dots: false, + infinite: false, + speed: 500, + slidesToShow: 1, + slidesToScroll: 1, + centerMode: true, + initialSlide: this.props.slideno || 0 + } + setTimeout(function() { + window.dispatchEvent(new Event('resize')); + }, 1000) + var list = []; + if (this.props.component_list !== undefined) { + for (var i = 0; i < this.props.component_list.length; i++) { + list.push(
{this.props.component_list[i]}
); + } + } + return ( +
+ + {list} + +
o + ); + } +}); +module.exports = SimpleSlider; diff --git a/skyquake/framework/widgets/form_controls/formControls.scss b/skyquake/framework/widgets/form_controls/formControls.scss new file mode 100644 index 000000000..4a8843501 --- /dev/null +++ b/skyquake/framework/widgets/form_controls/formControls.scss @@ -0,0 +1,60 @@ +/* + * + * Copyright 2016 RIFT.IO Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +@import 'style/_colors.scss'; + +.sqTextInput { + display: -ms-flexbox; + display: flex; + -ms-flex-direction: column; + flex-direction: column; + width: 100%; + margin-bottom:1rem; + -ms-flex-align: start; + align-items: flex-start; + -ms-flex-pack:start; + justify-content:flex-start; + span { + color:$darker-gray; + text-transform:uppercase; + } + input, .readonly, textarea { + height: 35px; + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.39), 0 -1px 1px #ffffff, 0 1px 0 #ffffff; + font-size: 1rem; + display: block; + background: white !important; + margin: 0; + margin-top: 0.25rem; + padding-left:0.25rem; + min-width:100%; + &[disabled] { + background:#ccc; + } + } + .readonly { + line-height: 35px; + box-shadow:none; + } + textarea { + -ms-flex-align: stretch; + -ms-grid-row-align: stretch; + align-items: stretch; + border:0px; + height: 100%; + } +} diff --git a/skyquake/framework/widgets/form_controls/selectOption.jsx b/skyquake/framework/widgets/form_controls/selectOption.jsx new file mode 100644 index 000000000..41a8b1353 --- /dev/null +++ b/skyquake/framework/widgets/form_controls/selectOption.jsx @@ -0,0 +1,59 @@ +/* + * + * Copyright 2016 RIFT.IO Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +import React from 'react'; + +export default class SelectOption extends React.Component { + constructor(props){ + super(props); + this.state = {}; + } + handleOnChange = (e) => { + this.props.onChange(e); + } + render() { + let html; + let defaultValue = this.props.defaultValue; + let options = this.props.options.map(function(op, i) { + let value = JSON.stringify(op.value); + return + }); + if (this.props.initial) { + options.unshift(); + } + html = ( + + ); + return html; + } +} +SelectOption.defaultProps = { + options: [], + onChange: function(e) { + console.dir(e) + }, + defaultValue: false, + initial: false, + label: null +} diff --git a/skyquake/framework/widgets/form_controls/textInput.jsx b/skyquake/framework/widgets/form_controls/textInput.jsx new file mode 100644 index 000000000..03dfa9c8a --- /dev/null +++ b/skyquake/framework/widgets/form_controls/textInput.jsx @@ -0,0 +1,75 @@ +/* + * + * Copyright 2016 RIFT.IO Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +import './formControls.scss'; + +import React, {Component} from 'react'; + +export default class TextInput extends Component { + render() { + let {label, onChange, value, defaultValue, ...props} = this.props; + let inputProperties = { + value: value, + onChange: onChange + } + let isRequired; + let inputType; + if(this.props.required) { + isRequired = * + } + if (defaultValue) { + inputProperties.defaultValue = defaultValue; + } + if (props.pattern) { + inputProperties.pattern = props.pattern; + } + if (value == undefined) { + value = defaultValue; + } + switch(props.type) { + case 'textarea': + inputType = \";\n\tsupport.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue;\n})();\nvar strundefined = typeof undefined;\n\n\n\nsupport.focusinBubbles = \"onfocusin\" in window;\n\n\nvar\n\trkeyEvent = /^key/,\n\trmouseEvent = /^(?:mouse|pointer|contextmenu)|click/,\n\trfocusMorph = /^(?:focusinfocus|focusoutblur)$/,\n\trtypenamespace = /^([^.]*)(?:\\.(.+)|)$/;\n\nfunction returnTrue() {\n\treturn true;\n}\n\nfunction returnFalse() {\n\treturn false;\n}\n\nfunction safeActiveElement() {\n\ttry {\n\t\treturn document.activeElement;\n\t} catch ( err ) { }\n}\n\n/*\n * Helper functions for managing events -- not part of the public interface.\n * Props to Dean Edwards' addEvent library for many of the ideas.\n */\njQuery.event = {\n\n\tglobal: {},\n\n\tadd: function( elem, types, handler, data, selector ) {\n\n\t\tvar handleObjIn, eventHandle, tmp,\n\t\t\tevents, t, handleObj,\n\t\t\tspecial, handlers, type, namespaces, origType,\n\t\t\telemData = data_priv.get( elem );\n\n\t\t// Don't attach events to noData or text/comment nodes (but allow plain objects)\n\t\tif ( !elemData ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Caller can pass in an object of custom data in lieu of the handler\n\t\tif ( handler.handler ) {\n\t\t\thandleObjIn = handler;\n\t\t\thandler = handleObjIn.handler;\n\t\t\tselector = handleObjIn.selector;\n\t\t}\n\n\t\t// Make sure that the handler has a unique ID, used to find/remove it later\n\t\tif ( !handler.guid ) {\n\t\t\thandler.guid = jQuery.guid++;\n\t\t}\n\n\t\t// Init the element's event structure and main handler, if this is the first\n\t\tif ( !(events = elemData.events) ) {\n\t\t\tevents = elemData.events = {};\n\t\t}\n\t\tif ( !(eventHandle = elemData.handle) ) {\n\t\t\teventHandle = elemData.handle = function( e ) {\n\t\t\t\t// Discard the second event of a jQuery.event.trigger() and\n\t\t\t\t// when an event is called after a page has unloaded\n\t\t\t\treturn typeof jQuery !== strundefined && jQuery.event.triggered !== e.type ?\n\t\t\t\t\tjQuery.event.dispatch.apply( elem, arguments ) : undefined;\n\t\t\t};\n\t\t}\n\n\t\t// Handle multiple events separated by a space\n\t\ttypes = ( types || \"\" ).match( rnotwhite ) || [ \"\" ];\n\t\tt = types.length;\n\t\twhile ( t-- ) {\n\t\t\ttmp = rtypenamespace.exec( types[t] ) || [];\n\t\t\ttype = origType = tmp[1];\n\t\t\tnamespaces = ( tmp[2] || \"\" ).split( \".\" ).sort();\n\n\t\t\t// There *must* be a type, no attaching namespace-only handlers\n\t\t\tif ( !type ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// If event changes its type, use the special event handlers for the changed type\n\t\t\tspecial = jQuery.event.special[ type ] || {};\n\n\t\t\t// If selector defined, determine special event api type, otherwise given type\n\t\t\ttype = ( selector ? special.delegateType : special.bindType ) || type;\n\n\t\t\t// Update special based on newly reset type\n\t\t\tspecial = jQuery.event.special[ type ] || {};\n\n\t\t\t// handleObj is passed to all event handlers\n\t\t\thandleObj = jQuery.extend({\n\t\t\t\ttype: type,\n\t\t\t\torigType: origType,\n\t\t\t\tdata: data,\n\t\t\t\thandler: handler,\n\t\t\t\tguid: handler.guid,\n\t\t\t\tselector: selector,\n\t\t\t\tneedsContext: selector && jQuery.expr.match.needsContext.test( selector ),\n\t\t\t\tnamespace: namespaces.join(\".\")\n\t\t\t}, handleObjIn );\n\n\t\t\t// Init the event handler queue if we're the first\n\t\t\tif ( !(handlers = events[ type ]) ) {\n\t\t\t\thandlers = events[ type ] = [];\n\t\t\t\thandlers.delegateCount = 0;\n\n\t\t\t\t// Only use addEventListener if the special events handler returns false\n\t\t\t\tif ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {\n\t\t\t\t\tif ( elem.addEventListener ) {\n\t\t\t\t\t\telem.addEventListener( type, eventHandle, false );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ( special.add ) {\n\t\t\t\tspecial.add.call( elem, handleObj );\n\n\t\t\t\tif ( !handleObj.handler.guid ) {\n\t\t\t\t\thandleObj.handler.guid = handler.guid;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Add to the element's handler list, delegates in front\n\t\t\tif ( selector ) {\n\t\t\t\thandlers.splice( handlers.delegateCount++, 0, handleObj );\n\t\t\t} else {\n\t\t\t\thandlers.push( handleObj );\n\t\t\t}\n\n\t\t\t// Keep track of which events have ever been used, for event optimization\n\t\t\tjQuery.event.global[ type ] = true;\n\t\t}\n\n\t},\n\n\t// Detach an event or set of events from an element\n\tremove: function( elem, types, handler, selector, mappedTypes ) {\n\n\t\tvar j, origCount, tmp,\n\t\t\tevents, t, handleObj,\n\t\t\tspecial, handlers, type, namespaces, origType,\n\t\t\telemData = data_priv.hasData( elem ) && data_priv.get( elem );\n\n\t\tif ( !elemData || !(events = elemData.events) ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Once for each type.namespace in types; type may be omitted\n\t\ttypes = ( types || \"\" ).match( rnotwhite ) || [ \"\" ];\n\t\tt = types.length;\n\t\twhile ( t-- ) {\n\t\t\ttmp = rtypenamespace.exec( types[t] ) || [];\n\t\t\ttype = origType = tmp[1];\n\t\t\tnamespaces = ( tmp[2] || \"\" ).split( \".\" ).sort();\n\n\t\t\t// Unbind all events (on this namespace, if provided) for the element\n\t\t\tif ( !type ) {\n\t\t\t\tfor ( type in events ) {\n\t\t\t\t\tjQuery.event.remove( elem, type + types[ t ], handler, selector, true );\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tspecial = jQuery.event.special[ type ] || {};\n\t\t\ttype = ( selector ? special.delegateType : special.bindType ) || type;\n\t\t\thandlers = events[ type ] || [];\n\t\t\ttmp = tmp[2] && new RegExp( \"(^|\\\\.)\" + namespaces.join(\"\\\\.(?:.*\\\\.|)\") + \"(\\\\.|$)\" );\n\n\t\t\t// Remove matching events\n\t\t\torigCount = j = handlers.length;\n\t\t\twhile ( j-- ) {\n\t\t\t\thandleObj = handlers[ j ];\n\n\t\t\t\tif ( ( mappedTypes || origType === handleObj.origType ) &&\n\t\t\t\t\t( !handler || handler.guid === handleObj.guid ) &&\n\t\t\t\t\t( !tmp || tmp.test( handleObj.namespace ) ) &&\n\t\t\t\t\t( !selector || selector === handleObj.selector || selector === \"**\" && handleObj.selector ) ) {\n\t\t\t\t\thandlers.splice( j, 1 );\n\n\t\t\t\t\tif ( handleObj.selector ) {\n\t\t\t\t\t\thandlers.delegateCount--;\n\t\t\t\t\t}\n\t\t\t\t\tif ( special.remove ) {\n\t\t\t\t\t\tspecial.remove.call( elem, handleObj );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Remove generic event handler if we removed something and no more handlers exist\n\t\t\t// (avoids potential for endless recursion during removal of special event handlers)\n\t\t\tif ( origCount && !handlers.length ) {\n\t\t\t\tif ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) {\n\t\t\t\t\tjQuery.removeEvent( elem, type, elemData.handle );\n\t\t\t\t}\n\n\t\t\t\tdelete events[ type ];\n\t\t\t}\n\t\t}\n\n\t\t// Remove the expando if it's no longer used\n\t\tif ( jQuery.isEmptyObject( events ) ) {\n\t\t\tdelete elemData.handle;\n\t\t\tdata_priv.remove( elem, \"events\" );\n\t\t}\n\t},\n\n\ttrigger: function( event, data, elem, onlyHandlers ) {\n\n\t\tvar i, cur, tmp, bubbleType, ontype, handle, special,\n\t\t\teventPath = [ elem || document ],\n\t\t\ttype = hasOwn.call( event, \"type\" ) ? event.type : event,\n\t\t\tnamespaces = hasOwn.call( event, \"namespace\" ) ? event.namespace.split(\".\") : [];\n\n\t\tcur = tmp = elem = elem || document;\n\n\t\t// Don't do events on text and comment nodes\n\t\tif ( elem.nodeType === 3 || elem.nodeType === 8 ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// focus/blur morphs to focusin/out; ensure we're not firing them right now\n\t\tif ( rfocusMorph.test( type + jQuery.event.triggered ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\tif ( type.indexOf(\".\") >= 0 ) {\n\t\t\t// Namespaced trigger; create a regexp to match event type in handle()\n\t\t\tnamespaces = type.split(\".\");\n\t\t\ttype = namespaces.shift();\n\t\t\tnamespaces.sort();\n\t\t}\n\t\tontype = type.indexOf(\":\") < 0 && \"on\" + type;\n\n\t\t// Caller can pass in a jQuery.Event object, Object, or just an event type string\n\t\tevent = event[ jQuery.expando ] ?\n\t\t\tevent :\n\t\t\tnew jQuery.Event( type, typeof event === \"object\" && event );\n\n\t\t// Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true)\n\t\tevent.isTrigger = onlyHandlers ? 2 : 3;\n\t\tevent.namespace = namespaces.join(\".\");\n\t\tevent.namespace_re = event.namespace ?\n\t\t\tnew RegExp( \"(^|\\\\.)\" + namespaces.join(\"\\\\.(?:.*\\\\.|)\") + \"(\\\\.|$)\" ) :\n\t\t\tnull;\n\n\t\t// Clean up the event in case it is being reused\n\t\tevent.result = undefined;\n\t\tif ( !event.target ) {\n\t\t\tevent.target = elem;\n\t\t}\n\n\t\t// Clone any incoming data and prepend the event, creating the handler arg list\n\t\tdata = data == null ?\n\t\t\t[ event ] :\n\t\t\tjQuery.makeArray( data, [ event ] );\n\n\t\t// Allow special events to draw outside the lines\n\t\tspecial = jQuery.event.special[ type ] || {};\n\t\tif ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Determine event propagation path in advance, per W3C events spec (#9951)\n\t\t// Bubble up to document, then to window; watch for a global ownerDocument var (#9724)\n\t\tif ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) {\n\n\t\t\tbubbleType = special.delegateType || type;\n\t\t\tif ( !rfocusMorph.test( bubbleType + type ) ) {\n\t\t\t\tcur = cur.parentNode;\n\t\t\t}\n\t\t\tfor ( ; cur; cur = cur.parentNode ) {\n\t\t\t\teventPath.push( cur );\n\t\t\t\ttmp = cur;\n\t\t\t}\n\n\t\t\t// Only add window if we got to document (e.g., not plain obj or detached DOM)\n\t\t\tif ( tmp === (elem.ownerDocument || document) ) {\n\t\t\t\teventPath.push( tmp.defaultView || tmp.parentWindow || window );\n\t\t\t}\n\t\t}\n\n\t\t// Fire handlers on the event path\n\t\ti = 0;\n\t\twhile ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) {\n\n\t\t\tevent.type = i > 1 ?\n\t\t\t\tbubbleType :\n\t\t\t\tspecial.bindType || type;\n\n\t\t\t// jQuery handler\n\t\t\thandle = ( data_priv.get( cur, \"events\" ) || {} )[ event.type ] && data_priv.get( cur, \"handle\" );\n\t\t\tif ( handle ) {\n\t\t\t\thandle.apply( cur, data );\n\t\t\t}\n\n\t\t\t// Native handler\n\t\t\thandle = ontype && cur[ ontype ];\n\t\t\tif ( handle && handle.apply && jQuery.acceptData( cur ) ) {\n\t\t\t\tevent.result = handle.apply( cur, data );\n\t\t\t\tif ( event.result === false ) {\n\t\t\t\t\tevent.preventDefault();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tevent.type = type;\n\n\t\t// If nobody prevented the default action, do it now\n\t\tif ( !onlyHandlers && !event.isDefaultPrevented() ) {\n\n\t\t\tif ( (!special._default || special._default.apply( eventPath.pop(), data ) === false) &&\n\t\t\t\tjQuery.acceptData( elem ) ) {\n\n\t\t\t\t// Call a native DOM method on the target with the same name name as the event.\n\t\t\t\t// Don't do default actions on window, that's where global variables be (#6170)\n\t\t\t\tif ( ontype && jQuery.isFunction( elem[ type ] ) && !jQuery.isWindow( elem ) ) {\n\n\t\t\t\t\t// Don't re-trigger an onFOO event when we call its FOO() method\n\t\t\t\t\ttmp = elem[ ontype ];\n\n\t\t\t\t\tif ( tmp ) {\n\t\t\t\t\t\telem[ ontype ] = null;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Prevent re-triggering of the same event, since we already bubbled it above\n\t\t\t\t\tjQuery.event.triggered = type;\n\t\t\t\t\telem[ type ]();\n\t\t\t\t\tjQuery.event.triggered = undefined;\n\n\t\t\t\t\tif ( tmp ) {\n\t\t\t\t\t\telem[ ontype ] = tmp;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn event.result;\n\t},\n\n\tdispatch: function( event ) {\n\n\t\t// Make a writable jQuery.Event from the native event object\n\t\tevent = jQuery.event.fix( event );\n\n\t\tvar i, j, ret, matched, handleObj,\n\t\t\thandlerQueue = [],\n\t\t\targs = slice.call( arguments ),\n\t\t\thandlers = ( data_priv.get( this, \"events\" ) || {} )[ event.type ] || [],\n\t\t\tspecial = jQuery.event.special[ event.type ] || {};\n\n\t\t// Use the fix-ed jQuery.Event rather than the (read-only) native event\n\t\targs[0] = event;\n\t\tevent.delegateTarget = this;\n\n\t\t// Call the preDispatch hook for the mapped type, and let it bail if desired\n\t\tif ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Determine handlers\n\t\thandlerQueue = jQuery.event.handlers.call( this, event, handlers );\n\n\t\t// Run delegates first; they may want to stop propagation beneath us\n\t\ti = 0;\n\t\twhile ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) {\n\t\t\tevent.currentTarget = matched.elem;\n\n\t\t\tj = 0;\n\t\t\twhile ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) {\n\n\t\t\t\t// Triggered event must either 1) have no namespace, or 2) have namespace(s)\n\t\t\t\t// a subset or equal to those in the bound event (both can have no namespace).\n\t\t\t\tif ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) {\n\n\t\t\t\t\tevent.handleObj = handleObj;\n\t\t\t\t\tevent.data = handleObj.data;\n\n\t\t\t\t\tret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler )\n\t\t\t\t\t\t\t.apply( matched.elem, args );\n\n\t\t\t\t\tif ( ret !== undefined ) {\n\t\t\t\t\t\tif ( (event.result = ret) === false ) {\n\t\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\t\tevent.stopPropagation();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Call the postDispatch hook for the mapped type\n\t\tif ( special.postDispatch ) {\n\t\t\tspecial.postDispatch.call( this, event );\n\t\t}\n\n\t\treturn event.result;\n\t},\n\n\thandlers: function( event, handlers ) {\n\t\tvar i, matches, sel, handleObj,\n\t\t\thandlerQueue = [],\n\t\t\tdelegateCount = handlers.delegateCount,\n\t\t\tcur = event.target;\n\n\t\t// Find delegate handlers\n\t\t// Black-hole SVG instance trees (#13180)\n\t\t// Avoid non-left-click bubbling in Firefox (#3861)\n\t\tif ( delegateCount && cur.nodeType && (!event.button || event.type !== \"click\") ) {\n\n\t\t\tfor ( ; cur !== this; cur = cur.parentNode || this ) {\n\n\t\t\t\t// Don't process clicks on disabled elements (#6911, #8165, #11382, #11764)\n\t\t\t\tif ( cur.disabled !== true || event.type !== \"click\" ) {\n\t\t\t\t\tmatches = [];\n\t\t\t\t\tfor ( i = 0; i < delegateCount; i++ ) {\n\t\t\t\t\t\thandleObj = handlers[ i ];\n\n\t\t\t\t\t\t// Don't conflict with Object.prototype properties (#13203)\n\t\t\t\t\t\tsel = handleObj.selector + \" \";\n\n\t\t\t\t\t\tif ( matches[ sel ] === undefined ) {\n\t\t\t\t\t\t\tmatches[ sel ] = handleObj.needsContext ?\n\t\t\t\t\t\t\t\tjQuery( sel, this ).index( cur ) >= 0 :\n\t\t\t\t\t\t\t\tjQuery.find( sel, this, null, [ cur ] ).length;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif ( matches[ sel ] ) {\n\t\t\t\t\t\t\tmatches.push( handleObj );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif ( matches.length ) {\n\t\t\t\t\t\thandlerQueue.push({ elem: cur, handlers: matches });\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Add the remaining (directly-bound) handlers\n\t\tif ( delegateCount < handlers.length ) {\n\t\t\thandlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) });\n\t\t}\n\n\t\treturn handlerQueue;\n\t},\n\n\t// Includes some event props shared by KeyEvent and MouseEvent\n\tprops: \"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which\".split(\" \"),\n\n\tfixHooks: {},\n\n\tkeyHooks: {\n\t\tprops: \"char charCode key keyCode\".split(\" \"),\n\t\tfilter: function( event, original ) {\n\n\t\t\t// Add which for key events\n\t\t\tif ( event.which == null ) {\n\t\t\t\tevent.which = original.charCode != null ? original.charCode : original.keyCode;\n\t\t\t}\n\n\t\t\treturn event;\n\t\t}\n\t},\n\n\tmouseHooks: {\n\t\tprops: \"button buttons clientX clientY offsetX offsetY pageX pageY screenX screenY toElement\".split(\" \"),\n\t\tfilter: function( event, original ) {\n\t\t\tvar eventDoc, doc, body,\n\t\t\t\tbutton = original.button;\n\n\t\t\t// Calculate pageX/Y if missing and clientX/Y available\n\t\t\tif ( event.pageX == null && original.clientX != null ) {\n\t\t\t\teventDoc = event.target.ownerDocument || document;\n\t\t\t\tdoc = eventDoc.documentElement;\n\t\t\t\tbody = eventDoc.body;\n\n\t\t\t\tevent.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 );\n\t\t\t\tevent.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 );\n\t\t\t}\n\n\t\t\t// Add which for click: 1 === left; 2 === middle; 3 === right\n\t\t\t// Note: button is not normalized, so don't use it\n\t\t\tif ( !event.which && button !== undefined ) {\n\t\t\t\tevent.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) );\n\t\t\t}\n\n\t\t\treturn event;\n\t\t}\n\t},\n\n\tfix: function( event ) {\n\t\tif ( event[ jQuery.expando ] ) {\n\t\t\treturn event;\n\t\t}\n\n\t\t// Create a writable copy of the event object and normalize some properties\n\t\tvar i, prop, copy,\n\t\t\ttype = event.type,\n\t\t\toriginalEvent = event,\n\t\t\tfixHook = this.fixHooks[ type ];\n\n\t\tif ( !fixHook ) {\n\t\t\tthis.fixHooks[ type ] = fixHook =\n\t\t\t\trmouseEvent.test( type ) ? this.mouseHooks :\n\t\t\t\trkeyEvent.test( type ) ? this.keyHooks :\n\t\t\t\t{};\n\t\t}\n\t\tcopy = fixHook.props ? this.props.concat( fixHook.props ) : this.props;\n\n\t\tevent = new jQuery.Event( originalEvent );\n\n\t\ti = copy.length;\n\t\twhile ( i-- ) {\n\t\t\tprop = copy[ i ];\n\t\t\tevent[ prop ] = originalEvent[ prop ];\n\t\t}\n\n\t\t// Support: Cordova 2.5 (WebKit) (#13255)\n\t\t// All events should have a target; Cordova deviceready doesn't\n\t\tif ( !event.target ) {\n\t\t\tevent.target = document;\n\t\t}\n\n\t\t// Support: Safari 6.0+, Chrome<28\n\t\t// Target should not be a text node (#504, #13143)\n\t\tif ( event.target.nodeType === 3 ) {\n\t\t\tevent.target = event.target.parentNode;\n\t\t}\n\n\t\treturn fixHook.filter ? fixHook.filter( event, originalEvent ) : event;\n\t},\n\n\tspecial: {\n\t\tload: {\n\t\t\t// Prevent triggered image.load events from bubbling to window.load\n\t\t\tnoBubble: true\n\t\t},\n\t\tfocus: {\n\t\t\t// Fire native event if possible so blur/focus sequence is correct\n\t\t\ttrigger: function() {\n\t\t\t\tif ( this !== safeActiveElement() && this.focus ) {\n\t\t\t\t\tthis.focus();\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t},\n\t\t\tdelegateType: \"focusin\"\n\t\t},\n\t\tblur: {\n\t\t\ttrigger: function() {\n\t\t\t\tif ( this === safeActiveElement() && this.blur ) {\n\t\t\t\t\tthis.blur();\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t},\n\t\t\tdelegateType: \"focusout\"\n\t\t},\n\t\tclick: {\n\t\t\t// For checkbox, fire native event so checked state will be right\n\t\t\ttrigger: function() {\n\t\t\t\tif ( this.type === \"checkbox\" && this.click && jQuery.nodeName( this, \"input\" ) ) {\n\t\t\t\t\tthis.click();\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t},\n\n\t\t\t// For cross-browser consistency, don't fire native .click() on links\n\t\t\t_default: function( event ) {\n\t\t\t\treturn jQuery.nodeName( event.target, \"a\" );\n\t\t\t}\n\t\t},\n\n\t\tbeforeunload: {\n\t\t\tpostDispatch: function( event ) {\n\n\t\t\t\t// Support: Firefox 20+\n\t\t\t\t// Firefox doesn't alert if the returnValue field is not set.\n\t\t\t\tif ( event.result !== undefined && event.originalEvent ) {\n\t\t\t\t\tevent.originalEvent.returnValue = event.result;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t},\n\n\tsimulate: function( type, elem, event, bubble ) {\n\t\t// Piggyback on a donor event to simulate a different one.\n\t\t// Fake originalEvent to avoid donor's stopPropagation, but if the\n\t\t// simulated event prevents default then we do the same on the donor.\n\t\tvar e = jQuery.extend(\n\t\t\tnew jQuery.Event(),\n\t\t\tevent,\n\t\t\t{\n\t\t\t\ttype: type,\n\t\t\t\tisSimulated: true,\n\t\t\t\toriginalEvent: {}\n\t\t\t}\n\t\t);\n\t\tif ( bubble ) {\n\t\t\tjQuery.event.trigger( e, null, elem );\n\t\t} else {\n\t\t\tjQuery.event.dispatch.call( elem, e );\n\t\t}\n\t\tif ( e.isDefaultPrevented() ) {\n\t\t\tevent.preventDefault();\n\t\t}\n\t}\n};\n\njQuery.removeEvent = function( elem, type, handle ) {\n\tif ( elem.removeEventListener ) {\n\t\telem.removeEventListener( type, handle, false );\n\t}\n};\n\njQuery.Event = function( src, props ) {\n\t// Allow instantiation without the 'new' keyword\n\tif ( !(this instanceof jQuery.Event) ) {\n\t\treturn new jQuery.Event( src, props );\n\t}\n\n\t// Event object\n\tif ( src && src.type ) {\n\t\tthis.originalEvent = src;\n\t\tthis.type = src.type;\n\n\t\t// Events bubbling up the document may have been marked as prevented\n\t\t// by a handler lower down the tree; reflect the correct value.\n\t\tthis.isDefaultPrevented = src.defaultPrevented ||\n\t\t\t\tsrc.defaultPrevented === undefined &&\n\t\t\t\t// Support: Android<4.0\n\t\t\t\tsrc.returnValue === false ?\n\t\t\treturnTrue :\n\t\t\treturnFalse;\n\n\t// Event type\n\t} else {\n\t\tthis.type = src;\n\t}\n\n\t// Put explicitly provided properties onto the event object\n\tif ( props ) {\n\t\tjQuery.extend( this, props );\n\t}\n\n\t// Create a timestamp if incoming event doesn't have one\n\tthis.timeStamp = src && src.timeStamp || jQuery.now();\n\n\t// Mark it as fixed\n\tthis[ jQuery.expando ] = true;\n};\n\n// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding\n// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html\njQuery.Event.prototype = {\n\tisDefaultPrevented: returnFalse,\n\tisPropagationStopped: returnFalse,\n\tisImmediatePropagationStopped: returnFalse,\n\n\tpreventDefault: function() {\n\t\tvar e = this.originalEvent;\n\n\t\tthis.isDefaultPrevented = returnTrue;\n\n\t\tif ( e && e.preventDefault ) {\n\t\t\te.preventDefault();\n\t\t}\n\t},\n\tstopPropagation: function() {\n\t\tvar e = this.originalEvent;\n\n\t\tthis.isPropagationStopped = returnTrue;\n\n\t\tif ( e && e.stopPropagation ) {\n\t\t\te.stopPropagation();\n\t\t}\n\t},\n\tstopImmediatePropagation: function() {\n\t\tvar e = this.originalEvent;\n\n\t\tthis.isImmediatePropagationStopped = returnTrue;\n\n\t\tif ( e && e.stopImmediatePropagation ) {\n\t\t\te.stopImmediatePropagation();\n\t\t}\n\n\t\tthis.stopPropagation();\n\t}\n};\n\n// Create mouseenter/leave events using mouseover/out and event-time checks\n// Support: Chrome 15+\njQuery.each({\n\tmouseenter: \"mouseover\",\n\tmouseleave: \"mouseout\",\n\tpointerenter: \"pointerover\",\n\tpointerleave: \"pointerout\"\n}, function( orig, fix ) {\n\tjQuery.event.special[ orig ] = {\n\t\tdelegateType: fix,\n\t\tbindType: fix,\n\n\t\thandle: function( event ) {\n\t\t\tvar ret,\n\t\t\t\ttarget = this,\n\t\t\t\trelated = event.relatedTarget,\n\t\t\t\thandleObj = event.handleObj;\n\n\t\t\t// For mousenter/leave call the handler if related is outside the target.\n\t\t\t// NB: No relatedTarget if the mouse left/entered the browser window\n\t\t\tif ( !related || (related !== target && !jQuery.contains( target, related )) ) {\n\t\t\t\tevent.type = handleObj.origType;\n\t\t\t\tret = handleObj.handler.apply( this, arguments );\n\t\t\t\tevent.type = fix;\n\t\t\t}\n\t\t\treturn ret;\n\t\t}\n\t};\n});\n\n// Support: Firefox, Chrome, Safari\n// Create \"bubbling\" focus and blur events\nif ( !support.focusinBubbles ) {\n\tjQuery.each({ focus: \"focusin\", blur: \"focusout\" }, function( orig, fix ) {\n\n\t\t// Attach a single capturing handler on the document while someone wants focusin/focusout\n\t\tvar handler = function( event ) {\n\t\t\t\tjQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true );\n\t\t\t};\n\n\t\tjQuery.event.special[ fix ] = {\n\t\t\tsetup: function() {\n\t\t\t\tvar doc = this.ownerDocument || this,\n\t\t\t\t\tattaches = data_priv.access( doc, fix );\n\n\t\t\t\tif ( !attaches ) {\n\t\t\t\t\tdoc.addEventListener( orig, handler, true );\n\t\t\t\t}\n\t\t\t\tdata_priv.access( doc, fix, ( attaches || 0 ) + 1 );\n\t\t\t},\n\t\t\tteardown: function() {\n\t\t\t\tvar doc = this.ownerDocument || this,\n\t\t\t\t\tattaches = data_priv.access( doc, fix ) - 1;\n\n\t\t\t\tif ( !attaches ) {\n\t\t\t\t\tdoc.removeEventListener( orig, handler, true );\n\t\t\t\t\tdata_priv.remove( doc, fix );\n\n\t\t\t\t} else {\n\t\t\t\t\tdata_priv.access( doc, fix, attaches );\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t});\n}\n\njQuery.fn.extend({\n\n\ton: function( types, selector, data, fn, /*INTERNAL*/ one ) {\n\t\tvar origFn, type;\n\n\t\t// Types can be a map of types/handlers\n\t\tif ( typeof types === \"object\" ) {\n\t\t\t// ( types-Object, selector, data )\n\t\t\tif ( typeof selector !== \"string\" ) {\n\t\t\t\t// ( types-Object, data )\n\t\t\t\tdata = data || selector;\n\t\t\t\tselector = undefined;\n\t\t\t}\n\t\t\tfor ( type in types ) {\n\t\t\t\tthis.on( type, selector, data, types[ type ], one );\n\t\t\t}\n\t\t\treturn this;\n\t\t}\n\n\t\tif ( data == null && fn == null ) {\n\t\t\t// ( types, fn )\n\t\t\tfn = selector;\n\t\t\tdata = selector = undefined;\n\t\t} else if ( fn == null ) {\n\t\t\tif ( typeof selector === \"string\" ) {\n\t\t\t\t// ( types, selector, fn )\n\t\t\t\tfn = data;\n\t\t\t\tdata = undefined;\n\t\t\t} else {\n\t\t\t\t// ( types, data, fn )\n\t\t\t\tfn = data;\n\t\t\t\tdata = selector;\n\t\t\t\tselector = undefined;\n\t\t\t}\n\t\t}\n\t\tif ( fn === false ) {\n\t\t\tfn = returnFalse;\n\t\t} else if ( !fn ) {\n\t\t\treturn this;\n\t\t}\n\n\t\tif ( one === 1 ) {\n\t\t\torigFn = fn;\n\t\t\tfn = function( event ) {\n\t\t\t\t// Can use an empty set, since event contains the info\n\t\t\t\tjQuery().off( event );\n\t\t\t\treturn origFn.apply( this, arguments );\n\t\t\t};\n\t\t\t// Use same guid so caller can remove using origFn\n\t\t\tfn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );\n\t\t}\n\t\treturn this.each( function() {\n\t\t\tjQuery.event.add( this, types, fn, data, selector );\n\t\t});\n\t},\n\tone: function( types, selector, data, fn ) {\n\t\treturn this.on( types, selector, data, fn, 1 );\n\t},\n\toff: function( types, selector, fn ) {\n\t\tvar handleObj, type;\n\t\tif ( types && types.preventDefault && types.handleObj ) {\n\t\t\t// ( event ) dispatched jQuery.Event\n\t\t\thandleObj = types.handleObj;\n\t\t\tjQuery( types.delegateTarget ).off(\n\t\t\t\thandleObj.namespace ? handleObj.origType + \".\" + handleObj.namespace : handleObj.origType,\n\t\t\t\thandleObj.selector,\n\t\t\t\thandleObj.handler\n\t\t\t);\n\t\t\treturn this;\n\t\t}\n\t\tif ( typeof types === \"object\" ) {\n\t\t\t// ( types-object [, selector] )\n\t\t\tfor ( type in types ) {\n\t\t\t\tthis.off( type, selector, types[ type ] );\n\t\t\t}\n\t\t\treturn this;\n\t\t}\n\t\tif ( selector === false || typeof selector === \"function\" ) {\n\t\t\t// ( types [, fn] )\n\t\t\tfn = selector;\n\t\t\tselector = undefined;\n\t\t}\n\t\tif ( fn === false ) {\n\t\t\tfn = returnFalse;\n\t\t}\n\t\treturn this.each(function() {\n\t\t\tjQuery.event.remove( this, types, fn, selector );\n\t\t});\n\t},\n\n\ttrigger: function( type, data ) {\n\t\treturn this.each(function() {\n\t\t\tjQuery.event.trigger( type, data, this );\n\t\t});\n\t},\n\ttriggerHandler: function( type, data ) {\n\t\tvar elem = this[0];\n\t\tif ( elem ) {\n\t\t\treturn jQuery.event.trigger( type, data, elem, true );\n\t\t}\n\t}\n});\n\n\nvar\n\trxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\\w:]+)[^>]*)\\/>/gi,\n\trtagName = /<([\\w:]+)/,\n\trhtml = /<|&#?\\w+;/,\n\trnoInnerhtml = /<(?:script|style|link)/i,\n\t// checked=\"checked\" or checked\n\trchecked = /checked\\s*(?:[^=]|=\\s*.checked.)/i,\n\trscriptType = /^$|\\/(?:java|ecma)script/i,\n\trscriptTypeMasked = /^true\\/(.*)/,\n\trcleanScript = /^\\s*\\s*$/g,\n\n\t// We have to close these tags to support XHTML (#13200)\n\twrapMap = {\n\n\t\t// Support: IE9\n\t\toption: [ 1, \"\" ],\n\n\t\tthead: [ 1, \"\", \"
\" ],\n\t\tcol: [ 2, \"\", \"
\" ],\n\t\ttr: [ 2, \"\", \"
\" ],\n\t\ttd: [ 3, \"\", \"
\" ],\n\n\t\t_default: [ 0, \"\", \"\" ]\n\t};\n\n// Support: IE9\nwrapMap.optgroup = wrapMap.option;\n\nwrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;\nwrapMap.th = wrapMap.td;\n\n// Support: 1.x compatibility\n// Manipulating tables requires a tbody\nfunction manipulationTarget( elem, content ) {\n\treturn jQuery.nodeName( elem, \"table\" ) &&\n\t\tjQuery.nodeName( content.nodeType !== 11 ? content : content.firstChild, \"tr\" ) ?\n\n\t\telem.getElementsByTagName(\"tbody\")[0] ||\n\t\t\telem.appendChild( elem.ownerDocument.createElement(\"tbody\") ) :\n\t\telem;\n}\n\n// Replace/restore the type attribute of script elements for safe DOM manipulation\nfunction disableScript( elem ) {\n\telem.type = (elem.getAttribute(\"type\") !== null) + \"/\" + elem.type;\n\treturn elem;\n}\nfunction restoreScript( elem ) {\n\tvar match = rscriptTypeMasked.exec( elem.type );\n\n\tif ( match ) {\n\t\telem.type = match[ 1 ];\n\t} else {\n\t\telem.removeAttribute(\"type\");\n\t}\n\n\treturn elem;\n}\n\n// Mark scripts as having already been evaluated\nfunction setGlobalEval( elems, refElements ) {\n\tvar i = 0,\n\t\tl = elems.length;\n\n\tfor ( ; i < l; i++ ) {\n\t\tdata_priv.set(\n\t\t\telems[ i ], \"globalEval\", !refElements || data_priv.get( refElements[ i ], \"globalEval\" )\n\t\t);\n\t}\n}\n\nfunction cloneCopyEvent( src, dest ) {\n\tvar i, l, type, pdataOld, pdataCur, udataOld, udataCur, events;\n\n\tif ( dest.nodeType !== 1 ) {\n\t\treturn;\n\t}\n\n\t// 1. Copy private data: events, handlers, etc.\n\tif ( data_priv.hasData( src ) ) {\n\t\tpdataOld = data_priv.access( src );\n\t\tpdataCur = data_priv.set( dest, pdataOld );\n\t\tevents = pdataOld.events;\n\n\t\tif ( events ) {\n\t\t\tdelete pdataCur.handle;\n\t\t\tpdataCur.events = {};\n\n\t\t\tfor ( type in events ) {\n\t\t\t\tfor ( i = 0, l = events[ type ].length; i < l; i++ ) {\n\t\t\t\t\tjQuery.event.add( dest, type, events[ type ][ i ] );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// 2. Copy user data\n\tif ( data_user.hasData( src ) ) {\n\t\tudataOld = data_user.access( src );\n\t\tudataCur = jQuery.extend( {}, udataOld );\n\n\t\tdata_user.set( dest, udataCur );\n\t}\n}\n\nfunction getAll( context, tag ) {\n\tvar ret = context.getElementsByTagName ? context.getElementsByTagName( tag || \"*\" ) :\n\t\t\tcontext.querySelectorAll ? context.querySelectorAll( tag || \"*\" ) :\n\t\t\t[];\n\n\treturn tag === undefined || tag && jQuery.nodeName( context, tag ) ?\n\t\tjQuery.merge( [ context ], ret ) :\n\t\tret;\n}\n\n// Fix IE bugs, see support tests\nfunction fixInput( src, dest ) {\n\tvar nodeName = dest.nodeName.toLowerCase();\n\n\t// Fails to persist the checked state of a cloned checkbox or radio button.\n\tif ( nodeName === \"input\" && rcheckableType.test( src.type ) ) {\n\t\tdest.checked = src.checked;\n\n\t// Fails to return the selected option to the default selected state when cloning options\n\t} else if ( nodeName === \"input\" || nodeName === \"textarea\" ) {\n\t\tdest.defaultValue = src.defaultValue;\n\t}\n}\n\njQuery.extend({\n\tclone: function( elem, dataAndEvents, deepDataAndEvents ) {\n\t\tvar i, l, srcElements, destElements,\n\t\t\tclone = elem.cloneNode( true ),\n\t\t\tinPage = jQuery.contains( elem.ownerDocument, elem );\n\n\t\t// Fix IE cloning issues\n\t\tif ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) &&\n\t\t\t\t!jQuery.isXMLDoc( elem ) ) {\n\n\t\t\t// We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2\n\t\t\tdestElements = getAll( clone );\n\t\t\tsrcElements = getAll( elem );\n\n\t\t\tfor ( i = 0, l = srcElements.length; i < l; i++ ) {\n\t\t\t\tfixInput( srcElements[ i ], destElements[ i ] );\n\t\t\t}\n\t\t}\n\n\t\t// Copy the events from the original to the clone\n\t\tif ( dataAndEvents ) {\n\t\t\tif ( deepDataAndEvents ) {\n\t\t\t\tsrcElements = srcElements || getAll( elem );\n\t\t\t\tdestElements = destElements || getAll( clone );\n\n\t\t\t\tfor ( i = 0, l = srcElements.length; i < l; i++ ) {\n\t\t\t\t\tcloneCopyEvent( srcElements[ i ], destElements[ i ] );\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tcloneCopyEvent( elem, clone );\n\t\t\t}\n\t\t}\n\n\t\t// Preserve script evaluation history\n\t\tdestElements = getAll( clone, \"script\" );\n\t\tif ( destElements.length > 0 ) {\n\t\t\tsetGlobalEval( destElements, !inPage && getAll( elem, \"script\" ) );\n\t\t}\n\n\t\t// Return the cloned set\n\t\treturn clone;\n\t},\n\n\tbuildFragment: function( elems, context, scripts, selection ) {\n\t\tvar elem, tmp, tag, wrap, contains, j,\n\t\t\tfragment = context.createDocumentFragment(),\n\t\t\tnodes = [],\n\t\t\ti = 0,\n\t\t\tl = elems.length;\n\n\t\tfor ( ; i < l; i++ ) {\n\t\t\telem = elems[ i ];\n\n\t\t\tif ( elem || elem === 0 ) {\n\n\t\t\t\t// Add nodes directly\n\t\t\t\tif ( jQuery.type( elem ) === \"object\" ) {\n\t\t\t\t\t// Support: QtWebKit, PhantomJS\n\t\t\t\t\t// push.apply(_, arraylike) throws on ancient WebKit\n\t\t\t\t\tjQuery.merge( nodes, elem.nodeType ? [ elem ] : elem );\n\n\t\t\t\t// Convert non-html into a text node\n\t\t\t\t} else if ( !rhtml.test( elem ) ) {\n\t\t\t\t\tnodes.push( context.createTextNode( elem ) );\n\n\t\t\t\t// Convert html into DOM nodes\n\t\t\t\t} else {\n\t\t\t\t\ttmp = tmp || fragment.appendChild( context.createElement(\"div\") );\n\n\t\t\t\t\t// Deserialize a standard representation\n\t\t\t\t\ttag = ( rtagName.exec( elem ) || [ \"\", \"\" ] )[ 1 ].toLowerCase();\n\t\t\t\t\twrap = wrapMap[ tag ] || wrapMap._default;\n\t\t\t\t\ttmp.innerHTML = wrap[ 1 ] + elem.replace( rxhtmlTag, \"<$1>\" ) + wrap[ 2 ];\n\n\t\t\t\t\t// Descend through wrappers to the right content\n\t\t\t\t\tj = wrap[ 0 ];\n\t\t\t\t\twhile ( j-- ) {\n\t\t\t\t\t\ttmp = tmp.lastChild;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Support: QtWebKit, PhantomJS\n\t\t\t\t\t// push.apply(_, arraylike) throws on ancient WebKit\n\t\t\t\t\tjQuery.merge( nodes, tmp.childNodes );\n\n\t\t\t\t\t// Remember the top-level container\n\t\t\t\t\ttmp = fragment.firstChild;\n\n\t\t\t\t\t// Ensure the created nodes are orphaned (#12392)\n\t\t\t\t\ttmp.textContent = \"\";\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Remove wrapper from fragment\n\t\tfragment.textContent = \"\";\n\n\t\ti = 0;\n\t\twhile ( (elem = nodes[ i++ ]) ) {\n\n\t\t\t// #4087 - If origin and destination elements are the same, and this is\n\t\t\t// that element, do not do anything\n\t\t\tif ( selection && jQuery.inArray( elem, selection ) !== -1 ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tcontains = jQuery.contains( elem.ownerDocument, elem );\n\n\t\t\t// Append to fragment\n\t\t\ttmp = getAll( fragment.appendChild( elem ), \"script\" );\n\n\t\t\t// Preserve script evaluation history\n\t\t\tif ( contains ) {\n\t\t\t\tsetGlobalEval( tmp );\n\t\t\t}\n\n\t\t\t// Capture executables\n\t\t\tif ( scripts ) {\n\t\t\t\tj = 0;\n\t\t\t\twhile ( (elem = tmp[ j++ ]) ) {\n\t\t\t\t\tif ( rscriptType.test( elem.type || \"\" ) ) {\n\t\t\t\t\t\tscripts.push( elem );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn fragment;\n\t},\n\n\tcleanData: function( elems ) {\n\t\tvar data, elem, type, key,\n\t\t\tspecial = jQuery.event.special,\n\t\t\ti = 0;\n\n\t\tfor ( ; (elem = elems[ i ]) !== undefined; i++ ) {\n\t\t\tif ( jQuery.acceptData( elem ) ) {\n\t\t\t\tkey = elem[ data_priv.expando ];\n\n\t\t\t\tif ( key && (data = data_priv.cache[ key ]) ) {\n\t\t\t\t\tif ( data.events ) {\n\t\t\t\t\t\tfor ( type in data.events ) {\n\t\t\t\t\t\t\tif ( special[ type ] ) {\n\t\t\t\t\t\t\t\tjQuery.event.remove( elem, type );\n\n\t\t\t\t\t\t\t// This is a shortcut to avoid jQuery.event.remove's overhead\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tjQuery.removeEvent( elem, type, data.handle );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif ( data_priv.cache[ key ] ) {\n\t\t\t\t\t\t// Discard any remaining `private` data\n\t\t\t\t\t\tdelete data_priv.cache[ key ];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Discard any remaining `user` data\n\t\t\tdelete data_user.cache[ elem[ data_user.expando ] ];\n\t\t}\n\t}\n});\n\njQuery.fn.extend({\n\ttext: function( value ) {\n\t\treturn access( this, function( value ) {\n\t\t\treturn value === undefined ?\n\t\t\t\tjQuery.text( this ) :\n\t\t\t\tthis.empty().each(function() {\n\t\t\t\t\tif ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {\n\t\t\t\t\t\tthis.textContent = value;\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t}, null, value, arguments.length );\n\t},\n\n\tappend: function() {\n\t\treturn this.domManip( arguments, function( elem ) {\n\t\t\tif ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {\n\t\t\t\tvar target = manipulationTarget( this, elem );\n\t\t\t\ttarget.appendChild( elem );\n\t\t\t}\n\t\t});\n\t},\n\n\tprepend: function() {\n\t\treturn this.domManip( arguments, function( elem ) {\n\t\t\tif ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {\n\t\t\t\tvar target = manipulationTarget( this, elem );\n\t\t\t\ttarget.insertBefore( elem, target.firstChild );\n\t\t\t}\n\t\t});\n\t},\n\n\tbefore: function() {\n\t\treturn this.domManip( arguments, function( elem ) {\n\t\t\tif ( this.parentNode ) {\n\t\t\t\tthis.parentNode.insertBefore( elem, this );\n\t\t\t}\n\t\t});\n\t},\n\n\tafter: function() {\n\t\treturn this.domManip( arguments, function( elem ) {\n\t\t\tif ( this.parentNode ) {\n\t\t\t\tthis.parentNode.insertBefore( elem, this.nextSibling );\n\t\t\t}\n\t\t});\n\t},\n\n\tremove: function( selector, keepData /* Internal Use Only */ ) {\n\t\tvar elem,\n\t\t\telems = selector ? jQuery.filter( selector, this ) : this,\n\t\t\ti = 0;\n\n\t\tfor ( ; (elem = elems[i]) != null; i++ ) {\n\t\t\tif ( !keepData && elem.nodeType === 1 ) {\n\t\t\t\tjQuery.cleanData( getAll( elem ) );\n\t\t\t}\n\n\t\t\tif ( elem.parentNode ) {\n\t\t\t\tif ( keepData && jQuery.contains( elem.ownerDocument, elem ) ) {\n\t\t\t\t\tsetGlobalEval( getAll( elem, \"script\" ) );\n\t\t\t\t}\n\t\t\t\telem.parentNode.removeChild( elem );\n\t\t\t}\n\t\t}\n\n\t\treturn this;\n\t},\n\n\tempty: function() {\n\t\tvar elem,\n\t\t\ti = 0;\n\n\t\tfor ( ; (elem = this[i]) != null; i++ ) {\n\t\t\tif ( elem.nodeType === 1 ) {\n\n\t\t\t\t// Prevent memory leaks\n\t\t\t\tjQuery.cleanData( getAll( elem, false ) );\n\n\t\t\t\t// Remove any remaining nodes\n\t\t\t\telem.textContent = \"\";\n\t\t\t}\n\t\t}\n\n\t\treturn this;\n\t},\n\n\tclone: function( dataAndEvents, deepDataAndEvents ) {\n\t\tdataAndEvents = dataAndEvents == null ? false : dataAndEvents;\n\t\tdeepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;\n\n\t\treturn this.map(function() {\n\t\t\treturn jQuery.clone( this, dataAndEvents, deepDataAndEvents );\n\t\t});\n\t},\n\n\thtml: function( value ) {\n\t\treturn access( this, function( value ) {\n\t\t\tvar elem = this[ 0 ] || {},\n\t\t\t\ti = 0,\n\t\t\t\tl = this.length;\n\n\t\t\tif ( value === undefined && elem.nodeType === 1 ) {\n\t\t\t\treturn elem.innerHTML;\n\t\t\t}\n\n\t\t\t// See if we can take a shortcut and just use innerHTML\n\t\t\tif ( typeof value === \"string\" && !rnoInnerhtml.test( value ) &&\n\t\t\t\t!wrapMap[ ( rtagName.exec( value ) || [ \"\", \"\" ] )[ 1 ].toLowerCase() ] ) {\n\n\t\t\t\tvalue = value.replace( rxhtmlTag, \"<$1>\" );\n\n\t\t\t\ttry {\n\t\t\t\t\tfor ( ; i < l; i++ ) {\n\t\t\t\t\t\telem = this[ i ] || {};\n\n\t\t\t\t\t\t// Remove element nodes and prevent memory leaks\n\t\t\t\t\t\tif ( elem.nodeType === 1 ) {\n\t\t\t\t\t\t\tjQuery.cleanData( getAll( elem, false ) );\n\t\t\t\t\t\t\telem.innerHTML = value;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\telem = 0;\n\n\t\t\t\t// If using innerHTML throws an exception, use the fallback method\n\t\t\t\t} catch( e ) {}\n\t\t\t}\n\n\t\t\tif ( elem ) {\n\t\t\t\tthis.empty().append( value );\n\t\t\t}\n\t\t}, null, value, arguments.length );\n\t},\n\n\treplaceWith: function() {\n\t\tvar arg = arguments[ 0 ];\n\n\t\t// Make the changes, replacing each context element with the new content\n\t\tthis.domManip( arguments, function( elem ) {\n\t\t\targ = this.parentNode;\n\n\t\t\tjQuery.cleanData( getAll( this ) );\n\n\t\t\tif ( arg ) {\n\t\t\t\targ.replaceChild( elem, this );\n\t\t\t}\n\t\t});\n\n\t\t// Force removal if there was no new content (e.g., from empty arguments)\n\t\treturn arg && (arg.length || arg.nodeType) ? this : this.remove();\n\t},\n\n\tdetach: function( selector ) {\n\t\treturn this.remove( selector, true );\n\t},\n\n\tdomManip: function( args, callback ) {\n\n\t\t// Flatten any nested arrays\n\t\targs = concat.apply( [], args );\n\n\t\tvar fragment, first, scripts, hasScripts, node, doc,\n\t\t\ti = 0,\n\t\t\tl = this.length,\n\t\t\tset = this,\n\t\t\tiNoClone = l - 1,\n\t\t\tvalue = args[ 0 ],\n\t\t\tisFunction = jQuery.isFunction( value );\n\n\t\t// We can't cloneNode fragments that contain checked, in WebKit\n\t\tif ( isFunction ||\n\t\t\t\t( l > 1 && typeof value === \"string\" &&\n\t\t\t\t\t!support.checkClone && rchecked.test( value ) ) ) {\n\t\t\treturn this.each(function( index ) {\n\t\t\t\tvar self = set.eq( index );\n\t\t\t\tif ( isFunction ) {\n\t\t\t\t\targs[ 0 ] = value.call( this, index, self.html() );\n\t\t\t\t}\n\t\t\t\tself.domManip( args, callback );\n\t\t\t});\n\t\t}\n\n\t\tif ( l ) {\n\t\t\tfragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, this );\n\t\t\tfirst = fragment.firstChild;\n\n\t\t\tif ( fragment.childNodes.length === 1 ) {\n\t\t\t\tfragment = first;\n\t\t\t}\n\n\t\t\tif ( first ) {\n\t\t\t\tscripts = jQuery.map( getAll( fragment, \"script\" ), disableScript );\n\t\t\t\thasScripts = scripts.length;\n\n\t\t\t\t// Use the original fragment for the last item instead of the first because it can end up\n\t\t\t\t// being emptied incorrectly in certain situations (#8070).\n\t\t\t\tfor ( ; i < l; i++ ) {\n\t\t\t\t\tnode = fragment;\n\n\t\t\t\t\tif ( i !== iNoClone ) {\n\t\t\t\t\t\tnode = jQuery.clone( node, true, true );\n\n\t\t\t\t\t\t// Keep references to cloned scripts for later restoration\n\t\t\t\t\t\tif ( hasScripts ) {\n\t\t\t\t\t\t\t// Support: QtWebKit\n\t\t\t\t\t\t\t// jQuery.merge because push.apply(_, arraylike) throws\n\t\t\t\t\t\t\tjQuery.merge( scripts, getAll( node, \"script\" ) );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tcallback.call( this[ i ], node, i );\n\t\t\t\t}\n\n\t\t\t\tif ( hasScripts ) {\n\t\t\t\t\tdoc = scripts[ scripts.length - 1 ].ownerDocument;\n\n\t\t\t\t\t// Reenable scripts\n\t\t\t\t\tjQuery.map( scripts, restoreScript );\n\n\t\t\t\t\t// Evaluate executable scripts on first document insertion\n\t\t\t\t\tfor ( i = 0; i < hasScripts; i++ ) {\n\t\t\t\t\t\tnode = scripts[ i ];\n\t\t\t\t\t\tif ( rscriptType.test( node.type || \"\" ) &&\n\t\t\t\t\t\t\t!data_priv.access( node, \"globalEval\" ) && jQuery.contains( doc, node ) ) {\n\n\t\t\t\t\t\t\tif ( node.src ) {\n\t\t\t\t\t\t\t\t// Optional AJAX dependency, but won't run scripts if not present\n\t\t\t\t\t\t\t\tif ( jQuery._evalUrl ) {\n\t\t\t\t\t\t\t\t\tjQuery._evalUrl( node.src );\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tjQuery.globalEval( node.textContent.replace( rcleanScript, \"\" ) );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn this;\n\t}\n});\n\njQuery.each({\n\tappendTo: \"append\",\n\tprependTo: \"prepend\",\n\tinsertBefore: \"before\",\n\tinsertAfter: \"after\",\n\treplaceAll: \"replaceWith\"\n}, function( name, original ) {\n\tjQuery.fn[ name ] = function( selector ) {\n\t\tvar elems,\n\t\t\tret = [],\n\t\t\tinsert = jQuery( selector ),\n\t\t\tlast = insert.length - 1,\n\t\t\ti = 0;\n\n\t\tfor ( ; i <= last; i++ ) {\n\t\t\telems = i === last ? this : this.clone( true );\n\t\t\tjQuery( insert[ i ] )[ original ]( elems );\n\n\t\t\t// Support: QtWebKit\n\t\t\t// .get() because push.apply(_, arraylike) throws\n\t\t\tpush.apply( ret, elems.get() );\n\t\t}\n\n\t\treturn this.pushStack( ret );\n\t};\n});\n\n\nvar iframe,\n\telemdisplay = {};\n\n/**\n * Retrieve the actual display of a element\n * @param {String} name nodeName of the element\n * @param {Object} doc Document object\n */\n// Called only from within defaultDisplay\nfunction actualDisplay( name, doc ) {\n\tvar style,\n\t\telem = jQuery( doc.createElement( name ) ).appendTo( doc.body ),\n\n\t\t// getDefaultComputedStyle might be reliably used only on attached element\n\t\tdisplay = window.getDefaultComputedStyle && ( style = window.getDefaultComputedStyle( elem[ 0 ] ) ) ?\n\n\t\t\t// Use of this method is a temporary fix (more like optimization) until something better comes along,\n\t\t\t// since it was removed from specification and supported only in FF\n\t\t\tstyle.display : jQuery.css( elem[ 0 ], \"display\" );\n\n\t// We don't have any data stored on the element,\n\t// so use \"detach\" method as fast way to get rid of the element\n\telem.detach();\n\n\treturn display;\n}\n\n/**\n * Try to determine the default display value of an element\n * @param {String} nodeName\n */\nfunction defaultDisplay( nodeName ) {\n\tvar doc = document,\n\t\tdisplay = elemdisplay[ nodeName ];\n\n\tif ( !display ) {\n\t\tdisplay = actualDisplay( nodeName, doc );\n\n\t\t// If the simple way fails, read from inside an iframe\n\t\tif ( display === \"none\" || !display ) {\n\n\t\t\t// Use the already-created iframe if possible\n\t\t\tiframe = (iframe || jQuery( \"