Rift.IO OSM R1 Initial Submission
Signed-off-by: Jeremy Mordkoff <jeremy.mordkoff@riftio.com>
diff --git a/skyquake/framework/core/api_utils/constants.js b/skyquake/framework/core/api_utils/constants.js
new file mode 100644
index 0000000..dccadb7
--- /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 0000000..6076594
--- /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 0000000..163769a
--- /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 0000000..3762643
--- /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 <kiran.kashalkar@riftio.com>
+ */
+
+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 0000000..b0223b2
--- /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 0000000..5f22835
--- /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 <kiran.kashalkar@riftio.com>
+ */
+
+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 0000000..5ba0eb5
--- /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 <kiran.kashalkar@riftio.com>
+ */
+
+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 0000000..c85eba6
--- /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 <kiran.kashalkar@riftio.com>
+ */
+
+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 0000000..6653e41
--- /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 <kiran.kashalkar@riftio.com>
+ */
+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 0000000..3a686f0
--- /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 <kiran.kashalkar@riftio.com>
+ */
+
+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 0000000..28b7f15
--- /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 <laurence.maultsby@riftio.com>
+ */
+
+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 0000000..7c3c440
--- /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 <laurence.maultsby@riftio.com>
+ */
+
+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 0000000..82c7ec5
--- /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 <kiran.kashalkar@riftio.com>
+ */
+
+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 0000000..7a073c5
--- /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 <kiran.kashalkar@riftio.com>
+ */
+
+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 0000000..da3f3b1
--- /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 <kiran.kashalkar@riftio.com>
+ */
+
+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 0000000..d3cfeb9
--- /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 <kiran.kashalkar@riftio.com>
+ */
+
+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 0000000..ad881c3
--- /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 <mikhus@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
+ * Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * @authors: Mykhailo Stadnyk <mikhus@gmail.com>
+ * Chris Poile <poile@edwards.usask.ca>
+ * Luca Invernizzi <http://www.lucainvernizzi.net>
+ * Robert Blackburn <http://www.rwblackburn.com>
+ */
+
+ /**
+ * @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 0000000..b3105cd
--- /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: '<div></div>',
+ 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 0000000..9378984
--- /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 0000000..02dbee0
--- /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 0000000..db161b2
--- /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 0000000..2a64a8c
--- /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 0000000..664e1b2
--- /dev/null
+++ b/skyquake/framework/style/fonts/Roboto-Light-webfont.ttf
Binary files 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 0000000..9b5e8e4
--- /dev/null
+++ b/skyquake/framework/style/fonts/Roboto-Regular-webfont.eot
Binary files 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 0000000..de7d77f
--- /dev/null
+++ b/skyquake/framework/style/fonts/Roboto-Regular-webfont.svg
@@ -0,0 +1,621 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
+<svg xmlns="http://www.w3.org/2000/svg">
+<metadata></metadata>
+<defs>
+<font id="robotoregular" horiz-adv-x="1164" >
+<font-face units-per-em="2048" ascent="1638" descent="-410" />
+<missing-glyph horiz-adv-x="509" />
+<glyph unicode="fi" horiz-adv-x="1140" d="M28 936v146h170v117q0 182 106.5 282t295.5 100q67 0 132 -15.5t153 -45.5l-34 -160q-53 21 -113 36t-123 15q-117 0 -168.5 -52t-51.5 -160v-117h215v-146h-215v-936h-197v936h-170zM783 0v1082h198v-1082h-198z" />
+<glyph horiz-adv-x="2048" />
+<glyph horiz-adv-x="2048" />
+<glyph unicode="
" horiz-adv-x="509" />
+<glyph unicode=" " horiz-adv-x="509" />
+<glyph unicode="	" horiz-adv-x="509" />
+<glyph unicode=" " horiz-adv-x="509" />
+<glyph unicode="!" horiz-adv-x="539" d="M171 0v204h198v-204h-198zM171 478v978h197v-978h-197z" />
+<glyph unicode=""" horiz-adv-x="668" d="M80 1040l1 240v280h197v-270l-101 -250h-97zM389 1040l1 248v272h197v-270l-101 -250h-97z" />
+<glyph unicode="#" horiz-adv-x="1276" d="M70 410v140h264l68 348h-256v142h284l82 416h151l-82 -416h255l82 416h151l-82 -416h199v-142h-226l-68 -348h219v-140h-247l-80 -410h-152l80 410h-255l-80 -410h-151l80 410h-236zM485 550h255l68 348h-255z" />
+<glyph unicode="$" horiz-adv-x="1153" d="M114 424l2 5h190q0 -154 77.5 -219.5t190.5 -65.5q129 0 201.5 61.5t72.5 170.5q0 89 -64 153t-210 114q-202 61 -305 163t-103 272q0 165 94.5 269t260.5 125v221h158v-222q168 -24 260.5 -143.5t92.5 -320.5h-196q0 136 -63 220t-175 84q-118 0 -176.5 -61.5 t-58.5 -168.5q0 -97 60.5 -157t218.5 -114q205 -66 304 -164.5t99 -267.5q0 -172 -103 -273.5t-283 -120.5v-192h-157v191q-172 18 -282 125.5t-106 315.5z" />
+<glyph unicode="%" horiz-adv-x="1498" d="M104 1099v77q0 127 82 214t219 87t219 -86.5t82 -214.5v-77q0 -127 -81.5 -213t-217.5 -86q-138 0 -220.5 86t-82.5 213zM250 1099q0 -74 40.5 -125.5t116.5 -51.5q73 0 113 51t40 126v77q0 74 -40.5 126.5t-114.5 52.5q-75 0 -115 -52.5t-40 -126.5v-77zM349 177 l711 1138l109 -67l-711 -1138zM809 279v78q0 127 82 213.5t219 86.5q136 0 218.5 -86.5t82.5 -213.5v-78q0 -128 -82 -214t-217 -86q-138 0 -220.5 86t-82.5 214zM955 279q0 -75 40.5 -126.5t116.5 -51.5q73 0 113 51.5t40 126.5v78q0 74 -41 126t-114 52q-74 0 -114.5 -52 t-40.5 -126v-78z" />
+<glyph unicode="&" horiz-adv-x="1276" d="M64 392q0 122 70.5 213.5t210.5 183.5q-78 99 -116 176.5t-38 159.5q0 169 97.5 260.5t268.5 91.5q158 0 257 -91t99 -219q0 -98 -52.5 -169.5t-155.5 -146.5l-109 -80l340 -409q41 65 64 144t23 167h176q0 -132 -39 -244t-113 -201l185 -223l-2 -5h-229l-85 102 q-80 -60 -177 -91.5t-201 -31.5q-217 0 -345.5 115t-128.5 298zM261 392q0 -113 71 -186t206 -73q72 0 142 24.5t132 70.5l-361 435l-40 -29q-91 -68 -120.5 -130t-29.5 -112zM388 1127q0 -53 27 -110.5t81 -125.5l138 95q57 38 77.5 82.5t20.5 98.5q0 61 -48.5 108 t-126.5 47q-81 0 -125 -56.5t-44 -138.5z" />
+<glyph unicode="'" horiz-adv-x="359" d="M80 1055l1 265v240h197v-223l-101 -282h-97z" />
+<glyph unicode="(" horiz-adv-x="679" d="M132 582v9q0 394 159 673t334 372l6 -1l38 -116q-137 -107 -238.5 -343t-101.5 -583v-13q0 -347 101 -583t239 -352l-38 -108h-6q-175 93 -334 371.5t-159 673.5z" />
+<glyph unicode=")" horiz-adv-x="687" d="M6 -355q135 105 237.5 345.5t102.5 589.5v13q0 342 -105.5 583.5t-234.5 351.5l38 108h6q174 -93 333.5 -372t159.5 -673v-9q0 -395 -159.5 -673.5t-333.5 -371.5h-6z" />
+<glyph unicode="*" horiz-adv-x="884" d="M28 1071l49 154l296 -111l-10 342h161l-10 -348l293 110l48 -156l-302 -89l193 -270l-131 -96l-181 287l-176 -279l-132 93l198 274z" />
+<glyph unicode="+" horiz-adv-x="1162" d="M78 605v178h402v423h197v-423h399v-178h-399v-459h-197v459h-402z" />
+<glyph unicode="," horiz-adv-x="404" d="M48 -258l70 316v163h197v-173l-150 -306h-117z" />
+<glyph unicode="-" horiz-adv-x="561" d="M35 538v154h490v-154h-490z" />
+<glyph unicode="." horiz-adv-x="548" d="M161 0v202h197v-202h-197z" />
+<glyph unicode="/" horiz-adv-x="850" d="M16 -125l608 1581h167l-607 -1581h-168z" />
+<glyph unicode="0" horiz-adv-x="1154" d="M113 555v345q0 278 124.5 427.5t338.5 149.5q215 0 339.5 -149.5t124.5 -427.5v-345q0 -279 -123.5 -427.5t-338.5 -148.5t-340 149t-125 427zM310 515q0 -189 69 -285.5t199 -96.5t197.5 96t67.5 286v427q0 189 -68.5 284.5t-198.5 95.5t-198 -95.5t-68 -284.5v-427z " />
+<glyph unicode="1" horiz-adv-x="1153" d="M186 1260v142l495 54v-1456h-197v1264z" />
+<glyph unicode="2" horiz-adv-x="1153" d="M97 1033q-5 188 125 316t360 128q196 0 312.5 -114.5t116.5 -291.5q0 -119 -70.5 -238.5t-197.5 -256.5l-383 -417l2 -5h700v-154h-944v135l477 530q128 143 173.5 227t45.5 172q0 109 -63.5 183.5t-168.5 74.5q-151 0 -222.5 -77.5t-71.5 -217.5h-189z" />
+<glyph unicode="3" horiz-adv-x="1153" d="M100 378l3 6h188q0 -115 70.5 -183t193.5 -68q125 0 196 68t71 201q0 135 -63 199t-199 64h-172v154h172q131 0 185.5 65.5t54.5 182.5q0 125 -62 190t-183 65q-115 0 -184.5 -67.5t-69.5 -179.5h-189l-2 6q-5 165 119.5 280.5t325.5 115.5q202 0 322 -107.5t120 -306.5 q0 -90 -54.5 -179.5t-163.5 -136.5q131 -43 185.5 -135t54.5 -206q0 -199 -130.5 -313t-333.5 -114q-199 0 -329.5 107.5t-125.5 291.5z" />
+<glyph unicode="4" horiz-adv-x="1153" d="M55 336v111l642 1009h208v-966h201v-154h-201v-336h-196v336h-654zM265 490h444v683l-6 1l-19 -50z" />
+<glyph unicode="5" horiz-adv-x="1153" d="M157 377l2 6h178q0 -119 68.5 -184.5t177.5 -65.5q125 0 194 88t69 241q0 140 -70 230t-193 90q-116 0 -168 -35t-76 -107l-164 17l84 799h729v-175h-562l-48 -409q46 34 102.5 56.5t130.5 24.5q201 2 316.5 -131t115.5 -358q0 -219 -117.5 -352t-342.5 -133 q-185 0 -308 101t-118 297z" />
+<glyph unicode="6" horiz-adv-x="1153" d="M132 571v278q0 280 156 454t387 174q75 0 148.5 -17t121.5 -43l-42 -151q-49 25 -102.5 40.5t-125.5 15.5q-156 0 -251.5 -125t-95.5 -326v-23q64 56 146.5 87.5t177.5 31.5q195 0 311 -135t116 -342q0 -226 -123.5 -368.5t-329.5 -142.5q-214 0 -354 155t-140 437z M328 552q0 -201 85 -310t213 -109q121 0 188.5 102.5t67.5 254.5q0 144 -72.5 237t-201.5 93q-101 0 -172 -41t-108 -109v-118z" />
+<glyph unicode="7" horiz-adv-x="1153" d="M77 1301v155h985v-155q-264 -314 -356.5 -556.5t-133.5 -587.5l-16 -157h-197l16 157q42 344 163 615t331 529h-792z" />
+<glyph unicode="8" horiz-adv-x="1153" d="M102 394q0 123 74 217t200 138q-109 42 -171 127.5t-62 199.5q0 192 118.5 296.5t313.5 104.5q192 0 313.5 -104.5t121.5 -296.5q0 -114 -64 -199.5t-173 -127.5q126 -44 201.5 -138t75.5 -217q0 -202 -131.5 -308.5t-341.5 -106.5q-214 0 -344.5 106.5t-130.5 308.5z M299 398q0 -124 76 -194.5t202 -70.5q123 0 200 71t77 194q0 120 -79 197t-200 77q-123 0 -199.5 -77t-76.5 -197zM340 1072q0 -111 65.5 -178t171.5 -67q104 0 170 67t66 178q0 108 -67.5 179t-170.5 71q-105 0 -170 -68.5t-65 -181.5z" />
+<glyph unicode="9" horiz-adv-x="1153" d="M83 978q0 219 131.5 359t319.5 140q228 0 359.5 -142.5t131.5 -419.5v-347q0 -285 -142.5 -437t-371.5 -152q-77 0 -156.5 14.5t-142.5 44.5l30 151q59 -31 122.5 -43.5t146.5 -12.5q144 0 230.5 109t86.5 324v66q-49 -71 -122.5 -107.5t-163.5 -36.5q-211 0 -335 130.5 t-124 359.5zM280 978q0 -150 70.5 -243t191.5 -93q109 0 181.5 47t104.5 120v126q0 191 -73.5 289t-214.5 98q-108 0 -184 -96.5t-76 -247.5z" />
+<glyph unicode=":" horiz-adv-x="517" d="M161 0v202h197v-202h-197zM161 876v202h197v-202h-197z" />
+<glyph unicode=";" horiz-adv-x="525" d="M99 -258l70 316v163h197v-173l-150 -306h-117zM162 876v202h197v-202h-197z" />
+<glyph unicode="<" horiz-adv-x="1040" d="M71 466v149l816 378v-201l-559 -233l-85 -18v-6l85 -19l559 -228v-201z" />
+<glyph unicode="=" horiz-adv-x="1153" d="M152 407v164h834v-164h-834zM152 823v164h834v-164h-834z" />
+<glyph unicode=">" horiz-adv-x="1072" d="M136 87v196l598 238l85 17v6l-85 20l-598 234v195l856 -378v-149z" />
+<glyph unicode="?" horiz-adv-x="974" d="M61 1122q-3 161 113.5 258t296.5 97q197 0 306 -100.5t109 -280.5q0 -129 -70.5 -236t-186.5 -219q-54 -54 -65.5 -97t-11.5 -134h-197q1 145 25 201t126 148q99 117 141 180t42 152q0 106 -56.5 163t-161.5 57q-91 0 -155 -49.5t-64 -145.5h-188zM353 0v208h206v-208 h-206z" />
+<glyph unicode="@" horiz-adv-x="1833" d="M114 478q19 423 249 688t602 265q379 0 581.5 -250t185.5 -679q-9 -214 -120 -368.5t-332 -154.5q-73 0 -126 41.5t-76 117.5q-50 -80 -122 -119.5t-168 -39.5q-125 0 -194 120.5t-51 316.5q23 259 137.5 415.5t279.5 156.5q105 0 169 -26t139 -80l-4 -4h6l-51 -585 q-9 -110 21.5 -151.5t81.5 -41.5q123 0 197 113.5t82 288.5q16 382 -144 595.5t-496 213.5q-308 0 -495.5 -231t-202.5 -602q-18 -376 150 -594.5t482 -218.5q88 0 178.5 21.5t152.5 56.5l38 -107q-67 -42 -170.5 -65.5t-202.5 -23.5q-380 0 -587.5 249.5t-189.5 681.5z M720 416q-11 -142 21.5 -216t106.5 -74q64 0 117 24.5t97 87.5q-1 12 -0.5 25.5t2.5 29.5l47 538q-26 12 -54.5 19t-59.5 7q-125 0 -191 -109.5t-86 -331.5z" />
+<glyph unicode="A" horiz-adv-x="1326" d="M20 0l563 1456h169l554 -1456h-201l-136 375h-610l-138 -375h-201zM420 540h490l-240 663h-6z" />
+<glyph unicode="B" horiz-adv-x="1309" d="M180 0v1456h475q228 0 357 -98.5t129 -295.5q0 -97 -62 -173.5t-163 -113.5q132 -28 207.5 -129t75.5 -235q0 -200 -129.5 -305.5t-351.5 -105.5h-538zM377 154h341q134 0 209 66.5t75 188.5q0 128 -62.5 201t-192.5 73h-370v-529zM377 837h319q110 0 179 60.5t69 168.5 q0 118 -74.5 176.5t-214.5 58.5h-278v-464z" />
+<glyph unicode="C" horiz-adv-x="1297" d="M118 598v259q0 269 155.5 444.5t402.5 175.5q247 1 393 -131q142 -128 142 -337v-12l-2 -6h-189q0 153 -90 242t-254 89q-165 0 -263 -133t-98 -330v-261q0 -199 98 -332t263 -133q164 0 254 88.5t90 244.5h189l2 -6v-11q0 -198 -144 -332q-148 -138 -391 -138 q-247 0 -402.5 175t-155.5 444z" />
+<glyph unicode="D" horiz-adv-x="1349" d="M180 0v1456h447q286 0 459 -175.5t173 -453.5v-199q0 -279 -173 -453.5t-459 -174.5h-447zM377 154h250q202 0 318.5 133t116.5 341v201q0 206 -116.5 339t-318.5 133h-250v-1147z" />
+<glyph unicode="E" horiz-adv-x="1197" d="M180 0v1456h955v-155h-758v-471h667v-155h-667v-521h769v-154h-966z" />
+<glyph unicode="F" horiz-adv-x="1193" d="M180 0v1456h963v-155h-766v-502h664v-155h-664v-644h-197z" />
+<glyph unicode="G" horiz-adv-x="1396" d="M120 578v300q0 265 159 432t410 167q250 0 393 -123t146 -317l-2 -6h-188q-9 127 -96.5 209t-252.5 82q-167 0 -269 -125t-102 -317v-302q0 -194 114 -319.5t290 -125.5q124 0 203 33t113 75v331h-319v155h516v-534q-52 -80 -180.5 -147t-332.5 -67q-261 0 -431.5 167 t-170.5 432z" />
+<glyph unicode="H" horiz-adv-x="1461" d="M180 0v1456h197v-658h707v658h197v-1456h-197v643h-707v-643h-197z" />
+<glyph unicode="I" horiz-adv-x="579" d="M190 0v1456h198v-1456h-198z" />
+<glyph unicode="J" horiz-adv-x="1130" d="M66 395l2 6h189q0 -135 68.5 -201.5t193.5 -66.5q109 0 178 73.5t69 196.5v1053h197v-1053q0 -195 -123.5 -309.5t-320.5 -114.5q-210 0 -334 107q-119 102 -119 293v16z" />
+<glyph unicode="K" horiz-adv-x="1317" d="M180 0v1456h197v-644h152l521 644h218l3 -5l-565 -699l606 -747l-3 -5h-235l-527 657h-170v-657h-197z" />
+<glyph unicode="L" horiz-adv-x="1106" d="M180 0v1456h197v-1302h689v-154h-886z" />
+<glyph unicode="M" horiz-adv-x="1799" d="M180 0v1456h252l464 -1183h6l464 1183h252v-1456h-197v576l20 592l-5 1l-472 -1169h-131l-470 1166l-5 -1l19 -589v-576h-197z" />
+<glyph unicode="N" horiz-adv-x="1461" d="M180 0v1456h197l701 -1124l6 2v1122h197v-1456h-197l-701 1126l-6 -2v-1124h-197z" />
+<glyph unicode="O" horiz-adv-x="1396" d="M113 598v259q0 266 159.5 443t414.5 177q264 0 429.5 -176.5t165.5 -443.5v-259q0 -267 -165.5 -443t-429.5 -176q-255 0 -414.5 176t-159.5 443zM310 598q0 -202 102.5 -330t274.5 -128q183 0 290.5 127.5t107.5 330.5v261q0 200 -108 328t-290 128q-172 0 -274.5 -128 t-102.5 -328v-261z" />
+<glyph unicode="P" horiz-adv-x="1312" d="M180 0v1456h557q233 0 362 -120t129 -316q0 -199 -129 -317.5t-362 -118.5h-360v-584h-197zM377 738h360q148 0 221 79.5t73 200.5t-73.5 202t-220.5 81h-360v-563z" />
+<glyph unicode="Q" horiz-adv-x="1396" d="M113 598v259q0 266 159.5 443t414.5 177q264 0 429.5 -176.5t165.5 -443.5v-259q0 -142 -50 -263t-140 -205l247 -233l-135 -129l-276 257q-56 -23 -116.5 -34.5t-124.5 -11.5q-255 0 -414.5 176t-159.5 443zM310 598q0 -202 102.5 -330t274.5 -128q183 0 290.5 127.5 t107.5 330.5v261q0 200 -108 328t-290 128q-172 0 -274.5 -128t-102.5 -328v-261z" />
+<glyph unicode="R" horiz-adv-x="1357" d="M180 0v1455h527q239 0 365 -106t126 -308q0 -112 -58.5 -195t-170.5 -132q120 -39 172.5 -126.5t52.5 -216.5v-137q0 -68 15 -122t52 -88v-24h-203q-39 34 -50 100t-11 136v133q0 118 -69 190t-185 72h-366v-631h-197zM377 786h310q167 0 240.5 63.5t73.5 193.5 q0 123 -71.5 190.5t-222.5 67.5h-330v-515z" />
+<glyph unicode="S" horiz-adv-x="1277" d="M102 413l2 6h188q0 -140 103 -213t255 -73q149 0 236 63t87 171q0 100 -75 167.5t-266 113.5q-231 55 -360.5 162t-129.5 269q0 170 139.5 284t361.5 114q239 0 381 -131q137 -127 136 -292v-12l-2 -6h-188q0 128 -84.5 207t-242.5 79q-147 0 -225.5 -66.5t-78.5 -173.5 q0 -95 85 -158.5t276 -111.5q230 -57 350 -168t120 -275q0 -176 -144 -283t-376 -107q-218 0 -386 118q-163 115 -162 305v11z" />
+<glyph unicode="T" horiz-adv-x="1200" d="M34 1301v155h1132v-155h-468v-1301h-197v1301h-467z" />
+<glyph unicode="U" horiz-adv-x="1386" d="M147 469v987h197v-987q0 -165 94 -250.5t248 -85.5q162 0 261.5 85.5t99.5 250.5v987h197v-987q0 -238 -154.5 -364t-403.5 -126q-240 0 -389.5 126.5t-149.5 363.5z" />
+<glyph unicode="V" horiz-adv-x="1295" d="M22 1456h214l376 -1094l33 -115h6l33 115l376 1094h213l-541 -1456h-169z" />
+<glyph unicode="W" horiz-adv-x="1809" d="M54 1456h196l222 -952l27 -182l6 -1l39 183l267 952h174l269 -952l40 -187h6l29 187l217 952h197l-351 -1456h-176l-287 1010l-26 131h-6l-25 -131l-292 -1010h-176z" />
+<glyph unicode="X" horiz-adv-x="1295" d="M66 0l472 734l-462 722h236l338 -568l340 568h238l-462 -722l472 -734h-235l-349 578l-350 -578h-238z" />
+<glyph unicode="Y" horiz-adv-x="1250" d="M20 1456h225l380 -740l380 740h225l-511 -944v-512h-196v525z" />
+<glyph unicode="Z" horiz-adv-x="1225" d="M97 0v146l778 1155h-767v155h992v-141l-781 -1161h814v-154h-1036z" />
+<glyph unicode="[" horiz-adv-x="552" d="M143 -312v1976h385v-155h-188v-1666h188v-155h-385z" />
+<glyph unicode="\" horiz-adv-x="846" d="M39 1456h186l608 -1581h-186z" />
+<glyph unicode="]" horiz-adv-x="552" d="M11 -157h189v1666h-189v155h386v-1976h-386v155z" />
+<glyph unicode="^" horiz-adv-x="856" d="M61 729l299 727h134l298 -727h-181l-166 419l-16 70h-6l-16 -70l-163 -419h-183z" />
+<glyph unicode="_" horiz-adv-x="931" d="M4 0h923v-154h-923v154z" />
+<glyph unicode="`" horiz-adv-x="641" d="M82 1471l3 6h230l175 -266h-158z" />
+<glyph unicode="a" horiz-adv-x="1126" d="M106 304q0 155 125.5 242.5t340.5 87.5h214v107q0 95 -58 150.5t-164 55.5q-95 0 -154.5 -48.5t-59.5 -116.5h-188l-2 6v11q0 111 112 205q118 98 303 98q184 0 296 -93.5t112 -269.5v-521q0 -58 6 -112t22 -106h-203q-10 49 -15.5 86.5t-6.5 75.5q-55 -78 -143.5 -130.5 t-190.5 -52.5q-169 0 -257.5 86.5t-88.5 238.5zM303 300q0 -72 45 -114t133 -42q107 0 193 55t112 126v176h-221q-119 0 -190.5 -60t-71.5 -141z" />
+<glyph unicode="b" d="M143 0v1560h197v-606q51 72 126.5 110t176.5 38q200 0 312 -160t112 -421v-21q0 -234 -112.5 -377.5t-309.5 -143.5q-107 0 -186 41.5t-131 122.5l-24 -143h-161zM340 309q38 -80 99.5 -125t155.5 -45q139 0 207 99t68 262v21q0 186 -68.5 303.5t-208.5 117.5 q-91 0 -153.5 -44.5t-99.5 -119.5v-469z" />
+<glyph unicode="c" horiz-adv-x="1087" d="M97 520v42q0 231 125.5 385.5t360.5 154.5q191 0 311 -112q117 -108 116 -265v-10l-2 -6h-178q0 99 -70 168.5t-177 69.5q-155 0 -221.5 -111.5t-66.5 -273.5v-42q0 -166 66 -276.5t222 -110.5q98 0 172.5 60.5t74.5 148.5h177l2 -6v-10q-1 -134 -125 -238 q-130 -108 -301 -109q-236 0 -361 154t-125 387z" />
+<glyph unicode="d" d="M98 500v21q0 261 111.5 421t312.5 160q95 0 168.5 -35t125.5 -102v595h197v-1560h-161l-23 133q-53 -76 -130 -115t-179 -39q-198 0 -310 143.5t-112 377.5zM295 500q0 -164 67 -262.5t208 -98.5q88 0 148 40t98 112v505q-38 67 -98.5 106.5t-145.5 39.5 q-142 0 -209.5 -117t-67.5 -304v-21z" />
+<glyph unicode="e" horiz-adv-x="1083" d="M99 520v44q0 231 137.5 384.5t325.5 153.5q219 0 331 -132t112 -352v-123h-702l-3 -5q3 -156 79 -256.5t213 -100.5q100 0 175.5 28.5t130.5 78.5l77 -128q-58 -57 -153 -95t-230 -38q-226 0 -359.5 150.5t-133.5 390.5zM307 654l2 -5h499v26q0 116 -62 194t-184 78 q-99 0 -169 -83.5t-86 -209.5z" />
+<glyph unicode="f" horiz-adv-x="707" d="M56 936v146h169v137q0 173 90.5 267.5t252.5 94.5q34 0 68.5 -5.5t76.5 -15.5l-24 -150q-18 4 -43.5 7t-53.5 3q-86 0 -128 -51.5t-42 -149.5v-137h225v-146h-225v-936h-197v936h-169z" />
+<glyph unicode="g" d="M100 500v21q0 261 114 421t315 160q103 0 181 -41.5t130 -119.5l24 141h157v-1088q0 -208 -121 -319.5t-349 -111.5q-78 0 -168.5 21.5t-159.5 58.5l50 153q53 -30 128 -48.5t148 -18.5q144 0 209.5 65.5t65.5 199.5v122q-53 -68 -127 -102.5t-170 -34.5q-199 0 -313 144 t-114 377zM297 500q0 -163 69 -262t210 -99q89 0 149 40.5t99 114.5v498q-38 69 -99 109.5t-147 40.5q-141 0 -211 -118t-70 -303v-21z" />
+<glyph unicode="h" d="M143 0v1560h197v-623q56 78 137.5 121.5t180.5 43.5q173 0 269.5 -104t96.5 -320v-678h-197v680q0 134 -57.5 198t-171.5 64q-82 0 -148.5 -38.5t-109.5 -104.5v-799h-197z" />
+<glyph unicode="i" horiz-adv-x="516" d="M159 0v1082h197v-1082h-197zM159 1359v201h197v-201h-197z" />
+<glyph unicode="j" horiz-adv-x="530" d="M-66 -419l14 155q14 -5 40 -8.5t43 -3.5q65 0 103.5 44t38.5 143v1171h197v-1171q0 -167 -86 -257.5t-239 -90.5q-31 0 -56.5 4.5t-54.5 13.5zM167 1363v197h197v-197h-197z" />
+<glyph unicode="k" horiz-adv-x="1050" d="M144 0v1560h197v-904h126l296 426h236l-370 -492l423 -590h-232l-351 499h-128v-499h-197z" />
+<glyph unicode="l" horiz-adv-x="516" d="M159 0v1560h197v-1560h-197z" />
+<glyph unicode="m" horiz-adv-x="1790" d="M143 0v1082h176l14 -142q53 77 134.5 119.5t189.5 42.5t185.5 -50t116.5 -150q51 92 135 146t196 54q165 0 261 -113.5t96 -341.5v-647h-197v649q0 160 -55 226.5t-164 66.5q-101 0 -163.5 -70t-73.5 -177v-8v-687h-198v649q0 152 -56.5 222.5t-162.5 70.5 q-90 0 -148 -37t-89 -104v-801h-197z" />
+<glyph unicode="n" d="M143 0v1082h176l14 -161q54 86 135.5 133.5t185.5 47.5q175 0 271 -102.5t96 -316.5v-683h-197v679q0 143 -56.5 203t-172.5 60q-85 0 -150.5 -41t-104.5 -112v-789h-197z" />
+<glyph unicode="o" d="M97 529v22q0 240 130 395.5t353 155.5q225 0 355.5 -155t130.5 -396v-22q0 -242 -130 -396t-354 -154t-354.5 154.5t-130.5 395.5zM294 529q0 -172 72.5 -284t215.5 -112q141 0 214 112t73 284v22q0 170 -73.5 283t-215.5 113q-141 0 -213.5 -113t-72.5 -283v-22z" />
+<glyph unicode="p" d="M143 -416v1498h151l31 -140q53 78 132 119t184 41q201 0 312.5 -159.5t111.5 -421.5v-21q0 -234 -112 -377.5t-309 -143.5q-100 0 -175.5 33.5t-128.5 100.5v-529h-197zM340 275q37 -67 97 -104.5t147 -37.5q140 0 212 102.5t72 264.5v21q0 184 -72.5 302.5t-213.5 118.5 q-85 0 -145 -38.5t-97 -105.5v-523z" />
+<glyph unicode="q" d="M98 500v21q0 261 111.5 421t312.5 160q99 0 174 -37.5t127 -109.5l29 127h150v-1498h-197v518q-52 -61 -123 -92t-162 -31q-198 0 -310 143.5t-112 377.5zM295 500q0 -164 67.5 -265.5t207.5 -101.5q81 0 138.5 36t96.5 101v546q-39 61 -96.5 96t-136.5 35 q-141 0 -209 -119.5t-68 -306.5v-21z" />
+<glyph unicode="r" horiz-adv-x="702" d="M143 0v1082h176l19 -158q46 84 113.5 131t155.5 47q22 0 42 -3.5t33 -7.5l-27 -183l-101 6q-78 0 -131.5 -37t-82.5 -104v-773h-197z" />
+<glyph unicode="s" horiz-adv-x="1071" d="M109 329l2 6h188q5 -105 78 -153.5t171 -48.5q105 0 164.5 42.5t59.5 111.5q0 65 -49.5 107t-187.5 73q-197 43 -296.5 116.5t-99.5 200.5q0 132 112 225t292 93q189 0 301 -97q107 -93 106 -224v-12l-2 -6h-188q0 71 -59.5 127.5t-157.5 56.5q-105 0 -156 -46t-51 -111 q0 -64 45 -101t183 -66q205 -44 305 -119.5t100 -202.5q0 -144 -116.5 -233t-304.5 -89q-207 0 -326 105q-113 100 -113 232v13z" />
+<glyph unicode="t" horiz-adv-x="708" d="M34 936v146h172v261h197v-261h205v-146h-205v-657q0 -76 31.5 -107t83.5 -31q17 0 37.5 4t36.5 10l26 -135q-22 -18 -64.5 -29.5t-85.5 -11.5q-120 0 -191 72.5t-71 227.5v657h-172z" />
+<glyph unicode="u" d="M139 444v638h197v-640q0 -173 51 -238t159 -65q105 0 173.5 42.5t103.5 120.5v780h197v-1082h-177l-13 160q-51 -87 -131 -134t-185 -47q-177 0 -276 113t-99 352z" />
+<glyph unicode="v" horiz-adv-x="1030" d="M46 1082h202l256 -763l17 -76h6l19 76l249 763h201l-398 -1082h-149z" />
+<glyph unicode="w" horiz-adv-x="1550" d="M45 1082h196l179 -688l23 -131h6l28 131l216 688h158l217 -688l31 -146h6l29 146l170 688h196l-314 -1082h-159l-209 659l-45 184l-6 -1l-43 -183l-206 -659h-159z" />
+<glyph unicode="x" horiz-adv-x="1030" d="M46 0l361 547l-351 535h227l227 -399l230 399h230l-351 -535l361 -547h-226l-240 409l-240 -409h-228z" />
+<glyph unicode="y" horiz-adv-x="1030" d="M26 1082h220l228 -681l35 -136h6l266 817h219l-455 -1248q-41 -109 -117.5 -190t-206.5 -81q-24 0 -61 5.5t-57 10.5l20 155q-6 1 35.5 -2t52.5 -3q63 0 103 56t67 124l47 113z" />
+<glyph unicode="z" horiz-adv-x="1030" d="M94 0v138l585 788h-578v156h819v-134l-591 -794h625v-154h-860z" />
+<glyph unicode="{" horiz-adv-x="696" d="M63 543v147q106 0 157.5 61.5t51.5 174.5v206q0 171 82 290.5t277 174.5l40 -117q-110 -35 -156 -125.5t-46 -222.5v-206q0 -105 -42.5 -185t-127.5 -125q85 -46 127.5 -126.5t42.5 -183.5v-205q0 -132 46 -221.5t156 -125.5l-40 -118q-195 55 -277 175t-82 290v205 q0 112 -51.5 174.5t-157.5 62.5z" />
+<glyph unicode="|" horiz-adv-x="507" d="M175 -270v1726h158v-1726h-158z" />
+<glyph unicode="}" horiz-adv-x="696" d="M21 -246q109 36 156 125.5t47 221.5v205q0 107 45 187t139 123q-94 41 -139 121t-45 189v206q0 132 -47 222.5t-156 125.5l41 117q194 -55 276.5 -174.5t82.5 -290.5v-206q0 -113 50.5 -174.5t158.5 -61.5v-147q-108 0 -158.5 -62.5t-50.5 -174.5v-205q0 -170 -82.5 -290 t-276.5 -175z" />
+<glyph unicode="~" horiz-adv-x="1391" d="M128 474q0 136 85.5 232.5t217.5 96.5q88 0 163 -34.5t160 -104.5q58 -51 106 -74t100 -23q66 0 114.5 57t48.5 134l141 -18q0 -137 -87 -238t-217 -101q-90 0 -163.5 33t-158.5 107q-59 48 -108 72t-99 24q-67 0 -114.5 -53t-47.5 -128z" />
+<glyph unicode="¡" horiz-adv-x="507" d="M144 -374v978h197v-978h-197zM144 876v206h197v-206h-197z" />
+<glyph unicode="¢" horiz-adv-x="1122" d="M107 520v42q0 199 95 344.5t276 183.5v228h198v-223q157 -24 252.5 -130.5t92.5 -250.5l-2 -5h-179q0 99 -70 168.5t-177 69.5q-155 0 -221.5 -111.5t-66.5 -273.5v-42q0 -166 66 -276.5t222 -110.5q98 0 172.5 60.5t74.5 148.5h178l3 -6q3 -122 -98 -223t-247 -126v-232 h-198v236q-182 36 -276.5 182t-94.5 347z" />
+<glyph unicode="£" horiz-adv-x="1194" d="M70 615v155h158l-10 270q0 204 112 320.5t300 116.5q200 0 310 -104.5t106 -276.5l-2 -6h-190q0 118 -63 175t-161 57q-99 0 -157 -74.5t-58 -207.5l10 -270h418v-155h-413l6 -149q0 -90 -15.5 -171.5t-44.5 -140.5h735l-1 -154h-976v154h10q48 13 72 111t24 201l-6 149 h-164z" />
+<glyph unicode="¤" horiz-adv-x="1456" d="M104 112l138 140q-50 76 -76.5 166.5t-26.5 189.5q0 102 28.5 196t82.5 172l-146 149l139 139l143 -146q74 55 163 85.5t185 30.5q97 0 186 -31t164 -87l146 149l140 -140l-150 -153q52 -78 80.5 -170.5t28.5 -193.5q0 -98 -26.5 -187.5t-74.5 -165.5l142 -143l-140 -139 l-133 135q-77 -62 -169.5 -95t-193.5 -33t-193.5 32.5t-167.5 93.5l-130 -132zM321 608q0 -188 120.5 -320.5t292.5 -132.5q170 0 290.5 132.5t120.5 320.5q0 186 -120.5 318t-290.5 132q-172 0 -292.5 -132t-120.5 -318z" />
+<glyph unicode="¥" horiz-adv-x="1243" d="M30 1456h226l359 -663l360 663h224l-418 -718h312v-155h-383v-135h383v-155h-383v-293h-197v293h-375v155h375v135h-375v155h311z" />
+<glyph unicode="¦" horiz-adv-x="499" d="M145 -270v792h197v-792h-197zM145 698v758h197v-758h-197z" />
+<glyph unicode="§" horiz-adv-x="1259" d="M94 551q0 91 47 161.5t134 111.5q-68 50 -102 119.5t-34 166.5q0 166 134 266.5t358 100.5q233 0 363 -111.5t126 -313.5l-3 -6h-188q0 118 -79 197t-219 79q-145 0 -220 -59.5t-75 -150.5q0 -99 67 -148.5t278 -107.5q244 -69 355.5 -159.5t111.5 -265.5q0 -94 -48 -164 t-135 -110q69 -51 104 -119t35 -166q0 -172 -133 -269.5t-358 -97.5q-221 0 -372 102.5t-146 322.5l2 6l188 2q0 -143 96.5 -210.5t231.5 -67.5q137 0 215.5 59.5t78.5 150.5t-72 141.5t-276 113.5q-239 63 -352 156t-113 270zM291 553q0 -100 68 -151.5t278 -110.5 q56 -17 93 -28t70 -23q72 20 112 69.5t40 118.5q0 91 -73.5 144.5t-275.5 116.5q-47 12 -88.5 24.5t-77.5 27.5q-73 -19 -109.5 -69t-36.5 -119z" />
+<glyph unicode="¨" horiz-adv-x="1021" d="M170 1256v200h219v-200h-219zM640 1256v200h219v-200h-219z" />
+<glyph unicode="©" horiz-adv-x="1604" d="M88 729q0 315 207 531t503 216q295 0 502 -216t207 -531q0 -316 -207.5 -533t-501.5 -217q-296 0 -503 217t-207 533zM209 729q0 -265 171.5 -447t417.5 -182q245 0 417 182t172 447q0 263 -172 444t-417 181q-246 0 -417.5 -181t-171.5 -444zM436 669v119q0 173 94 280 t254 107q157 0 245.5 -79t84.5 -228l-2 -6h-146q0 95 -45.5 138.5t-136.5 43.5q-94 0 -145 -70.5t-51 -184.5v-120q0 -117 51 -187t145 -70q91 0 136 43t45 141h146l2 -6q4 -151 -84 -229.5t-245 -78.5q-160 0 -254 106.5t-94 280.5z" />
+<glyph unicode="ª" horiz-adv-x="917" d="M120 920q0 110 84.5 170t245.5 60h139v52q0 63 -30 97t-88 34q-67 0 -103.5 -27t-36.5 -76l-162 13l-1 6q-6 98 78.5 163t224.5 65q134 0 212 -71t78 -205v-314q0 -50 6 -94t20 -87h-174q-8 21 -13 45t-8 50q-33 -47 -89.5 -78t-133.5 -31q-119 0 -184 61t-65 167z M293 924q0 -45 29 -69t89 -24q51 0 105.5 30t72.5 65v110h-138q-75 0 -116.5 -33t-41.5 -79z" />
+<glyph unicode="«" horiz-adv-x="966" d="M98 507v19l295 389h148l-255 -399l255 -398h-148zM432 507v19l295 389h148l-255 -399l255 -398h-148z" />
+<glyph unicode="¬" horiz-adv-x="1137" d="M127 637v165h835v-427h-198v262h-637z" />
+<glyph unicode="­" horiz-adv-x="561" d="M35 538v154h490v-154h-490z" />
+<glyph unicode="®" horiz-adv-x="1604" d="M88 729q0 315 207 531t503 216q295 0 502 -216t207 -531q0 -316 -207.5 -533t-501.5 -217q-296 0 -503 217t-207 533zM209 729q0 -266 171.5 -447.5t417.5 -181.5q244 0 416 182t172 447q0 264 -171.5 444.5t-416.5 180.5q-246 0 -417.5 -180.5t-171.5 -444.5zM504 316 v850h280q152 0 238.5 -65.5t86.5 -191.5q0 -62 -33 -109t-96 -78q66 -26 95.5 -79t29.5 -128v-56q0 -41 3.5 -73.5t13.5 -53.5v-16h-153q-9 21 -11 61.5t-2 82.5v54q0 72 -33.5 106t-110.5 34h-159v-338h-149zM653 784h152q65 1 110.5 32.5t45.5 87.5q0 73 -39.5 102.5 t-137.5 29.5h-131v-252z" />
+<glyph unicode="¯" horiz-adv-x="950" d="M123 1310v146h721v-146h-721z" />
+<glyph unicode="°" horiz-adv-x="763" d="M128 1216q0 106 76 183.5t181 77.5q103 0 177.5 -77.5t74.5 -183.5q0 -108 -74 -182.5t-178 -74.5q-106 0 -181.5 74.5t-75.5 182.5zM259 1216q0 -55 36.5 -91t89.5 -36q52 0 87.5 36t35.5 91t-36 92.5t-87 37.5q-53 0 -89.5 -37.5t-36.5 -92.5z" />
+<glyph unicode="±" horiz-adv-x="1097" d="M99 702v154h381v411h177v-411h358v-154h-358v-413h-177v413h-381zM136 4v155h835v-155h-835z" />
+<glyph unicode="²" horiz-adv-x="868" d="M119 1240q-6 99 78 169t225 70q135 0 211 -64t76 -180q0 -80 -44.5 -136t-160.5 -161l-153 -135l2 -6h361v-130h-592v130l302 262q69 60 91 97.5t22 79.5q0 50 -28.5 81t-86.5 31q-67 0 -103.5 -32t-36.5 -82h-161z" />
+<glyph unicode="³" horiz-adv-x="876" d="M112 882l1 6h163q0 -46 37.5 -74.5t100.5 -28.5q72 0 114 29.5t42 77.5q0 62 -36.5 90.5t-109.5 28.5h-132v126h132q67 0 99.5 28.5t32.5 80.5q0 43 -36.5 72t-105.5 29q-56 0 -90.5 -24t-34.5 -64h-162l-2 6q-6 94 78.5 153.5t210.5 59.5q145 0 229 -59.5t84 -169.5 q0 -55 -35.5 -100.5t-97.5 -71.5q70 -23 108 -71t38 -116q0 -111 -90 -173t-236 -62q-127 0 -217.5 58t-84.5 169z" />
+<glyph unicode="´" horiz-adv-x="654" d="M131 1211l185 266h230l2 -6l-270 -260h-147z" />
+<glyph unicode="µ" d="M153 -416v1498h196v-642q2 -178 57.5 -242.5t155.5 -64.5q98 0 158.5 36t92.5 106v807h197v-1082h-177l-9 108q-44 -63 -107.5 -96t-146.5 -33q-72 0 -126.5 16.5t-94.5 51.5v-463h-196z" />
+<glyph unicode="¶" horiz-adv-x="1006" d="M63 988q0 207 129.5 337.5t362.5 130.5h281v-1456h-197v520h-84q-233 0 -362.5 129.5t-129.5 338.5z" />
+<glyph unicode="·" horiz-adv-x="540" d="M161 624v212h198v-212h-198z" />
+<glyph unicode="¸" horiz-adv-x="509" d="M119 -326q72 0 116 24.5t44 73.5q0 48 -36 67t-123 26l32 135h140l-12 -52q65 -11 108 -52t43 -121q0 -96 -79 -153t-226 -57z" />
+<glyph unicode="¹" horiz-adv-x="557" d="M95 1320v134l301 23v-812h-174v655h-127z" />
+<glyph unicode="º" horiz-adv-x="933" d="M120 1025v117q0 148 94 241.5t251 93.5q158 0 252 -93.5t94 -241.5v-117q0 -149 -93.5 -241.5t-250.5 -92.5q-158 0 -252.5 92.5t-94.5 241.5zM293 1025q0 -88 44 -140.5t130 -52.5q83 0 127.5 53t44.5 140v117q0 84 -45 137.5t-129 53.5t-128 -53.5t-44 -137.5v-117z " />
+<glyph unicode="»" horiz-adv-x="966" d="M110 152l255 398l-255 399h148l295 -389v-19l-295 -389h-148zM456 152l255 398l-255 399h148l295 -389v-19l-295 -389h-148z" />
+<glyph unicode="¼" horiz-adv-x="1595" d="M184 1319v134l301 23v-812h-174v655h-127zM339 185l711 1138l109 -67l-711 -1138zM785 254l422 547h173v-519h126v-130h-126v-152h-170v152h-417zM967 282h243v310l-6 1l-13 -22z" />
+<glyph unicode="½" horiz-adv-x="1708" d="M184 1319v134l301 23v-812h-174v655h-127zM352 185l711 1138l109 -67l-711 -1138zM930 573q-6 99 78 169t225 70q135 0 211 -64t76 -180q0 -80 -44.5 -136t-160.5 -161l-153 -135l2 -6h361v-130h-592v130l302 262q69 60 91 97.5t22 79.5q0 50 -28.5 81t-86.5 31 q-67 0 -103.5 -32t-36.5 -82h-161z" />
+<glyph unicode="¾" horiz-adv-x="1781" d="M128 883l1 6h163q0 -46 37.5 -74.5t100.5 -28.5q72 0 114 29.5t42 77.5q0 62 -36.5 90.5t-109.5 28.5h-132v126h132q67 0 99.5 28.5t32.5 80.5q0 43 -36.5 72t-105.5 29q-56 0 -90.5 -24t-34.5 -64h-162l-2 6q-6 94 78.5 153.5t210.5 59.5q145 0 229 -59.5t84 -169.5 q0 -55 -35.5 -100.5t-97.5 -71.5q70 -23 108 -71t38 -116q0 -111 -90 -173t-236 -62q-127 0 -217.5 58t-84.5 169zM522 185l711 1138l109 -67l-711 -1138zM974 254l422 547h173v-519h126v-130h-126v-152h-170v152h-417zM1156 282h243v310l-6 1l-13 -22z" />
+<glyph unicode="¿" horiz-adv-x="1013" d="M114 -13q0 127 70 233.5t187 220.5q53 53 65 96t12 135h197q-2 -146 -26 -202t-125 -147q-100 -118 -141.5 -181t-41.5 -150q0 -106 56 -163t162 -57q90 0 154.5 49.5t64.5 145.5h188l3 -6q2 -161 -114.5 -258t-295.5 -97q-198 0 -306.5 100.5t-108.5 280.5zM441 874v209 h206v-209h-206z" />
+<glyph unicode="À" horiz-adv-x="1326" d="M20 0l563 1456h169l554 -1456h-201l-136 375h-610l-138 -375h-201zM378 1820l3 6h230l175 -266h-158zM420 540h490l-240 663h-6z" />
+<glyph unicode="Á" horiz-adv-x="1326" d="M20 0l563 1456h169l554 -1456h-201l-136 375h-610l-138 -375h-201zM420 540h490l-240 663h-6zM613 1556l185 266h230l2 -6l-270 -260h-147z" />
+<glyph unicode="Â" horiz-adv-x="1326" d="M20 0l563 1456h169l554 -1456h-201l-136 375h-610l-138 -375h-201zM356 1601v26l246 237h120l248 -238v-25h-161l-147 148l-146 -148h-160zM420 540h490l-240 663h-6z" />
+<glyph unicode="Ã" horiz-adv-x="1326" d="M20 0l563 1456h169l554 -1456h-201l-136 375h-610l-138 -375h-201zM316 1628q0 93 59 161.5t150 68.5q56 0 140 -47t136 -47q41 0 71 32.5t30 79.5l108 -32q0 -94 -59.5 -159t-149.5 -65q-71 0 -148 46.5t-128 46.5q-43 0 -72 -32.5t-29 -78.5zM420 540h490l-240 663h-6z " />
+<glyph unicode="Ä" horiz-adv-x="1326" d="M20 0l563 1456h169l554 -1456h-201l-136 375h-610l-138 -375h-201zM319 1605v200h219v-200h-219zM420 540h490l-240 663h-6zM789 1605v200h219v-200h-219z" />
+<glyph unicode="Å" horiz-adv-x="1326" d="M20 0l563 1456h169l554 -1456h-201l-136 375h-610l-138 -375h-201zM420 540h490l-240 663h-6zM457 1734q0 84 60.5 141t147.5 57q85 0 145 -56.5t60 -141.5q0 -86 -59.5 -140t-145.5 -54q-87 0 -147.5 54t-60.5 140zM560 1734q0 -43 31 -73.5t74 -30.5q42 0 72 29.5 t30 74.5t-30 76t-72 31q-43 0 -74 -31t-31 -76z" />
+<glyph unicode="Æ" horiz-adv-x="1922" d="M-20 0l880 1456h967v-155h-691l20 -466h590v-155h-584l22 -526h705v-154h-895l-15 350h-557l-202 -350h-240zM525 529h447l-31 710l-5 2z" />
+<glyph unicode="Ç" horiz-adv-x="1297" d="M118 598v259q0 269 155.5 444.5t402.5 175.5t393 -131.5t142 -348.5l-2 -6h-189q0 153 -90 242t-254 89q-165 0 -263 -133t-98 -330v-261q0 -199 98 -332t263 -133q164 0 254 88.5t90 244.5h189l2 -6q4 -205 -144 -343t-391 -138q-247 0 -402.5 175t-155.5 444zM581 -334 q72 0 116 24.5t44 73.5q0 48 -36 67t-123 26l32 135h140l-12 -52q65 -11 108 -52t43 -121q0 -96 -79 -153t-226 -57z" />
+<glyph unicode="È" horiz-adv-x="1197" d="M180 0v1456h955v-155h-758v-471h667v-155h-667v-521h769v-154h-966zM303 1820l3 6h230l175 -266h-158z" />
+<glyph unicode="É" horiz-adv-x="1197" d="M180 0v1456h955v-155h-758v-471h667v-155h-667v-521h769v-154h-966zM538 1556l185 266h230l2 -6l-270 -260h-147z" />
+<glyph unicode="Ê" horiz-adv-x="1197" d="M180 0v1456h955v-155h-758v-471h667v-155h-667v-521h769v-154h-966zM322 1601v26l246 237h120l248 -238v-25h-161l-147 148l-146 -148h-160z" />
+<glyph unicode="Ë" horiz-adv-x="1197" d="M180 0v1456h955v-155h-758v-471h667v-155h-667v-521h769v-154h-966zM284 1605v200h219v-200h-219zM754 1605v200h219v-200h-219z" />
+<glyph unicode="Ì" horiz-adv-x="579" d="M-34 1820l3 6h230l175 -266h-158zM190 0v1456h198v-1456h-198z" />
+<glyph unicode="Í" horiz-adv-x="579" d="M190 0v1456h198v-1456h-198zM199 1556l185 266h230l2 -6l-270 -260h-147z" />
+<glyph unicode="Î" horiz-adv-x="579" d="M-15 1601v26l246 237h120l248 -238v-25h-161l-147 148l-146 -148h-160zM190 0v1456h198v-1456h-198z" />
+<glyph unicode="Ï" horiz-adv-x="579" d="M-53 1605v200h219v-200h-219zM190 0v1456h198v-1456h-198zM417 1605v200h219v-200h-219z" />
+<glyph unicode="Ð" horiz-adv-x="1379" d="M42 663v155h168v638h447q286 0 459 -175.5t173 -453.5v-199q0 -279 -173 -453.5t-459 -174.5h-447v663h-168zM407 154h250q202 0 318.5 133t116.5 341v201q0 206 -116.5 339t-318.5 133h-250v-483h276v-155h-276v-509z" />
+<glyph unicode="Ñ" horiz-adv-x="1461" d="M180 0v1456h197l701 -1124l6 2v1122h197v-1456h-197l-701 1126l-6 -2v-1124h-197zM381 1628q0 93 59 161.5t150 68.5q56 0 140 -47t136 -47q41 0 71 32.5t30 79.5l108 -32q0 -94 -59.5 -159t-149.5 -65q-71 0 -148 46.5t-128 46.5q-43 0 -72 -32.5t-29 -78.5z" />
+<glyph unicode="Ò" horiz-adv-x="1396" d="M113 598v259q0 266 159.5 443t414.5 177q264 0 429.5 -176.5t165.5 -443.5v-259q0 -267 -165.5 -443t-429.5 -176q-255 0 -414.5 176t-159.5 443zM310 598q0 -202 102.5 -330t274.5 -128q183 0 290.5 127.5t107.5 330.5v261q0 200 -108 328t-290 128q-172 0 -274.5 -128 t-102.5 -328v-261zM373 1841l3 6h230l175 -266h-158z" />
+<glyph unicode="Ó" horiz-adv-x="1396" d="M113 598v259q0 266 159.5 443t414.5 177q264 0 429.5 -176.5t165.5 -443.5v-259q0 -267 -165.5 -443t-429.5 -176q-255 0 -414.5 176t-159.5 443zM310 598q0 -202 102.5 -330t274.5 -128q183 0 290.5 127.5t107.5 330.5v261q0 200 -108 328t-290 128q-172 0 -274.5 -128 t-102.5 -328v-261zM608 1577l185 266h230l2 -6l-270 -260h-147z" />
+<glyph unicode="Ô" horiz-adv-x="1396" d="M113 598v259q0 266 159.5 443t414.5 177q264 0 429.5 -176.5t165.5 -443.5v-259q0 -267 -165.5 -443t-429.5 -176q-255 0 -414.5 176t-159.5 443zM310 598q0 -202 102.5 -330t274.5 -128q183 0 290.5 127.5t107.5 330.5v261q0 200 -108 328t-290 128q-172 0 -274.5 -128 t-102.5 -328v-261zM392 1622v26l246 237h120l248 -238v-25h-161l-147 148l-146 -148h-160z" />
+<glyph unicode="Õ" horiz-adv-x="1396" d="M113 598v259q0 266 159.5 443t414.5 177q264 0 429.5 -176.5t165.5 -443.5v-259q0 -267 -165.5 -443t-429.5 -176q-255 0 -414.5 176t-159.5 443zM310 598q0 -202 102.5 -330t274.5 -128q183 0 290.5 127.5t107.5 330.5v261q0 200 -108 328t-290 128q-172 0 -274.5 -128 t-102.5 -328v-261zM351 1649q0 93 59 161.5t150 68.5q56 0 140 -47t136 -47q41 0 71 32.5t30 79.5l108 -32q0 -94 -59.5 -159t-149.5 -65q-71 0 -148 46.5t-128 46.5q-43 0 -72 -32.5t-29 -78.5z" />
+<glyph unicode="Ö" horiz-adv-x="1396" d="M113 598v259q0 266 159.5 443t414.5 177q264 0 429.5 -176.5t165.5 -443.5v-259q0 -267 -165.5 -443t-429.5 -176q-255 0 -414.5 176t-159.5 443zM310 598q0 -202 102.5 -330t274.5 -128q183 0 290.5 127.5t107.5 330.5v261q0 200 -108 328t-290 128q-172 0 -274.5 -128 t-102.5 -328v-261zM354 1626v200h219v-200h-219zM824 1626v200h219v-200h-219z" />
+<glyph unicode="×" horiz-adv-x="1096" d="M88 351l327 334l-327 334l126 126l326 -333l327 333l126 -126l-328 -334l328 -334l-126 -126l-327 332l-326 -332z" />
+<glyph unicode="Ø" horiz-adv-x="1396" d="M113 598v259q0 266 159.5 443t414.5 177q94 0 178.5 -25.5t156.5 -71.5l81 137h149l-132 -221q77 -84 119.5 -197t42.5 -242v-259q0 -267 -165.5 -443t-429.5 -176q-85 0 -160.5 20.5t-139.5 60.5l-91 -154h-149l139 234q-84 84 -128.5 202t-44.5 256zM310 598 q0 -85 19 -158t54 -125l6 -1l544 916q-50 41 -112 63t-134 22q-172 0 -274.5 -128t-102.5 -328v-261zM475 208q44 -34 97 -51t115 -17q183 0 290.5 127.5t107.5 330.5v261q0 75 -16.5 142t-46.5 117l-6 1z" />
+<glyph unicode="Ù" horiz-adv-x="1386" d="M147 469v987h197v-987q0 -165 94 -250.5t248 -85.5q162 0 261.5 85.5t99.5 250.5v987h197v-987q0 -238 -154.5 -364t-403.5 -126q-240 0 -389.5 126.5t-149.5 363.5zM372 1820l3 6h230l175 -266h-158z" />
+<glyph unicode="Ú" horiz-adv-x="1386" d="M147 469v987h197v-987q0 -165 94 -250.5t248 -85.5q162 0 261.5 85.5t99.5 250.5v987h197v-987q0 -238 -154.5 -364t-403.5 -126q-240 0 -389.5 126.5t-149.5 363.5zM607 1556l185 266h230l2 -6l-270 -260h-147z" />
+<glyph unicode="Û" horiz-adv-x="1386" d="M147 469v987h197v-987q0 -165 94 -250.5t248 -85.5q162 0 261.5 85.5t99.5 250.5v987h197v-987q0 -238 -154.5 -364t-403.5 -126q-240 0 -389.5 126.5t-149.5 363.5zM391 1601v26l246 237h120l248 -238v-25h-161l-147 148l-146 -148h-160z" />
+<glyph unicode="Ü" horiz-adv-x="1386" d="M147 469v987h197v-987q0 -165 94 -250.5t248 -85.5q162 0 261.5 85.5t99.5 250.5v987h197v-987q0 -238 -154.5 -364t-403.5 -126q-240 0 -389.5 126.5t-149.5 363.5zM353 1605v200h219v-200h-219zM823 1605v200h219v-200h-219z" />
+<glyph unicode="Ý" horiz-adv-x="1250" d="M20 1456h225l380 -740l380 740h225l-511 -944v-512h-196v525zM535 1555l185 266h230l2 -6l-270 -260h-147z" />
+<glyph unicode="Þ" horiz-adv-x="1209" d="M163 0v1456h197v-293h269q232 0 362 -118t130 -307q0 -190 -130 -307.5t-362 -117.5h-269v-313h-197zM360 467h269q147 0 220.5 78t73.5 191q0 114 -73.5 193.5t-220.5 79.5h-269v-542z" />
+<glyph unicode="ß" horiz-adv-x="1221" d="M137 0v1082q0 223 117.5 348t300.5 125q161 0 262 -86t101 -253q0 -118 -64.5 -228t-64.5 -167q0 -82 173.5 -224t173.5 -281q0 -167 -104.5 -252t-282.5 -85q-84 0 -172.5 20.5t-125.5 50.5l44 159q43 -28 108 -52t126 -24q108 0 159 47.5t51 125.5q0 84 -173.5 227.5 t-173.5 289.5q0 80 70.5 190.5t70.5 186.5q0 93 -51 147t-117 54q-104 0 -168 -83.5t-64 -235.5v-1082h-196z" />
+<glyph unicode="à" horiz-adv-x="1126" d="M106 304q0 155 125.5 242.5t340.5 87.5h214v107q0 95 -58 150.5t-164 55.5q-95 0 -154.5 -48.5t-59.5 -116.5h-188l-2 6q-6 118 111.5 216t303.5 98q184 0 296 -93.5t112 -269.5v-521q0 -58 6 -112t22 -106h-203q-10 49 -15.5 86.5t-6.5 75.5q-55 -78 -143.5 -130.5 t-190.5 -52.5q-169 0 -257.5 86.5t-88.5 238.5zM230 1498l3 6h230l175 -266h-158zM303 300q0 -72 45 -114t133 -42q107 0 193 55t112 126v176h-221q-119 0 -190.5 -60t-71.5 -141z" />
+<glyph unicode="á" horiz-adv-x="1126" d="M106 304q0 155 125.5 242.5t340.5 87.5h214v107q0 95 -58 150.5t-164 55.5q-95 0 -154.5 -48.5t-59.5 -116.5h-188l-2 6q-6 118 111.5 216t303.5 98q184 0 296 -93.5t112 -269.5v-521q0 -58 6 -112t22 -106h-203q-10 49 -15.5 86.5t-6.5 75.5q-55 -78 -143.5 -130.5 t-190.5 -52.5q-169 0 -257.5 86.5t-88.5 238.5zM303 300q0 -72 45 -114t133 -42q107 0 193 55t112 126v176h-221q-119 0 -190.5 -60t-71.5 -141zM465 1234l185 266h230l2 -6l-270 -260h-147z" />
+<glyph unicode="â" horiz-adv-x="1126" d="M106 304q0 155 125.5 242.5t340.5 87.5h214v107q0 95 -58 150.5t-164 55.5q-95 0 -154.5 -48.5t-59.5 -116.5h-188l-2 6q-6 118 111.5 216t303.5 98q184 0 296 -93.5t112 -269.5v-521q0 -58 6 -112t22 -106h-203q-10 49 -15.5 86.5t-6.5 75.5q-55 -78 -143.5 -130.5 t-190.5 -52.5q-169 0 -257.5 86.5t-88.5 238.5zM249 1279v26l246 237h120l248 -238v-25h-161l-147 148l-146 -148h-160zM303 300q0 -72 45 -114t133 -42q107 0 193 55t112 126v176h-221q-119 0 -190.5 -60t-71.5 -141z" />
+<glyph unicode="ã" horiz-adv-x="1126" d="M106 304q0 155 125.5 242.5t340.5 87.5h214v107q0 95 -58 150.5t-164 55.5q-95 0 -154.5 -48.5t-59.5 -116.5h-188l-2 6q-6 118 111.5 216t303.5 98q184 0 296 -93.5t112 -269.5v-521q0 -58 6 -112t22 -106h-203q-10 49 -15.5 86.5t-6.5 75.5q-55 -78 -143.5 -130.5 t-190.5 -52.5q-169 0 -257.5 86.5t-88.5 238.5zM208 1306q0 93 59 161.5t150 68.5q56 0 140 -47t136 -47q41 0 71 32.5t30 79.5l108 -32q0 -94 -59.5 -159t-149.5 -65q-71 0 -148 46.5t-128 46.5q-43 0 -72 -32.5t-29 -78.5zM303 300q0 -72 45 -114t133 -42q107 0 193 55 t112 126v176h-221q-119 0 -190.5 -60t-71.5 -141z" />
+<glyph unicode="ä" horiz-adv-x="1126" d="M106 304q0 155 125.5 242.5t340.5 87.5h214v107q0 95 -58 150.5t-164 55.5q-95 0 -154.5 -48.5t-59.5 -116.5h-188l-2 6q-6 118 111.5 216t303.5 98q184 0 296 -93.5t112 -269.5v-521q0 -58 6 -112t22 -106h-203q-10 49 -15.5 86.5t-6.5 75.5q-55 -78 -143.5 -130.5 t-190.5 -52.5q-169 0 -257.5 86.5t-88.5 238.5zM211 1283v200h219v-200h-219zM303 300q0 -72 45 -114t133 -42q107 0 193 55t112 126v176h-221q-119 0 -190.5 -60t-71.5 -141zM681 1283v200h219v-200h-219z" />
+<glyph unicode="å" horiz-adv-x="1126" d="M106 304q0 155 125.5 242.5t340.5 87.5h214v107q0 95 -58 150.5t-164 55.5q-95 0 -154.5 -48.5t-59.5 -116.5h-188l-2 6q-6 118 111.5 216t303.5 98q184 0 296 -93.5t112 -269.5v-521q0 -58 6 -112t22 -106h-203q-10 49 -15.5 86.5t-6.5 75.5q-55 -78 -143.5 -130.5 t-190.5 -52.5q-169 0 -257.5 86.5t-88.5 238.5zM303 300q0 -72 45 -114t133 -42q107 0 193 55t112 126v176h-221q-119 0 -190.5 -60t-71.5 -141zM346 1412q0 84 60.5 141t147.5 57q85 0 145 -56.5t60 -141.5q0 -86 -59.5 -140t-145.5 -54q-87 0 -147.5 54t-60.5 140z M449 1412q0 -43 31 -73.5t74 -30.5q42 0 72 29.5t30 74.5t-30 76t-72 31q-43 0 -74 -31t-31 -76z" />
+<glyph unicode="æ" horiz-adv-x="1729" d="M58 304q0 158 115 244.5t335 86.5h229v85q0 106 -51.5 166.5t-149.5 60.5q-103 0 -164 -55t-61 -133l-188 18l-2 6q-5 138 109.5 228.5t305.5 90.5q114 0 201.5 -40.5t137.5 -117.5q64 75 151.5 116.5t188.5 41.5q214 0 329.5 -130t115.5 -358v-119h-709l-2 -5 q1 -159 79.5 -258t233.5 -99q103 0 169.5 27.5t144.5 78.5l67 -138q-53 -44 -147 -83t-234 -39q-136 0 -240 48.5t-170 138.5q-56 -79 -167.5 -133t-271.5 -54q-170 0 -262.5 87t-92.5 238zM255 300q0 -74 50 -120.5t147 -46.5q76 0 159 43.5t126 100.5v216h-227 q-120 0 -187.5 -56t-67.5 -137zM953 645l2 -5h508v31q0 122 -60 199t-188 77q-113 0 -182 -84.5t-80 -217.5z" />
+<glyph unicode="ç" horiz-adv-x="1087" d="M97 520v42q0 231 125.5 385.5t360.5 154.5q190 0 310.5 -112t116.5 -275l-2 -6h-178q0 99 -70 168.5t-177 69.5q-155 0 -221.5 -111.5t-66.5 -273.5v-42q0 -166 66 -276.5t222 -110.5q98 0 172.5 60.5t74.5 148.5h177l2 -6q5 -140 -124.5 -248.5t-301.5 -108.5 q-236 0 -361 154t-125 387zM440 -334q72 0 116 24.5t44 73.5q0 48 -36 67t-123 26l32 135h140l-12 -52q65 -11 108 -52t43 -121q0 -96 -79 -153t-226 -57z" />
+<glyph unicode="è" horiz-adv-x="1083" d="M99 520v44q0 231 137.5 384.5t325.5 153.5q219 0 331 -132t112 -352v-123h-702l-3 -5q3 -156 79 -256.5t213 -100.5q100 0 175.5 28.5t130.5 78.5l77 -128q-58 -57 -153 -95t-230 -38q-226 0 -359.5 150.5t-133.5 390.5zM233 1499l3 6h230l175 -266h-158zM307 654l2 -5 h499v26q0 116 -62 194t-184 78q-99 0 -169 -83.5t-86 -209.5z" />
+<glyph unicode="é" horiz-adv-x="1083" d="M99 520v44q0 231 137.5 384.5t325.5 153.5q219 0 331 -132t112 -352v-123h-702l-3 -5q3 -156 79 -256.5t213 -100.5q100 0 175.5 28.5t130.5 78.5l77 -128q-58 -57 -153 -95t-230 -38q-226 0 -359.5 150.5t-133.5 390.5zM307 654l2 -5h499v26q0 116 -62 194t-184 78 q-99 0 -169 -83.5t-86 -209.5zM468 1235l185 266h230l2 -6l-270 -260h-147z" />
+<glyph unicode="ê" horiz-adv-x="1083" d="M99 520v44q0 231 137.5 384.5t325.5 153.5q219 0 331 -132t112 -352v-123h-702l-3 -5q3 -156 79 -256.5t213 -100.5q100 0 175.5 28.5t130.5 78.5l77 -128q-58 -57 -153 -95t-230 -38q-226 0 -359.5 150.5t-133.5 390.5zM252 1280v26l246 237h120l248 -238v-25h-161 l-147 148l-146 -148h-160zM307 654l2 -5h499v26q0 116 -62 194t-184 78q-99 0 -169 -83.5t-86 -209.5z" />
+<glyph unicode="ë" horiz-adv-x="1083" d="M99 520v44q0 231 137.5 384.5t325.5 153.5q219 0 331 -132t112 -352v-123h-702l-3 -5q3 -156 79 -256.5t213 -100.5q100 0 175.5 28.5t130.5 78.5l77 -128q-58 -57 -153 -95t-230 -38q-226 0 -359.5 150.5t-133.5 390.5zM214 1284v200h219v-200h-219zM307 654l2 -5h499 v26q0 116 -62 194t-184 78q-99 0 -169 -83.5t-86 -209.5zM684 1284v200h219v-200h-219z" />
+<glyph unicode="ì" horiz-adv-x="515" d="M-71 1477l3 6h230l175 -266h-158zM153 0v1082h197v-1082h-197z" />
+<glyph unicode="í" horiz-adv-x="515" d="M153 0v1082h197v-1082h-197zM162 1213l185 266h230l2 -6l-270 -260h-147z" />
+<glyph unicode="î" horiz-adv-x="515" d="M-52 1258v26l246 237h120l248 -238v-25h-161l-147 148l-146 -148h-160zM153 0v1082h197v-1082h-197z" />
+<glyph unicode="ï" horiz-adv-x="515" d="M-90 1262v200h219v-200h-219zM153 0v1082h197v-1082h-197zM380 1262v200h219v-200h-219z" />
+<glyph unicode="ð" horiz-adv-x="1202" d="M72 466q0 228 138 370t351 142q90 0 169.5 -37t131.5 -97l4 5q-9 109 -51.5 197t-110.5 154l-290 -165l-77 102l256 146q-39 22 -80.5 39t-85.5 31l60 164q79 -19 151 -52t135 -79l218 125l77 -102l-195 -112q95 -104 147 -241.5t52 -300.5v-220q0 -245 -144 -400.5 t-359 -155.5q-218 0 -357.5 140t-139.5 347zM269 466q0 -132 82 -232.5t222 -100.5q133 0 217.5 114t84.5 288v148q-35 59 -115.5 99.5t-198.5 40.5q-131 0 -211.5 -104t-80.5 -253z" />
+<glyph unicode="ñ" d="M143 0v1082h176l14 -161q54 86 135.5 133.5t185.5 47.5q175 0 271 -102.5t96 -316.5v-683h-197v679q0 143 -56.5 203t-172.5 60q-85 0 -150.5 -41t-104.5 -112v-789h-197zM231 1306q0 93 59 161.5t150 68.5q56 0 140 -47t136 -47q41 0 71 32.5t30 79.5l108 -32 q0 -94 -59.5 -159t-149.5 -65q-71 0 -148 46.5t-128 46.5q-43 0 -72 -32.5t-29 -78.5z" />
+<glyph unicode="ò" d="M97 529v22q0 240 130 395.5t353 155.5q225 0 355.5 -155t130.5 -396v-22q0 -242 -130 -396t-354 -154t-354.5 154.5t-130.5 395.5zM257 1498l3 6h230l175 -266h-158zM294 529q0 -172 72.5 -284t215.5 -112q141 0 214 112t73 284v22q0 170 -73.5 283t-215.5 113 q-141 0 -213.5 -113t-72.5 -283v-22z" />
+<glyph unicode="ó" d="M97 529v22q0 240 130 395.5t353 155.5q225 0 355.5 -155t130.5 -396v-22q0 -242 -130 -396t-354 -154t-354.5 154.5t-130.5 395.5zM294 529q0 -172 72.5 -284t215.5 -112q141 0 214 112t73 284v22q0 170 -73.5 283t-215.5 113q-141 0 -213.5 -113t-72.5 -283v-22z M492 1234l185 266h230l2 -6l-270 -260h-147z" />
+<glyph unicode="ô" d="M97 529v22q0 240 130 395.5t353 155.5q225 0 355.5 -155t130.5 -396v-22q0 -242 -130 -396t-354 -154t-354.5 154.5t-130.5 395.5zM276 1279v26l246 237h120l248 -238v-25h-161l-147 148l-146 -148h-160zM294 529q0 -172 72.5 -284t215.5 -112q141 0 214 112t73 284v22 q0 170 -73.5 283t-215.5 113q-141 0 -213.5 -113t-72.5 -283v-22z" />
+<glyph unicode="õ" d="M97 529v22q0 240 130 395.5t353 155.5q225 0 355.5 -155t130.5 -396v-22q0 -242 -130 -396t-354 -154t-354.5 154.5t-130.5 395.5zM235 1306q0 93 59 161.5t150 68.5q56 0 140 -47t136 -47q41 0 71 32.5t30 79.5l108 -32q0 -94 -59.5 -159t-149.5 -65q-71 0 -148 46.5 t-128 46.5q-43 0 -72 -32.5t-29 -78.5zM294 529q0 -172 72.5 -284t215.5 -112q141 0 214 112t73 284v22q0 170 -73.5 283t-215.5 113q-141 0 -213.5 -113t-72.5 -283v-22z" />
+<glyph unicode="ö" d="M97 529v22q0 240 130 395.5t353 155.5q225 0 355.5 -155t130.5 -396v-22q0 -242 -130 -396t-354 -154t-354.5 154.5t-130.5 395.5zM238 1283v200h219v-200h-219zM294 529q0 -172 72.5 -284t215.5 -112q141 0 214 112t73 284v22q0 170 -73.5 283t-215.5 113 q-141 0 -213.5 -113t-72.5 -283v-22zM708 1283v200h219v-200h-219z" />
+<glyph unicode="÷" horiz-adv-x="1170" d="M71 597v188h998v-188h-998zM472 180v203h198v-203h-198zM472 999v203h198v-203h-198z" />
+<glyph unicode="ø" d="M97 529v22q0 240 130 395.5t353 155.5q56 0 107.5 -11t97.5 -31l74 149h129l-104 -211q88 -74 135 -190t47 -257v-22q0 -242 -130 -396t-354 -154q-51 0 -97 8.5t-88 24.5l-72 -147h-129l100 204q-96 71 -147.5 191t-51.5 269zM294 529q0 -91 20 -166.5t61 -123.5h6 l332 674q-29 16 -62.5 25t-70.5 9q-141 0 -213.5 -113t-72.5 -283v-22zM469 156q24 -12 52 -17.5t61 -5.5q141 0 214 112t73 284v22q0 80 -17.5 150.5t-49.5 117.5h-6z" />
+<glyph unicode="ù" d="M139 444v638h197v-640q0 -173 51 -238t159 -65q105 0 173.5 42.5t103.5 120.5v780h197v-1082h-177l-13 160q-51 -87 -131 -134t-185 -47q-177 0 -276 113t-99 352zM255 1477l3 6h230l175 -266h-158z" />
+<glyph unicode="ú" d="M139 444v638h197v-640q0 -173 51 -238t159 -65q105 0 173.5 42.5t103.5 120.5v780h197v-1082h-177l-13 160q-51 -87 -131 -134t-185 -47q-177 0 -276 113t-99 352zM490 1213l185 266h230l2 -6l-270 -260h-147z" />
+<glyph unicode="û" d="M139 444v638h197v-640q0 -173 51 -238t159 -65q105 0 173.5 42.5t103.5 120.5v780h197v-1082h-177l-13 160q-51 -87 -131 -134t-185 -47q-177 0 -276 113t-99 352zM274 1258v26l246 237h120l248 -238v-25h-161l-147 148l-146 -148h-160z" />
+<glyph unicode="ü" d="M139 444v638h197v-640q0 -173 51 -238t159 -65q105 0 173.5 42.5t103.5 120.5v780h197v-1082h-177l-13 160q-51 -87 -131 -134t-185 -47q-177 0 -276 113t-99 352zM236 1262v200h219v-200h-219zM706 1262v200h219v-200h-219z" />
+<glyph unicode="ý" horiz-adv-x="1030" d="M26 1082h220l228 -681l35 -136h6l266 817h219l-455 -1248q-41 -109 -117.5 -190t-206.5 -81q-24 0 -61 5.5t-57 10.5l20 155q-6 1 35.5 -2t52.5 -3q63 0 103 56t67 124l47 113zM424 1213l185 266h230l2 -6l-270 -260h-147z" />
+<glyph unicode="þ" horiz-adv-x="1186" d="M153 -416v1976h197v-598q53 68 128 104t173 36q201 0 312.5 -159.5t111.5 -421.5v-21q0 -234 -112 -377.5t-309 -143.5q-100 0 -175.5 33.5t-128.5 100.5v-529h-197zM350 275q37 -67 97 -104.5t147 -37.5q140 0 212 102.5t72 264.5v21q0 184 -72.5 302.5t-213.5 118.5 q-85 0 -145 -38.5t-97 -105.5v-523z" />
+<glyph unicode="ÿ" horiz-adv-x="1030" d="M26 1082h220l228 -681l35 -136h6l266 817h219l-455 -1248q-41 -109 -117.5 -190t-206.5 -81q-24 0 -61 5.5t-57 10.5l20 155q-6 1 35.5 -2t52.5 -3q63 0 103 56t67 124l47 113zM170 1262v200h219v-200h-219zM640 1262v200h219v-200h-219z" />
+<glyph unicode="Œ" horiz-adv-x="1960" d="M104 576v304q0 265 154.5 431t403.5 166q69 0 140.5 -6t150.5 -15h907v-155h-758v-471h667v-155h-667v-521h769v-154h-918q-92 -10 -157 -15.5t-132 -5.5q-249 0 -404.5 166t-155.5 431zM301 576q0 -214 97 -328t266 -114q61 0 122 4.5t119 13.5v1151q-61 8 -122 13.5 t-121 5.5q-169 0 -265 -113.5t-96 -326.5v-306z" />
+<glyph unicode="œ" horiz-adv-x="1854" d="M97 529v22q0 240 130 395.5t353 155.5q130 0 230 -54.5t164 -152.5q65 97 161.5 152t204.5 55q219 0 331 -132t112 -352v-123h-702l-3 -5q3 -156 79 -256.5t213 -100.5q100 0 175.5 28.5t130.5 78.5l77 -128q-58 -57 -153 -95t-230 -38q-131 0 -232.5 52.5t-166.5 148.5 q-64 -96 -163 -148.5t-226 -52.5q-224 0 -354.5 154.5t-130.5 395.5zM294 529q0 -172 72.5 -284t215.5 -112q141 0 214 112t73 284v22q0 170 -73.5 283t-215.5 113q-141 0 -213.5 -113t-72.5 -283v-22zM1085 654l2 -5h499v26q0 116 -62 194t-184 78q-99 0 -169 -83.5 t-86 -209.5z" />
+<glyph unicode="Ÿ" horiz-adv-x="1250" d="M20 1456h225l380 -740l380 740h225l-511 -944v-512h-196v525zM281 1604v200h219v-200h-219zM751 1604v200h219v-200h-219z" />
+<glyph unicode="ˆ" horiz-adv-x="979" d="M171 1252v26l246 237h120l248 -238v-25h-161l-147 148l-146 -148h-160z" />
+<glyph unicode="˜" horiz-adv-x="979" d="M135 1275q0 93 59 161.5t150 68.5q56 0 140 -47t136 -47q41 0 71 32.5t30 79.5l108 -32q0 -94 -59.5 -159t-149.5 -65q-71 0 -148 46.5t-128 46.5q-43 0 -72 -32.5t-29 -78.5z" />
+<glyph unicode=" " horiz-adv-x="966" />
+<glyph unicode=" " horiz-adv-x="1932" />
+<glyph unicode=" " horiz-adv-x="966" />
+<glyph unicode=" " horiz-adv-x="1932" />
+<glyph unicode=" " horiz-adv-x="644" />
+<glyph unicode=" " horiz-adv-x="483" />
+<glyph unicode=" " horiz-adv-x="322" />
+<glyph unicode=" " horiz-adv-x="322" />
+<glyph unicode=" " horiz-adv-x="241" />
+<glyph unicode=" " horiz-adv-x="386" />
+<glyph unicode=" " horiz-adv-x="107" />
+<glyph unicode="‐" horiz-adv-x="561" d="M35 538v154h490v-154h-490z" />
+<glyph unicode="‑" horiz-adv-x="561" d="M35 538v154h490v-154h-490z" />
+<glyph unicode="‒" horiz-adv-x="561" d="M35 538v154h490v-154h-490z" />
+<glyph unicode="–" horiz-adv-x="1416" d="M169 648v155h1086v-155h-1086z" />
+<glyph unicode="—" horiz-adv-x="1660" d="M141 648v155h1336v-155h-1336z" />
+<glyph unicode="‘" horiz-adv-x="418" d="M80 1020v184l160 356h97l-60 -362v-178h-197z" />
+<glyph unicode="’" horiz-adv-x="418" d="M80 1021l60 343v196h197v-193l-160 -346h-97z" />
+<glyph unicode="‚" horiz-adv-x="417" d="M80 -255l60 263v241h197v-223l-160 -281h-97z" />
+<glyph unicode="“" horiz-adv-x="744" d="M80 1020v184l160 356h97l-60 -362v-178h-197zM409 1020v184l160 356h97l-60 -362v-178h-197z" />
+<glyph unicode="”" horiz-adv-x="752" d="M80 1021l60 343v196h197v-193l-160 -346h-97zM417 1021l60 343v196h197v-193l-160 -346h-97z" />
+<glyph unicode="„" horiz-adv-x="726" d="M80 -239l60 325v194h197v-184l-160 -335h-97zM388 -239l60 333v186h197v-184l-160 -335h-97z" />
+<glyph unicode="•" horiz-adv-x="695" d="M137 733v60q0 88 56 144t150 56q95 0 151.5 -56t56.5 -144v-60q0 -89 -56 -143.5t-151 -54.5t-151 55t-56 143z" />
+<glyph unicode="…" horiz-adv-x="1380" d="M161 0v202h197v-202h-197zM604 0v202h197v-202h-197zM1024 0v202h197v-202h-197z" />
+<glyph unicode=" " horiz-adv-x="386" />
+<glyph unicode="‹" horiz-adv-x="615" d="M108 541v19l295 389h148l-255 -399l255 -398h-148z" />
+<glyph unicode="›" horiz-adv-x="615" d="M88 152l255 398l-255 399h148l295 -389v-19l-295 -389h-148z" />
+<glyph unicode=" " horiz-adv-x="483" />
+<glyph unicode="€" horiz-adv-x="1088" d="M79 481v155h146v136h-146v155h146v15q0 244 141.5 389.5t372.5 145.5q59 0 117.5 -8t124.5 -23l-19 -159q-54 16 -110.5 25.5t-112.5 9.5q-146 0 -231.5 -103t-85.5 -275v-17h492v-155h-492v-136h492v-155h-485l-2 -5q-4 -138 81.5 -240.5t232.5 -102.5q57 0 113 8.5 t108 25.5l19 -157q-56 -15 -117.5 -23t-122.5 -8q-231 0 -373.5 144.5t-142.5 357.5h-146z" />
+<glyph unicode="™" horiz-adv-x="1284" d="M103 1374v82h384v-82h-145v-455h-94v455h-145zM565 919v537h116l161 -390h6l162 390h110v-537h-93v343l-6 2l-150 -345h-51l-156 359l-6 -2v-357h-93z" />
+<glyph unicode="" horiz-adv-x="1080" d="M0 0v1080h1080v-1080h-1080z" />
+<glyph unicode="fl" horiz-adv-x="1223" d="M56 936v146h169v137q0 173 90.5 267.5t252.5 94.5q34 0 68.5 -5.5t76.5 -15.5l-24 -150q-18 4 -43.5 7t-53.5 3q-86 0 -128 -51.5t-42 -149.5v-137h225v-146h-225v-936h-197v936h-169zM866 0v1560h197v-1560h-197z" />
+<glyph unicode="ffi" horiz-adv-x="1847" d="M56 936v146h169v137q0 173 90.5 267.5t252.5 94.5q34 0 68.5 -5.5t76.5 -15.5l-24 -150q-18 4 -43.5 7t-53.5 3q-86 0 -128 -51.5t-42 -149.5v-137h225v-146h-225v-936h-197v936h-169zM735 936v146h170v117q0 182 106.5 282t295.5 100q67 0 132 -15.5t153 -45.5l-34 -160 q-53 21 -113 36t-123 15q-117 0 -168.5 -52t-51.5 -160v-117h215v-146h-215v-936h-197v936h-170zM1490 0v1082h198v-1082h-198z" />
+<glyph unicode="ffl" horiz-adv-x="1930" d="M56 936v146h169v137q0 173 90.5 267.5t252.5 94.5q34 0 68.5 -5.5t76.5 -15.5l-24 -150q-18 4 -43.5 7t-53.5 3q-86 0 -128 -51.5t-42 -149.5v-137h225v-146h-225v-936h-197v936h-169zM763 936v146h169v137q0 173 90.5 267.5t252.5 94.5q34 0 68.5 -5.5t76.5 -15.5 l-24 -150q-18 4 -43.5 7t-53.5 3q-86 0 -128 -51.5t-42 -149.5v-137h225v-146h-225v-936h-197v936h-169zM1573 0v1560h197v-1560h-197z" />
+<hkern u1=""" u2="w" k="-11" />
+<hkern u1="'" u2="w" k="-11" />
+<hkern u1="(" u2="Ÿ" k="-22" />
+<hkern u1="(" u2="Ý" k="-22" />
+<hkern u1="(" u2="Y" k="-22" />
+<hkern u1="(" u2="W" k="-37" />
+<hkern u1="(" u2="V" k="-20" />
+<hkern u1="A" u2="w" k="33" />
+<hkern u1="A" u2="t" k="17" />
+<hkern u1="A" u2="?" k="80" />
+<hkern u1="C" u2="}" k="17" />
+<hkern u1="C" u2="]" k="12" />
+<hkern u1="C" u2=")" k="26" />
+<hkern u1="D" u2="Æ" k="33" />
+<hkern u1="E" u2="w" k="22" />
+<hkern u1="E" u2="f" k="18" />
+<hkern u1="F" u2="…" k="273" />
+<hkern u1="F" u2="„" k="273" />
+<hkern u1="F" u2="‚" k="273" />
+<hkern u1="F" u2="œ" k="21" />
+<hkern u1="F" u2="ÿ" k="24" />
+<hkern u1="F" u2="ý" k="24" />
+<hkern u1="F" u2="ü" k="22" />
+<hkern u1="F" u2="û" k="22" />
+<hkern u1="F" u2="ú" k="22" />
+<hkern u1="F" u2="ù" k="22" />
+<hkern u1="F" u2="ö" k="21" />
+<hkern u1="F" u2="õ" k="21" />
+<hkern u1="F" u2="ô" k="21" />
+<hkern u1="F" u2="ó" k="21" />
+<hkern u1="F" u2="ò" k="21" />
+<hkern u1="F" u2="ë" k="21" />
+<hkern u1="F" u2="ê" k="21" />
+<hkern u1="F" u2="é" k="21" />
+<hkern u1="F" u2="è" k="21" />
+<hkern u1="F" u2="ç" k="21" />
+<hkern u1="F" u2="å" k="34" />
+<hkern u1="F" u2="ä" k="34" />
+<hkern u1="F" u2="ã" k="34" />
+<hkern u1="F" u2="â" k="34" />
+<hkern u1="F" u2="á" k="34" />
+<hkern u1="F" u2="à" k="34" />
+<hkern u1="F" u2="Å" k="59" />
+<hkern u1="F" u2="Ä" k="59" />
+<hkern u1="F" u2="Ã" k="59" />
+<hkern u1="F" u2="Â" k="59" />
+<hkern u1="F" u2="Á" k="59" />
+<hkern u1="F" u2="À" k="59" />
+<hkern u1="F" u2="y" k="24" />
+<hkern u1="F" u2="v" k="24" />
+<hkern u1="F" u2="u" k="22" />
+<hkern u1="F" u2="r" k="26" />
+<hkern u1="F" u2="q" k="21" />
+<hkern u1="F" u2="o" k="21" />
+<hkern u1="F" u2="g" k="21" />
+<hkern u1="F" u2="e" k="21" />
+<hkern u1="F" u2="d" k="21" />
+<hkern u1="F" u2="c" k="21" />
+<hkern u1="F" u2="a" k="34" />
+<hkern u1="F" u2="T" k="-20" />
+<hkern u1="F" u2="A" k="59" />
+<hkern u1="F" u2=":" k="273" />
+<hkern u1="F" u2="." k="273" />
+<hkern u1="F" u2="," k="273" />
+<hkern u1="K" u2="w" k="63" />
+<hkern u1="L" u2="w" k="52" />
+<hkern u1="O" u2="Æ" k="33" />
+<hkern u1="P" u2="Æ" k="293" />
+<hkern u1="P" u2="t" k="-14" />
+<hkern u1="Q" u2="Ÿ" k="35" />
+<hkern u1="Q" u2="Ý" k="35" />
+<hkern u1="Q" u2="Y" k="35" />
+<hkern u1="Q" u2="W" k="20" />
+<hkern u1="Q" u2="V" k="28" />
+<hkern u1="Q" u2="T" k="33" />
+<hkern u1="R" u2="Ÿ" k="48" />
+<hkern u1="R" u2="Ý" k="48" />
+<hkern u1="R" u2="Y" k="48" />
+<hkern u1="R" u2="V" k="19" />
+<hkern u1="R" u2="T" k="50" />
+<hkern u1="T" u2="ø" k="95" />
+<hkern u1="T" u2="æ" k="84" />
+<hkern u1="T" u2="Æ" k="188" />
+<hkern u1="T" u2="»" k="147" />
+<hkern u1="T" u2="«" k="151" />
+<hkern u1="T" u2="w" k="47" />
+<hkern u1="T" u2="r" k="65" />
+<hkern u1="V" u2="}" k="-19" />
+<hkern u1="V" u2="r" k="30" />
+<hkern u1="V" u2="]" k="-17" />
+<hkern u1="V" u2=")" k="-20" />
+<hkern u1="W" u2="}" k="-14" />
+<hkern u1="W" u2="r" k="21" />
+<hkern u1="W" u2="]" k="-12" />
+<hkern u1="W" u2=")" k="-15" />
+<hkern u1="Y" u2="•" k="45" />
+<hkern u1="Y" u2="ø" k="64" />
+<hkern u1="Y" u2="æ" k="63" />
+<hkern u1="Y" u2="Æ" k="96" />
+<hkern u1="Y" u2="»" k="51" />
+<hkern u1="Y" u2="«" k="82" />
+<hkern u1="Y" u2="}" k="-19" />
+<hkern u1="Y" u2="t" k="22" />
+<hkern u1="Y" u2="r" k="40" />
+<hkern u1="Y" u2="f" k="22" />
+<hkern u1="Y" u2="]" k="-18" />
+<hkern u1="Y" u2="*" k="49" />
+<hkern u1="Y" u2=")" k="-20" />
+<hkern u1="Y" u2="&" k="30" />
+<hkern u1="Z" u2="w" k="27" />
+<hkern u1="[" u2="Ü" k="18" />
+<hkern u1="[" u2="Û" k="18" />
+<hkern u1="[" u2="Ú" k="18" />
+<hkern u1="[" u2="Ù" k="18" />
+<hkern u1="[" u2="U" k="18" />
+<hkern u1="[" u2="J" k="18" />
+<hkern u1="f" u2="”" k="-16" />
+<hkern u1="f" u2="“" k="-16" />
+<hkern u1="f" u2="’" k="-16" />
+<hkern u1="f" u2="‘" k="-16" />
+<hkern u1="f" u2="œ" k="24" />
+<hkern u1="f" u2="ë" k="24" />
+<hkern u1="f" u2="ê" k="24" />
+<hkern u1="f" u2="é" k="24" />
+<hkern u1="f" u2="è" k="24" />
+<hkern u1="f" u2="ç" k="24" />
+<hkern u1="f" u2="}" k="-19" />
+<hkern u1="f" u2="q" k="24" />
+<hkern u1="f" u2="g" k="24" />
+<hkern u1="f" u2="e" k="24" />
+<hkern u1="f" u2="d" k="24" />
+<hkern u1="f" u2="c" k="24" />
+<hkern u1="f" u2="]" k="-18" />
+<hkern u1="f" u2=")" k="-20" />
+<hkern u1="f" u2="'" k="-16" />
+<hkern u1="f" u2=""" k="-16" />
+<hkern u1="k" u2="œ" k="20" />
+<hkern u1="k" u2="ë" k="20" />
+<hkern u1="k" u2="ê" k="20" />
+<hkern u1="k" u2="é" k="20" />
+<hkern u1="k" u2="è" k="20" />
+<hkern u1="k" u2="ç" k="20" />
+<hkern u1="k" u2="q" k="20" />
+<hkern u1="k" u2="g" k="20" />
+<hkern u1="k" u2="e" k="20" />
+<hkern u1="k" u2="d" k="20" />
+<hkern u1="k" u2="c" k="20" />
+<hkern u1="r" u2="w" k="-17" />
+<hkern u1="r" u2="t" k="-17" />
+<hkern u1="r" u2="f" k="-15" />
+<hkern u1="v" u2="f" k="-13" />
+<hkern u1="w" u2="…" k="124" />
+<hkern u1="w" u2="„" k="124" />
+<hkern u1="w" u2="‚" k="124" />
+<hkern u1="w" u2=":" k="124" />
+<hkern u1="w" u2="." k="124" />
+<hkern u1="w" u2="," k="124" />
+<hkern u1="y" u2="f" k="-13" />
+<hkern u1="{" u2="Ü" k="20" />
+<hkern u1="{" u2="Û" k="20" />
+<hkern u1="{" u2="Ú" k="20" />
+<hkern u1="{" u2="Ù" k="20" />
+<hkern u1="{" u2="U" k="20" />
+<hkern u1="{" u2="J" k="20" />
+<hkern u1="À" u2="w" k="33" />
+<hkern u1="À" u2="t" k="17" />
+<hkern u1="À" u2="?" k="80" />
+<hkern u1="Á" u2="w" k="33" />
+<hkern u1="Á" u2="t" k="17" />
+<hkern u1="Á" u2="?" k="80" />
+<hkern u1="Â" u2="w" k="33" />
+<hkern u1="Â" u2="t" k="17" />
+<hkern u1="Â" u2="?" k="80" />
+<hkern u1="Ã" u2="w" k="33" />
+<hkern u1="Ã" u2="t" k="17" />
+<hkern u1="Ã" u2="?" k="80" />
+<hkern u1="Ä" u2="w" k="33" />
+<hkern u1="Ä" u2="t" k="17" />
+<hkern u1="Ä" u2="?" k="80" />
+<hkern u1="Å" u2="w" k="33" />
+<hkern u1="Å" u2="t" k="17" />
+<hkern u1="Å" u2="?" k="80" />
+<hkern u1="Ç" u2="}" k="17" />
+<hkern u1="Ç" u2="]" k="12" />
+<hkern u1="Ç" u2=")" k="26" />
+<hkern u1="È" u2="w" k="22" />
+<hkern u1="È" u2="f" k="18" />
+<hkern u1="É" u2="w" k="22" />
+<hkern u1="É" u2="f" k="18" />
+<hkern u1="Ê" u2="w" k="22" />
+<hkern u1="Ê" u2="f" k="18" />
+<hkern u1="Ë" u2="w" k="22" />
+<hkern u1="Ë" u2="f" k="18" />
+<hkern u1="Ð" u2="Æ" k="33" />
+<hkern u1="Ò" u2="Æ" k="33" />
+<hkern u1="Ó" u2="Æ" k="33" />
+<hkern u1="Ô" u2="Æ" k="33" />
+<hkern u1="Õ" u2="Æ" k="33" />
+<hkern u1="Ö" u2="Æ" k="33" />
+<hkern u1="Ý" u2="•" k="45" />
+<hkern u1="Ý" u2="ø" k="64" />
+<hkern u1="Ý" u2="æ" k="63" />
+<hkern u1="Ý" u2="Æ" k="96" />
+<hkern u1="Ý" u2="»" k="51" />
+<hkern u1="Ý" u2="«" k="82" />
+<hkern u1="Ý" u2="}" k="-19" />
+<hkern u1="Ý" u2="t" k="22" />
+<hkern u1="Ý" u2="r" k="40" />
+<hkern u1="Ý" u2="f" k="22" />
+<hkern u1="Ý" u2="]" k="-18" />
+<hkern u1="Ý" u2="*" k="49" />
+<hkern u1="Ý" u2=")" k="-20" />
+<hkern u1="Ý" u2="&" k="30" />
+<hkern u1="ý" u2="f" k="-13" />
+<hkern u1="ÿ" u2="f" k="-13" />
+<hkern u1="Ÿ" u2="•" k="45" />
+<hkern u1="Ÿ" u2="ø" k="64" />
+<hkern u1="Ÿ" u2="æ" k="63" />
+<hkern u1="Ÿ" u2="Æ" k="96" />
+<hkern u1="Ÿ" u2="»" k="51" />
+<hkern u1="Ÿ" u2="«" k="82" />
+<hkern u1="Ÿ" u2="}" k="-19" />
+<hkern u1="Ÿ" u2="t" k="22" />
+<hkern u1="Ÿ" u2="r" k="40" />
+<hkern u1="Ÿ" u2="f" k="22" />
+<hkern u1="Ÿ" u2="]" k="-18" />
+<hkern u1="Ÿ" u2="*" k="49" />
+<hkern u1="Ÿ" u2=")" k="-20" />
+<hkern u1="Ÿ" u2="&" k="30" />
+<hkern u1="‘" u2="w" k="-11" />
+<hkern u1="’" u2="w" k="-11" />
+<hkern u1="“" u2="w" k="-11" />
+<hkern u1="”" u2="w" k="-11" />
+<hkern g1="comma,period,colon,quotesinglbase,quotedblbase,ellipsis" g2="quotedbl,quotesingle,quoteleft,quoteright,quotedblleft,quotedblright" k="282" />
+<hkern g1="B" g2="T" k="27" />
+<hkern g1="B" g2="V" k="24" />
+<hkern g1="B" g2="Y,Yacute,Ydieresis" k="55" />
+<hkern g1="C,Ccedilla" g2="T" k="29" />
+<hkern g1="D,O,Eth,Ograve,Oacute,Ocircumflex,Otilde,Odieresis" g2="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring" k="21" />
+<hkern g1="D,O,Eth,Ograve,Oacute,Ocircumflex,Otilde,Odieresis" g2="T" k="27" />
+<hkern g1="D,O,Eth,Ograve,Oacute,Ocircumflex,Otilde,Odieresis" g2="V" k="22" />
+<hkern g1="D,O,Eth,Ograve,Oacute,Ocircumflex,Otilde,Odieresis" g2="Y,Yacute,Ydieresis" k="43" />
+<hkern g1="D,O,Eth,Ograve,Oacute,Ocircumflex,Otilde,Odieresis" g2="comma,period,colon,quotesinglbase,quotedblbase,ellipsis" k="121" />
+<hkern g1="D,O,Eth,Ograve,Oacute,Ocircumflex,Otilde,Odieresis" g2="X" k="22" />
+<hkern g1="D,O,Eth,Ograve,Oacute,Ocircumflex,Otilde,Odieresis" g2="Z" k="23" />
+<hkern g1="E,Egrave,Eacute,Ecircumflex,Edieresis" g2="c,d,e,g,q,ccedilla,egrave,eacute,ecircumflex,edieresis,oe" k="19" />
+<hkern g1="E,Egrave,Eacute,Ecircumflex,Edieresis" g2="o,ograve,oacute,ocircumflex,otilde,odieresis" k="19" />
+<hkern g1="E,Egrave,Eacute,Ecircumflex,Edieresis" g2="T" k="-20" />
+<hkern g1="E,Egrave,Eacute,Ecircumflex,Edieresis" g2="u,ugrave,uacute,ucircumflex,udieresis" k="17" />
+<hkern g1="E,Egrave,Eacute,Ecircumflex,Edieresis" g2="v,y,yacute,ydieresis" k="26" />
+<hkern g1="H,I,M,N,Igrave,Iacute,Icircumflex,Idieresis,Ntilde" g2="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring" k="-18" />
+<hkern g1="H,I,M,N,Igrave,Iacute,Icircumflex,Idieresis,Ntilde" g2="T" k="29" />
+<hkern g1="H,I,M,N,Igrave,Iacute,Icircumflex,Idieresis,Ntilde" g2="Y,Yacute,Ydieresis" k="28" />
+<hkern g1="H,I,M,N,Igrave,Iacute,Icircumflex,Idieresis,Ntilde" g2="X" k="-17" />
+<hkern g1="J,U,Ugrave,Uacute,Ucircumflex,Udieresis" g2="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring" k="22" />
+<hkern g1="K" g2="c,d,e,g,q,ccedilla,egrave,eacute,ecircumflex,edieresis,oe" k="26" />
+<hkern g1="K" g2="o,ograve,oacute,ocircumflex,otilde,odieresis" k="27" />
+<hkern g1="K" g2="u,ugrave,uacute,ucircumflex,udieresis" k="23" />
+<hkern g1="K" g2="v,y,yacute,ydieresis" k="40" />
+<hkern g1="K" g2="hyphen,uni00AD,endash,emdash" k="162" />
+<hkern g1="K" g2="C,G,O,Q,Ccedilla,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,OE" k="31" />
+<hkern g1="L" g2="quotedbl,quotesingle,quoteleft,quoteright,quotedblleft,quotedblright" k="255" />
+<hkern g1="L" g2="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring" k="-19" />
+<hkern g1="L" g2="T" k="206" />
+<hkern g1="L" g2="V" k="205" />
+<hkern g1="L" g2="Y,Yacute,Ydieresis" k="278" />
+<hkern g1="L" g2="u,ugrave,uacute,ucircumflex,udieresis" k="14" />
+<hkern g1="L" g2="v,y,yacute,ydieresis" k="123" />
+<hkern g1="L" g2="C,G,O,Q,Ccedilla,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,OE" k="25" />
+<hkern g1="L" g2="U,Ugrave,Uacute,Ucircumflex,Udieresis" k="24" />
+<hkern g1="L" g2="W" k="93" />
+<hkern g1="P" g2="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring" k="177" />
+<hkern g1="P" g2="a,agrave,aacute,acircumflex,atilde,adieresis,aring" k="11" />
+<hkern g1="P" g2="c,d,e,g,q,ccedilla,egrave,eacute,ecircumflex,edieresis,oe" k="13" />
+<hkern g1="P" g2="o,ograve,oacute,ocircumflex,otilde,odieresis" k="13" />
+<hkern g1="P" g2="comma,period,colon,quotesinglbase,quotedblbase,ellipsis" k="402" />
+<hkern g1="P" g2="X" k="50" />
+<hkern g1="P" g2="Z" k="35" />
+<hkern g1="P" g2="v,y,yacute,ydieresis" k="-15" />
+<hkern g1="T" g2="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring" k="108" />
+<hkern g1="T" g2="a,agrave,aacute,acircumflex,atilde,adieresis,aring" k="83" />
+<hkern g1="T" g2="c,d,e,g,q,ccedilla,egrave,eacute,ecircumflex,edieresis,oe" k="89" />
+<hkern g1="T" g2="m,n,p,ntilde" k="89" />
+<hkern g1="T" g2="o,ograve,oacute,ocircumflex,otilde,odieresis" k="79" />
+<hkern g1="T" g2="s" k="76" />
+<hkern g1="T" g2="T" k="-16" />
+<hkern g1="T" g2="V" k="-16" />
+<hkern g1="T" g2="Y,Yacute,Ydieresis" k="-16" />
+<hkern g1="T" g2="comma,period,colon,quotesinglbase,quotedblbase,ellipsis" k="257" />
+<hkern g1="T" g2="u,ugrave,uacute,ucircumflex,udieresis" k="65" />
+<hkern g1="T" g2="v,y,yacute,ydieresis" k="81" />
+<hkern g1="T" g2="hyphen,uni00AD,endash,emdash" k="271" />
+<hkern g1="T" g2="C,G,O,Q,Ccedilla,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,OE" k="28" />
+<hkern g1="T" g2="W" k="-15" />
+<hkern g1="T" g2="S" k="16" />
+<hkern g1="T" g2="x" k="77" />
+<hkern g1="T" g2="z" k="60" />
+<hkern g1="V" g2="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring" k="75" />
+<hkern g1="V" g2="a,agrave,aacute,acircumflex,atilde,adieresis,aring" k="46" />
+<hkern g1="V" g2="c,d,e,g,q,ccedilla,egrave,eacute,ecircumflex,edieresis,oe" k="44" />
+<hkern g1="V" g2="o,ograve,oacute,ocircumflex,otilde,odieresis" k="46" />
+<hkern g1="V" g2="comma,period,colon,quotesinglbase,quotedblbase,ellipsis" k="215" />
+<hkern g1="V" g2="u,ugrave,uacute,ucircumflex,udieresis" k="28" />
+<hkern g1="V" g2="v,y,yacute,ydieresis" k="11" />
+<hkern g1="V" g2="hyphen,uni00AD,endash,emdash" k="154" />
+<hkern g1="V" g2="C,G,O,Q,Ccedilla,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,OE" k="13" />
+<hkern g1="W" g2="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring" k="43" />
+<hkern g1="W" g2="a,agrave,aacute,acircumflex,atilde,adieresis,aring" k="33" />
+<hkern g1="W" g2="c,d,e,g,q,ccedilla,egrave,eacute,ecircumflex,edieresis,oe" k="31" />
+<hkern g1="W" g2="o,ograve,oacute,ocircumflex,otilde,odieresis" k="31" />
+<hkern g1="W" g2="T" k="-14" />
+<hkern g1="W" g2="comma,period,colon,quotesinglbase,quotedblbase,ellipsis" k="142" />
+<hkern g1="W" g2="u,ugrave,uacute,ucircumflex,udieresis" k="19" />
+<hkern g1="W" g2="hyphen,uni00AD,endash,emdash" k="60" />
+<hkern g1="X" g2="c,d,e,g,q,ccedilla,egrave,eacute,ecircumflex,edieresis,oe" k="26" />
+<hkern g1="X" g2="o,ograve,oacute,ocircumflex,otilde,odieresis" k="21" />
+<hkern g1="X" g2="V" k="-14" />
+<hkern g1="X" g2="u,ugrave,uacute,ucircumflex,udieresis" k="21" />
+<hkern g1="X" g2="v,y,yacute,ydieresis" k="31" />
+<hkern g1="X" g2="hyphen,uni00AD,endash,emdash" k="153" />
+<hkern g1="X" g2="C,G,O,Q,Ccedilla,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,OE" k="25" />
+<hkern g1="Y,Yacute,Ydieresis" g2="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring" k="148" />
+<hkern g1="Y,Yacute,Ydieresis" g2="a,agrave,aacute,acircumflex,atilde,adieresis,aring" k="63" />
+<hkern g1="Y,Yacute,Ydieresis" g2="c,d,e,g,q,ccedilla,egrave,eacute,ecircumflex,edieresis,oe" k="65" />
+<hkern g1="Y,Yacute,Ydieresis" g2="m,n,p,ntilde" k="40" />
+<hkern g1="Y,Yacute,Ydieresis" g2="o,ograve,oacute,ocircumflex,otilde,odieresis" k="65" />
+<hkern g1="Y,Yacute,Ydieresis" g2="s" k="58" />
+<hkern g1="Y,Yacute,Ydieresis" g2="T" k="-17" />
+<hkern g1="Y,Yacute,Ydieresis" g2="V" k="-18" />
+<hkern g1="Y,Yacute,Ydieresis" g2="Y,Yacute,Ydieresis" k="-18" />
+<hkern g1="Y,Yacute,Ydieresis" g2="comma,period,colon,quotesinglbase,quotedblbase,ellipsis" k="230" />
+<hkern g1="Y,Yacute,Ydieresis" g2="X" k="-13" />
+<hkern g1="Y,Yacute,Ydieresis" g2="u,ugrave,uacute,ucircumflex,udieresis" k="39" />
+<hkern g1="Y,Yacute,Ydieresis" g2="v,y,yacute,ydieresis" k="20" />
+<hkern g1="Y,Yacute,Ydieresis" g2="hyphen,uni00AD,endash,emdash" k="150" />
+<hkern g1="Y,Yacute,Ydieresis" g2="C,G,O,Q,Ccedilla,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,OE" k="29" />
+<hkern g1="Y,Yacute,Ydieresis" g2="U,Ugrave,Uacute,Ucircumflex,Udieresis" k="96" />
+<hkern g1="Y,Yacute,Ydieresis" g2="W" k="-17" />
+<hkern g1="Y,Yacute,Ydieresis" g2="S" k="16" />
+<hkern g1="Y,Yacute,Ydieresis" g2="x" k="23" />
+<hkern g1="Y,Yacute,Ydieresis" g2="z" k="30" />
+<hkern g1="Y,Yacute,Ydieresis" g2="J" k="96" />
+<hkern g1="Z" g2="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring" k="-13" />
+<hkern g1="Z" g2="c,d,e,g,q,ccedilla,egrave,eacute,ecircumflex,edieresis,oe" k="21" />
+<hkern g1="Z" g2="o,ograve,oacute,ocircumflex,otilde,odieresis" k="21" />
+<hkern g1="Z" g2="u,ugrave,uacute,ucircumflex,udieresis" k="19" />
+<hkern g1="Z" g2="v,y,yacute,ydieresis" k="27" />
+<hkern g1="Z" g2="C,G,O,Q,Ccedilla,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,OE" k="26" />
+<hkern g1="a,agrave,aacute,acircumflex,atilde,adieresis,aring" g2="quotedbl,quotesingle,quoteleft,quoteright,quotedblleft,quotedblright" k="17" />
+<hkern g1="a,agrave,aacute,acircumflex,atilde,adieresis,aring" g2="v,y,yacute,ydieresis" k="15" />
+<hkern g1="b,p,thorn" g2="quotedbl,quotesingle,quoteleft,quoteright,quotedblleft,quotedblright" k="29" />
+<hkern g1="b,p,thorn" g2="v,y,yacute,ydieresis" k="11" />
+<hkern g1="b,p,thorn" g2="x" k="15" />
+<hkern g1="b,p,thorn" g2="z" k="15" />
+<hkern g1="c,ccedilla" g2="quotedbl,quotesingle,quoteleft,quoteright,quotedblleft,quotedblright" k="11" />
+<hkern g1="e,egrave,eacute,ecircumflex,edieresis" g2="quotedbl,quotesingle,quoteleft,quoteright,quotedblleft,quotedblright" k="14" />
+<hkern g1="e,egrave,eacute,ecircumflex,edieresis" g2="v,y,yacute,ydieresis" k="13" />
+<hkern g1="h,m,n,ntilde" g2="quotedbl,quotesingle,quoteleft,quoteright,quotedblleft,quotedblright" k="16" />
+<hkern g1="o,ograve,oacute,ocircumflex,otilde,odieresis" g2="quotedbl,quotesingle,quoteleft,quoteright,quotedblleft,quotedblright" k="20" />
+<hkern g1="o,ograve,oacute,ocircumflex,otilde,odieresis" g2="v,y,yacute,ydieresis" k="15" />
+<hkern g1="o,ograve,oacute,ocircumflex,otilde,odieresis" g2="x" k="21" />
+<hkern g1="o,ograve,oacute,ocircumflex,otilde,odieresis" g2="z" k="16" />
+<hkern g1="r" g2="quotedbl,quotesingle,quoteleft,quoteright,quotedblleft,quotedblright" k="-16" />
+<hkern g1="r" g2="c,d,e,g,q,ccedilla,egrave,eacute,ecircumflex,edieresis,oe" k="19" />
+<hkern g1="r" g2="o,ograve,oacute,ocircumflex,otilde,odieresis" k="20" />
+<hkern g1="r" g2="comma,period,colon,quotesinglbase,quotedblbase,ellipsis" k="172" />
+<hkern g1="r" g2="v,y,yacute,ydieresis" k="-18" />
+<hkern g1="v,y,yacute,ydieresis" g2="quotedbl,quotesingle,quoteleft,quoteright,quotedblleft,quotedblright" k="-15" />
+<hkern g1="v,y,yacute,ydieresis" g2="a,agrave,aacute,acircumflex,atilde,adieresis,aring" k="15" />
+<hkern g1="v,y,yacute,ydieresis" g2="c,d,e,g,q,ccedilla,egrave,eacute,ecircumflex,edieresis,oe" k="13" />
+<hkern g1="v,y,yacute,ydieresis" g2="o,ograve,oacute,ocircumflex,otilde,odieresis" k="15" />
+<hkern g1="v,y,yacute,ydieresis" g2="comma,period,colon,quotesinglbase,quotedblbase,ellipsis" k="165" />
+<hkern g1="x" g2="c,d,e,g,q,ccedilla,egrave,eacute,ecircumflex,edieresis,oe" k="20" />
+<hkern g1="x" g2="o,ograve,oacute,ocircumflex,otilde,odieresis" k="39" />
+<hkern g1="z" g2="c,d,e,g,q,ccedilla,egrave,eacute,ecircumflex,edieresis,oe" k="16" />
+<hkern g1="z" g2="o,ograve,oacute,ocircumflex,otilde,odieresis" k="16" />
+</font>
+</defs></svg>
\ 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 0000000..44dd78d
--- /dev/null
+++ b/skyquake/framework/style/fonts/Roboto-Regular-webfont.ttf
Binary files 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 0000000..bfa05d5
--- /dev/null
+++ b/skyquake/framework/style/fonts/Roboto-Regular-webfont.woff
Binary files 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 0000000..bbc67d8
--- /dev/null
+++ b/skyquake/framework/style/fonts/RobotoCondensed-Bold-webfont.eot
Binary files 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 0000000..417a2a9
--- /dev/null
+++ b/skyquake/framework/style/fonts/RobotoCondensed-Bold-webfont.svg
@@ -0,0 +1,643 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
+<svg xmlns="http://www.w3.org/2000/svg">
+<metadata></metadata>
+<defs>
+<font id="roboto_condensedbold" horiz-adv-x="995" >
+<font-face units-per-em="2048" ascent="1638" descent="-410" />
+<missing-glyph horiz-adv-x="449" />
+<glyph unicode="fi" horiz-adv-x="1107" d="M18 876v206h133v70q0 207 112 318t316 111q70 0 138.5 -15t153.5 -44l-40 -232q-65 22 -115.5 34t-118.5 12q-80 0 -121 -47t-41 -137v-70h173v-206h-173v-876h-284v876h-133zM716 0v1082h285v-1082h-285z" />
+<glyph horiz-adv-x="2048" />
+<glyph horiz-adv-x="2048" />
+<glyph unicode="
" horiz-adv-x="449" />
+<glyph horiz-adv-x="16" />
+<glyph unicode="	" horiz-adv-x="16" />
+<glyph unicode=" " horiz-adv-x="449" />
+<glyph unicode="	" horiz-adv-x="449" />
+<glyph unicode=" " horiz-adv-x="449" />
+<glyph unicode="!" horiz-adv-x="506" d="M112 506v950h285v-950h-285zM115 0v258h280v-258h-280z" />
+<glyph unicode=""" horiz-adv-x="602" d="M51 1046v514h198v-263l-74 -251h-124zM351 1046l1 247v267h196v-263l-74 -251h-123z" />
+<glyph unicode="#" horiz-adv-x="1034" d="M44 410v166h209l43 298h-189v166h213l61 416h172l-60 -416h136l60 416h173l-60 -416h186v-166h-210l-43 -298h189v-166h-213l-59 -410h-173l59 410h-136l-59 -410h-173l59 410h-185zM426 576h136l43 298h-136z" />
+<glyph unicode="$" horiz-adv-x="1019" d="M79 456h284q0 -135 44 -193.5t115 -58.5q63 0 96 49t33 128q0 84 -33 133t-115 96q-194 86 -285.5 183t-91.5 279q0 166 93.5 274t250.5 127v212h144v-215q149 -28 236.5 -148.5t87.5 -313.5h-284q0 117 -35 180t-92 63q-59 0 -88 -50t-29 -130q0 -76 32.5 -125 t117.5 -97q192 -91 283.5 -189t91.5 -277q0 -171 -92 -275.5t-248 -123.5v-199h-145v199q-161 20 -266 135t-105 337z" />
+<glyph unicode="%" horiz-adv-x="1356" d="M90 1099v77q0 129 74.5 215t209.5 86q137 0 211 -85.5t74 -215.5v-77q0 -130 -74 -214.5t-209 -84.5q-136 0 -211 85t-75 214zM276 1099q0 -57 26 -96.5t74 -39.5q46 0 71.5 39t25.5 97v77q0 58 -26 98t-73 40t-72.5 -40.5t-25.5 -97.5v-77zM283 184l601 1138l134 -74 l-601 -1138zM700 280v77q0 128 75 214t210 86q137 0 211 -85.5t74 -214.5v-77q0 -130 -74 -215t-209 -85q-136 0 -211.5 85t-75.5 215zM887 280q0 -56 28.5 -97t71.5 -40q55 -1 76 36t21 101v77q0 57 -26 97t-73 40q-48 0 -73 -40t-25 -97v-77z" />
+<glyph unicode="&" horiz-adv-x="1221" d="M59 388q0 118 57.5 203.5t175.5 178.5q-62 94 -92 170.5t-30 156.5q0 171 98.5 275.5t262.5 104.5q142 0 232.5 -98.5t90.5 -238.5q0 -97 -43.5 -177.5t-117.5 -139.5l-81 -65l230 -304l6 1q27 52 42.5 117.5t15.5 138.5h208q0 -138 -29.5 -252.5t-92.5 -203.5l194 -255 h-322l-64 84q-75 -53 -152.5 -79t-166.5 -26q-193 0 -307.5 113t-114.5 296zM345 406q0 -87 43.5 -143.5t109.5 -56.5q44 0 88 12t85 37l-259 344l-7 -6q-35 -48 -47.5 -93.5t-12.5 -93.5zM423 1100q0 -41 17.5 -84t52.5 -94l59 44q50 36 67 73.5t17 82.5q0 50 -29.5 88.5 t-76.5 38.5q-52 0 -79.5 -42.5t-27.5 -106.5z" />
+<glyph unicode="'" horiz-adv-x="302" d="M52 1275v285h190v-272l-66 -281h-123z" />
+<glyph unicode="(" horiz-adv-x="633" d="M109 567v24q0 384 131 658.5t297 356.5h6l50 -143q-105 -98 -177.5 -317.5t-72.5 -552.5v-28q0 -333 72 -552t178 -319l-50 -141h-6q-166 82 -297 355t-131 659z" />
+<glyph unicode=")" horiz-adv-x="632" d="M38 -306q104 98 177 318t73 553v28q0 332 -73.5 553t-176.5 319l52 141h6q169 -80 306 -361t137 -654v-24q0 -375 -137 -654.5t-306 -359.5h-6z" />
+<glyph unicode="*" horiz-adv-x="825" d="M31 1058l53 173l260 -127l-19 352h178l-19 -358l253 125l53 -175l-268 -93l176 -269l-144 -107l-149 294l-150 -285l-143 103l183 275z" />
+<glyph unicode="+" d="M54 556v254h307v396h269v-396h306v-254h-306v-410h-269v410h-307z" />
+<glyph unicode="," horiz-adv-x="459" d="M47 -309l59 319v231h252v-239l-145 -311h-166z" />
+<glyph unicode="-" horiz-adv-x="748" d="M120 511v228h497v-228h-497z" />
+<glyph unicode="." horiz-adv-x="543" d="M124 0v258h281v-258h-281z" />
+<glyph unicode="/" horiz-adv-x="646" d="M-12 -125l432 1581h206l-434 -1581h-204z" />
+<glyph unicode="0" horiz-adv-x="1014" d="M80 571v313q0 290 115.5 441.5t310.5 151.5t311 -151.5t116 -441.5v-313q0 -291 -115.5 -441.5t-309.5 -150.5q-196 0 -312 150.5t-116 441.5zM364 548q0 -178 37 -259.5t104 -75.5h6q66 -6 102 75t36 260v361q0 176 -37 257t-103 76h-6q-67 5 -103 -76t-36 -257v-361z " />
+<glyph unicode="1" horiz-adv-x="1019" d="M128 1153v205l554 99v-1457h-284v1159z" />
+<glyph unicode="2" horiz-adv-x="1019" d="M64 1004q-4 198 112 335.5t312 137.5q200 0 312 -116t112 -316q0 -134 -62.5 -248t-208.5 -298l-190 -272h492v-227h-860v193l397 503q79 112 113.5 195t34.5 151q0 93 -36 150t-104 57q-70 0 -107.5 -69t-37.5 -181h-277z" />
+<glyph unicode="3" horiz-adv-x="1019" d="M58 394l2 6h276q0 -84 44 -139t109 -55q74 0 117.5 55.5t43.5 148.5q0 116 -42.5 169.5t-125.5 53.5h-142v220h142q76 0 113 54t37 151q0 85 -37 138t-106 53q-56 0 -96.5 -47t-40.5 -123h-275l-2 6q-5 173 113.5 282.5t292.5 109.5q198 0 316 -108.5t118 -306.5 q0 -99 -51 -183.5t-140 -131.5q102 -42 156 -130.5t54 -210.5q0 -199 -127.5 -313t-326.5 -114q-175 0 -301 107.5t-121 307.5z" />
+<glyph unicode="4" horiz-adv-x="1019" d="M53 496l497 960h284v-910h126v-228h-126v-318h-283v318h-483zM311 546h240v503l-6 1l-15 -27z" />
+<glyph unicode="5" horiz-adv-x="1019" d="M86 393l2 5l276 15q0 -98 40 -152.5t105 -54.5q75 0 109 71.5t34 187.5q0 129 -38 204t-116 75q-59 0 -91 -32t-44 -85l-253 17l72 812h712v-233h-480l-31 -343q31 31 77.5 51.5t97.5 22.5q182 3 279.5 -125.5t97.5 -361.5q0 -208 -110.5 -348t-315.5 -140 q-174 0 -300.5 111.5t-122.5 302.5z" />
+<glyph unicode="6" horiz-adv-x="1019" d="M80 563v300q0 283 144.5 448.5t369.5 165.5q71 0 129.5 -15.5t119.5 -46.5l-48 -215q-53 23 -97.5 34.5t-102.5 11.5q-104 0 -166.5 -94.5t-59.5 -267.5l4 -4q39 45 100 71t136 26q165 0 257.5 -139t92.5 -356q0 -218 -119 -360.5t-307 -142.5q-202 0 -327.5 156 t-125.5 428zM364 560q0 -171 45.5 -263t123.5 -92q64 0 103 81.5t39 195.5q0 121 -40 196t-103 75q-59 0 -101 -20.5t-67 -57.5v-115z" />
+<glyph unicode="7" horiz-adv-x="1019" d="M55 1228v228h886v-228q-197 -253 -261.5 -486.5t-93.5 -601.5l-13 -140h-285l14 140q27 327 116.5 598t250.5 490h-614z" />
+<glyph unicode="8" horiz-adv-x="1019" d="M83 398q0 117 56 207.5t155 139.5q-87 47 -136 131t-49 194q0 193 110 300t290 107t290.5 -107t110.5 -300q0 -110 -49.5 -194t-135.5 -131q98 -50 154 -140t56 -207q0 -203 -117 -311t-308 -108t-309 108.5t-118 310.5zM368 419q0 -100 38 -156.5t104 -56.5 q64 0 102.5 56.5t38.5 156.5q0 98 -39 155.5t-103 57.5q-65 0 -103 -57.5t-38 -155.5zM395 1056q0 -92 29.5 -144.5t85.5 -52.5t85 52t29 145q0 89 -29.5 141t-85.5 52t-85 -51t-29 -142z" />
+<glyph unicode="9" horiz-adv-x="1019" d="M75 957q0 219 120.5 369.5t304.5 150.5q191 0 311 -154.5t120 -430.5v-356q0 -256 -136.5 -406.5t-345.5 -150.5q-62 0 -131 16t-128 46l32 212q55 -24 108.5 -35.5t118.5 -11.5q86 0 142.5 82t56.5 230v56q-40 -58 -93.5 -88t-113.5 -30q-171 0 -268.5 134.5 t-97.5 366.5zM358 957q0 -131 35 -209t102 -78q50 0 88.5 24.5t64.5 66.5v166q0 158 -40 239t-105 77h-6q-61 4 -100 -82.5t-39 -203.5z" />
+<glyph unicode=":" horiz-adv-x="540" d="M127 0v258h281v-258h-281zM127 823v258h281v-258h-281z" />
+<glyph unicode=";" horiz-adv-x="516" d="M89 -309l59 319v231h252v-239l-145 -311h-166zM118 823v258h281v-258h-281z" />
+<glyph unicode="<" horiz-adv-x="898" d="M48 430v230l739 364v-270l-482 -209v-6l482 -203v-271z" />
+<glyph unicode="=" horiz-adv-x="1023" d="M120 323v229h772v-229h-772zM120 756v228h772v-228h-772z" />
+<glyph unicode=">" horiz-adv-x="909" d="M106 65v269l487 209v6l-487 206v269l745 -364v-230z" />
+<glyph unicode="?" horiz-adv-x="898" d="M25 1075q-3 197 111 299.5t299 102.5q200 0 314.5 -115.5t114.5 -312.5q0 -125 -64 -231.5t-160 -178.5q-51 -46 -68.5 -94.5t-17.5 -131.5h-284q1 136 39.5 202t147.5 159q56 55 89 121.5t33 152.5q0 92 -37.5 146t-106.5 54q-53 0 -92 -46.5t-40 -132.5h-276zM271 0 v247h281v-247h-281z" />
+<glyph unicode="@" horiz-adv-x="1615" d="M56 478q15 428 226 682t558 254q342 0 526 -241t170 -657q-8 -219 -115.5 -378t-321.5 -159q-72 0 -123.5 44.5t-72.5 126.5q-38 -85 -93.5 -126.5t-129.5 -41.5q-128 0 -196.5 119t-50.5 315q22 252 127.5 404t255.5 152q106 0 171 -26t142 -80l-3 -4h5l-46 -569 q-5 -97 10 -132t41 -28l6 -1q96 -5 154.5 105t66.5 279q13 350 -119 546.5t-408 196.5q-266 0 -422 -210.5t-168 -570.5q-16 -352 126 -557.5t407 -205.5q75 0 155 19.5t138 49.5l35 -148q-60 -42 -151.5 -65.5t-179.5 -23.5q-353 0 -544.5 249t-175.5 682zM649 416 q-8 -127 15.5 -194t75.5 -67q46 0 83.5 28.5t66.5 103.5v6l39 496q-19 5 -37.5 8.5t-37.5 3.5q-95 0 -142.5 -95t-62.5 -290z" />
+<glyph unicode="A" horiz-adv-x="1134" d="M16 0l410 1456h296l409 -1456h-287l-79 309h-384l-78 -309h-287zM439 538h268l-131 515h-6z" />
+<glyph unicode="B" horiz-adv-x="1136" d="M110 0v1456h429q225 0 351.5 -100t126.5 -298q0 -104 -46 -184.5t-138 -120.5q115 -27 170 -117t55 -211q0 -209 -119.5 -317t-342.5 -108h-486zM395 227h201q90 0 134.5 50t44.5 149q0 104 -38 160t-122 56h-220v-415zM395 841h157q87 2 133.5 50.5t46.5 139.5 q0 102 -48 149.5t-145 47.5h-144v-387z" />
+<glyph unicode="C" horiz-adv-x="1088" d="M74 510v435q0 253 130.5 392.5t349.5 139.5q232 0 350.5 -124t122.5 -367l-3 -6h-274q1 17 1 33q0 118 -40 173q-45 63 -157 63q-96 0 -147 -75t-51 -227v-437q0 -154 54.5 -229t159.5 -75q104 0 144 60q36 53 36 172q0 15 -1 31h274l3 -6v-8q0 -234 -114 -354 q-116 -122 -342 -122t-361 138.5t-135 392.5z" />
+<glyph unicode="D" horiz-adv-x="1151" d="M110 0v1456h440q224 0 370 -169.5t146 -436.5v-245q0 -267 -146 -436t-370 -169h-440zM395 227h139q119 0 183.5 103.5t64.5 274.5v247q0 170 -64.5 273t-183.5 103h-139v-1001z" />
+<glyph unicode="E" horiz-adv-x="1008" d="M110 0v1456h858v-228h-573v-362h489v-227h-489v-412h575v-227h-860z" />
+<glyph unicode="F" horiz-adv-x="1016" d="M110 0v1456h866v-228h-581v-396h494v-227h-494v-605h-285z" />
+<glyph unicode="G" horiz-adv-x="1179" d="M81 506v444q0 251 136.5 389t361.5 138q242 0 363 -118.5t126 -339.5l-2 -6h-267q-3 123 -54.5 179.5t-157.5 56.5q-104 0 -163 -73.5t-59 -223.5v-446q0 -153 59.5 -226.5t173.5 -73.5q85 0 125.5 23t66.5 68v261h-192v200h476v-542q-59 -94 -177 -165.5t-299 -71.5 q-234 0 -375.5 137t-141.5 390z" />
+<glyph unicode="H" horiz-adv-x="1241" d="M110 0v1456h285v-626h452v626h284v-1456h-284v603h-452v-603h-285z" />
+<glyph unicode="I" horiz-adv-x="534" d="M125 0v1456h283v-1456h-283z" />
+<glyph unicode="J" horiz-adv-x="1015" d="M57 421l3 6h277q0 -115 40.5 -168t104.5 -53q58 0 99.5 63t41.5 175v1012h283v-1012q0 -214 -120.5 -339.5t-303.5 -125.5q-198 0 -314 112q-111 108 -111 316v14z" />
+<glyph unicode="K" horiz-adv-x="1137" d="M110 0v1456h285v-592h92l284 592h349l-397 -665l430 -791h-346l-288 605h-124v-605h-285z" />
+<glyph unicode="L" horiz-adv-x="959" d="M110 0v1456h285v-1229h532v-227h-817z" />
+<glyph unicode="M" horiz-adv-x="1481" d="M105 0v1456h369l261 -1069h6l264 1069h371v-1456h-283v423l24 566l-6 1l-278 -990h-189l-275 982l-6 -1l25 -558v-423h-283z" />
+<glyph unicode="N" horiz-adv-x="1241" d="M110 0v1456h285l445 -930l6 2v928h283v-1456h-283l-445 931l-6 -2v-929h-285z" />
+<glyph unicode="O" horiz-adv-x="1166" d="M75 523v408q0 255 138 400.5t368 145.5t370 -145.5t140 -400.5v-408q0 -256 -139 -400t-370 -144t-369 144t-138 400zM357 523q0 -158 57 -237t168 -79q110 0 168 79t58 237v410q0 156 -58 235.5t-169 79.5q-112 0 -168 -79t-56 -236v-410z" />
+<glyph unicode="P" horiz-adv-x="1158" d="M110 0v1456h506q218 0 344 -129t126 -338q0 -210 -126.5 -338.5t-343.5 -128.5h-221v-522h-285zM395 750h221q91 0 139 67t48 170q0 104 -47.5 172.5t-139.5 68.5h-221v-478z" />
+<glyph unicode="Q" horiz-adv-x="1241" d="M81 523v408q0 255 138 400.5t368 145.5t370 -145.5t140 -400.5v-408q0 -113 -29 -206t-82 -161l216 -237l-176 -150l-224 249q-49 -19 -102.5 -29t-111.5 -10q-231 0 -369 144t-138 400zM363 523q0 -158 57 -237t168 -79q110 0 168 79t58 237v410q0 156 -58 235.5 t-169 79.5q-112 0 -168 -79t-56 -236v-410z" />
+<glyph unicode="R" horiz-adv-x="1162" d="M111 0v1455h475q215 0 337.5 -113.5t122.5 -315.5q0 -114 -51 -196.5t-149 -131.5q112 -38 161.5 -126t49.5 -220v-103q0 -60 14 -128.5t49 -99.5v-21h-294q-35 32 -44 104t-9 147v99q0 109 -44.5 169t-127.5 60h-206v-579h-284zM395 806h190q87 0 132 53t45 151 q0 100 -45 159t-131 59h-191v-422z" />
+<glyph unicode="S" horiz-adv-x="1109" d="M72 449l2 6h277q0 -135 57 -194t172 -59q90 0 133.5 48t43.5 130q0 85 -44 134.5t-155 97.5q-227 79 -340.5 179.5t-113.5 281.5q0 179 132 291.5t337 112.5q208 1 338 -125q126 -122 126 -306v-11l-2 -6h-276q0 100 -50 162t-141 62q-85 0 -132.5 -51.5t-47.5 -129.5 q0 -72 53 -118.5t184 -103.5q207 -70 311.5 -177.5t104.5 -290.5q0 -189 -126 -296t-335 -107q-208 -1 -360 121q-148 118 -148 335v14z" />
+<glyph unicode="T" horiz-adv-x="994" d="M29 1228v228h938v-228h-328v-1228h-284v1228h-326z" />
+<glyph unicode="U" horiz-adv-x="1176" d="M94 465v991h284v-991q0 -133 55 -196t155 -63q103 0 157 60t54 199v991h283v-991q0 -233 -136.5 -359.5t-357.5 -126.5t-357.5 127t-136.5 359z" />
+<glyph unicode="V" horiz-adv-x="1129" d="M9 1456h299l241 -1084l11 -56h6l11 53l243 1087h299l-407 -1456h-296z" />
+<glyph unicode="W" horiz-adv-x="1548" d="M21 1456h276l157 -929h6l210 929h204l213 -929h6l157 929h276l-281 -1456h-265l-205 892h-6l-203 -892h-264z" />
+<glyph unicode="X" horiz-adv-x="1127" d="M27 0l362 734l-353 722h327l195 -509h6l198 509h329l-353 -722l377 -734h-343l-204 520h-6l-204 -520h-331z" />
+<glyph unicode="Y" horiz-adv-x="1124" d="M7 1456h311l240 -624h6l243 624h309l-416 -938v-518h-284v536z" />
+<glyph unicode="Z" horiz-adv-x="998" d="M60 1229v227h876v-138l-550 -1091h559v-227h-879v143l546 1086h-552z" />
+<glyph unicode="[" horiz-adv-x="530" d="M106 -339v2034h399v-217h-112v-1601h112v-216h-399z" />
+<glyph unicode="\" horiz-adv-x="758" d="M0 1456h280l515 -1581h-282z" />
+<glyph unicode="]" horiz-adv-x="530" d="M21 -123h114v1601h-114v217h399v-2034h-399v216z" />
+<glyph unicode="^" horiz-adv-x="785" d="M38 729l253 727h206l253 -727h-219l-134 422l-2 8h-6l-1 -8l-131 -422h-219z" />
+<glyph unicode="_" horiz-adv-x="793" d="M8 0h775v-220h-775v220z" />
+<glyph unicode="`" horiz-adv-x="629" d="M73 1471l3 6h298l175 -266h-230z" />
+<glyph unicode="a" horiz-adv-x="944" d="M57 302q0 159 102.5 247t304.5 88h107v88q0 79 -28.5 121.5t-85.5 42.5q-48 0 -72 -34t-24 -99l-275 1l-3 6v12q1 133 105 228q110 99 288 99q169 0 274.5 -98.5t105.5 -280.5v-438q0 -80 11 -149t34 -136h-282q-12 27 -21.5 59t-14.5 65q-40 -66 -97.5 -105.5 t-133.5 -39.5q-134 0 -214.5 88t-80.5 235zM341 315q0 -54 25 -88t68 -34t79.5 23t57.5 59v199h-107q-65 0 -94 -45t-29 -114z" />
+<glyph unicode="b" d="M92 0v1560h284v-570q38 54 89.5 83t116.5 29q176 0 267 -135.5t91 -361.5v-147q0 -219 -90.5 -349t-265.5 -130q-76 0 -132.5 35t-96.5 102l-17 -116h-246zM376 281q20 -37 53.5 -55.5t80.5 -18.5q86 0 116.5 63.5t30.5 187.5v147q0 127 -31.5 198t-116.5 71 q-46 0 -79 -23t-54 -65v-505z" />
+<glyph unicode="c" horiz-adv-x="910" d="M52 470v139q0 216 112 354.5t321 138.5q175 0 279 -113q101 -110 101 -294v-11l-2 -5h-259q0 88 -29.5 141.5t-89.5 53.5q-85 0 -117 -72t-32 -193v-139q0 -124 32 -194t119 -70q58 0 87.5 43t29.5 124h258l3 -6v-9q-1 -164 -105 -270q-108 -109 -273 -109 q-210 0 -322.5 137t-112.5 354z" />
+<glyph unicode="d" d="M54 514v21q0 255 94 411t265 156q63 0 114 -30t91 -86v574h284v-1560h-247l-19 118q-42 -68 -97.5 -103.5t-126.5 -35.5q-171 0 -264.5 146.5t-93.5 388.5zM338 514q0 -145 33.5 -226t113.5 -81q44 0 77 20.5t56 59.5v492q-23 45 -56 70t-75 25q-79 0 -114 -93t-35 -246 v-21z" />
+<glyph unicode="e" horiz-adv-x="938" d="M69 451v152q0 219 111.5 359t308.5 140q190 0 294 -127t104 -346v-174h-535v-4q0 -107 48.5 -176t146.5 -69q80 0 130 18t119 60l72 -181q-56 -54 -149.5 -89t-205.5 -35q-204 0 -324 134t-120 338zM353 645h255v40q0 90 -28 139.5t-91 49.5q-68 0 -100 -62t-36 -167z " />
+<glyph unicode="f" horiz-adv-x="647" d="M23 876v206h133v120q0 182 94 280.5t264 98.5q33 0 66 -5.5t76 -15.5l-24 -218q-20 4 -38.5 7t-43.5 3q-54 0 -82 -39t-28 -111v-120h174v-206h-174v-876h-284v876h-133z" />
+<glyph unicode="g" horiz-adv-x="994" d="M54 458v133q0 229 94.5 370t266.5 141q72 0 127 -36.5t96 -104.5l18 121h247v-1061q0 -213 -123.5 -335.5t-338.5 -122.5q-70 0 -149 22.5t-147 60.5l50 219q57 -32 116 -48t128 -16q90 0 135 56.5t45 164.5v62q-40 -52 -91 -78.5t-115 -26.5q-171 0 -265 131.5 t-94 347.5zM338 458q0 -118 34 -184.5t114 -66.5q44 0 77.5 18t55.5 53v512q-22 40 -54.5 62t-76.5 22q-80 0 -115 -77.5t-35 -205.5v-133z" />
+<glyph unicode="h" d="M84 0v1560h284v-607q42 71 101 110t130 39q144 0 226 -114.5t82 -353.5v-634h-285v636q0 134 -31.5 186t-98.5 52q-40 0 -71 -17.5t-53 -48.5v-808h-284z" />
+<glyph unicode="i" horiz-adv-x="495" d="M105 0v1082h284v-1082h-284zM105 1343v217h284v-217h-284z" />
+<glyph unicode="j" horiz-adv-x="487" d="M-73 -419l12 223q22 -6 40 -9t40 -3q44 0 67.5 44t23.5 131v1115h285v-1115q0 -194 -90 -299t-250 -105q-35 0 -64 4.5t-64 13.5zM105 1343v217h284v-217h-284z" />
+<glyph unicode="k" horiz-adv-x="963" d="M92 0v1560h284v-886h47l183 408h327l-282 -487l329 -595h-325l-227 450h-52v-450h-284z" />
+<glyph unicode="l" horiz-adv-x="495" d="M105 0v1560h284v-1560h-284z" />
+<glyph unicode="m" horiz-adv-x="1507" d="M93 0v1082h265l11 -138q43 76 108 117t149 41q82 0 143 -43.5t93 -132.5q42 84 108 130t154 46q134 0 211.5 -110.5t77.5 -337.5v-654h-285v654q0 125 -27 172.5t-84 47.5q-41 0 -73 -24t-53 -66q1 -19 2 -37.5t1 -38.5v-708h-283v654q0 124 -27.5 172t-85.5 48 q-39 0 -69.5 -19.5t-52.5 -54.5v-800h-283z" />
+<glyph unicode="n" d="M87 0v1082h265l12 -149q45 80 110.5 124.5t144.5 44.5q135 0 212.5 -104t77.5 -327v-671h-285v671q0 112 -31.5 157.5t-97.5 45.5q-38 0 -70 -20t-54 -55v-799h-284z" />
+<glyph unicode="o" d="M58 473v134q0 218 115.5 356.5t323.5 138.5t324 -138.5t116 -356.5v-134q0 -220 -116 -357t-323 -137q-209 0 -324.5 137t-115.5 357zM341 473q0 -125 35 -196t122 -71q84 0 119.5 71.5t35.5 195.5v134q0 122 -35.5 194.5t-120.5 72.5t-120.5 -73t-35.5 -194v-134z" />
+<glyph unicode="p" d="M92 -416v1498h263l10 -107q39 61 92.5 94t123.5 33q171 0 265 -140.5t94 -370.5v-133q0 -216 -94 -347.5t-263 -131.5q-66 0 -117.5 26.5t-89.5 78.5v-500h-284zM376 278q20 -36 52.5 -54t79.5 -18q79 0 113.5 67t34.5 185v133q0 128 -36 205.5t-113 77.5 q-45 0 -78 -22.5t-53 -63.5v-510z" />
+<glyph unicode="q" d="M54 458v133q0 229 94 370t265 141q71 0 125.5 -35.5t95.5 -101.5l20 117h247v-1498h-285v499q-39 -51 -89.5 -77.5t-114.5 -26.5q-170 0 -264 131.5t-94 347.5zM338 458q0 -119 33.5 -185.5t113.5 -66.5q44 0 77 18t54 53v515q-21 39 -53 60.5t-76 21.5q-79 0 -114 -77.5 t-35 -205.5v-133z" />
+<glyph unicode="r" horiz-adv-x="655" d="M92 0v1082h265l11 -145l5 -2q26 78 72.5 122.5t108.5 44.5q19 0 40.5 -3.5t37.5 -8.5l-29 -267l-107 3q-45 0 -74.5 -22t-45.5 -62v-742h-284z" />
+<glyph unicode="s" horiz-adv-x="888" d="M39 334l2 6h260q2 -86 40 -124t105 -38q57 0 87 28t30 77q0 46 -36 79.5t-138 82.5q-163 61 -244.5 138t-81.5 200q0 132 104.5 225.5t276.5 93.5q180 0 287 -96q103 -93 103 -234v-11l-3 -6h-274q0 68 -28 108t-86 40q-46 0 -78 -31t-32 -75q0 -48 32 -77.5t139 -80.5 q170 -58 252 -137.5t82 -208.5q0 -138 -110 -226t-289 -88q-187 0 -296 109q-104 104 -104 234v12z" />
+<glyph unicode="t" horiz-adv-x="648" d="M18 876v206h128v269h284v-269h150v-206h-150v-543q0 -66 19 -94.5t52 -28.5q26 0 44 2.5t40 8.5l22 -216q-45 -14 -86 -20t-93 -6q-137 0 -209.5 84.5t-72.5 268.5v544h-128z" />
+<glyph unicode="u" d="M85 428v654h284v-656q0 -115 28 -167t83 -52q47 0 82.5 17.5t60.5 51.5v806h285v-1082h-250l-17 125q-44 -71 -106.5 -108.5t-141.5 -37.5q-145 0 -226.5 109.5t-81.5 339.5z" />
+<glyph unicode="v" horiz-adv-x="909" d="M15 1082h297l127 -674l13 -82h6l14 82l125 674h297l-300 -1082h-278z" />
+<glyph unicode="w" horiz-adv-x="1280" d="M24 1082h265l101 -616h6l151 616h184l151 -614l6 1l101 613h263l-222 -1082h-237l-151 583h-6l-151 -583h-237z" />
+<glyph unicode="x" horiz-adv-x="909" d="M16 0l275 547l-268 535h319l109 -341h6l114 341h320l-268 -535l276 -547h-319l-119 351h-6l-119 -351h-320z" />
+<glyph unicode="y" horiz-adv-x="909" d="M5 1082h305l130 -638l2 -11l158 649h304l-365 -1245q-41 -117 -109.5 -195.5t-209.5 -78.5q-35 0 -62 6t-72 18l33 212q17 -3 24.5 -4.5t16.5 -1.5q62 0 93.5 32.5t45.5 85.5l25 80z" />
+<glyph unicode="z" horiz-adv-x="883" d="M65 0v169l412 685h-400v228h730v-165l-416 -690h423v-227h-749z" />
+<glyph unicode="{" horiz-adv-x="586" d="M44 514v202q69 0 103 55t34 154v207q0 171 71 290.5t238 174.5l53 -157q-62 -31 -86 -109.5t-24 -198.5v-207q0 -104 -41 -184.5t-124 -125.5q82 -46 123.5 -126.5t41.5 -183.5v-206q0 -120 23.5 -198t86.5 -109l-53 -158q-167 55 -238 175t-71 290v206q0 98 -34 153.5 t-103 55.5z" />
+<glyph unicode="|" horiz-adv-x="467" d="M153 -270v1726h163v-1726h-163z" />
+<glyph unicode="}" horiz-adv-x="586" d="M42 -208q62 31 87 109t25 198v206q0 104 41 184.5t126 125.5q-85 44 -126 124.5t-41 185.5v207q0 120 -25 198.5t-87 109.5l53 157q166 -55 237.5 -174.5t71.5 -290.5v-207q0 -100 33.5 -154.5t105.5 -54.5v-202q-72 0 -105.5 -55t-33.5 -154v-206q0 -170 -71.5 -290 t-237.5 -175z" />
+<glyph unicode="~" horiz-adv-x="1121" d="M89 444q0 165 74.5 273t193.5 108q69 0 132.5 -33.5t125.5 -98.5q37 -46 66 -65.5t63 -19.5q37 0 64.5 52.5t27.5 128.5l194 -28q0 -164 -76.5 -273.5t-193.5 -109.5q-71 0 -131.5 32t-124.5 102q-40 45 -68 64.5t-63 19.5q-38 0 -64.5 -51.5t-26.5 -126.5z" />
+<glyph unicode="¡" horiz-adv-x="534" d="M125 -374v949h285v-949h-285zM128 821v261h280v-261h-280z" />
+<glyph unicode="¢" horiz-adv-x="1031" d="M89 470v139q0 189 87.5 321t252.5 163v225h184v-225q136 -29 215 -137.5t79 -276.5h-266q0 88 -29.5 141.5t-89.5 53.5q-85 0 -117 -72t-32 -193v-139q0 -124 32 -194t119 -70q58 0 87.5 43t29.5 124h266q0 -149 -81 -252.5t-213 -131.5v-234h-184v233q-166 30 -253 161 t-87 321z" />
+<glyph unicode="£" horiz-adv-x="1055" d="M92 591v225h122l-7 209q0 211 105 331.5t282 120.5q188 0 293 -113t102 -297l-3 -6h-276q0 96 -34 142t-83 46q-48 0 -74.5 -58.5t-26.5 -165.5l8 -209h280v-225h-270l3 -82q0 -78 -29.5 -151t-83.5 -131h600v-227h-855v227h8q36 12 54 94.5t18 171.5l-3 98h-130z" />
+<glyph unicode="¤" horiz-adv-x="1257" d="M74 110l122 140q-45 76 -68.5 167t-23.5 191q0 103 25.5 196.5t73.5 172.5l-129 150l130 137l123 -142q65 54 142.5 83t161.5 29t161.5 -30t143.5 -84l125 145l132 -138l-133 -153q46 -78 71.5 -171.5t25.5 -194.5q0 -99 -23.5 -189t-66.5 -166l126 -143l-132 -137 l-114 131q-67 -60 -147.5 -92t-168.5 -32t-168 31.5t-147 90.5l-112 -128zM276 608q0 -189 103 -318.5t252 -129.5q147 0 250 129.5t103 318.5q0 187 -103 316.5t-250 129.5q-149 0 -252 -129.5t-103 -316.5z" />
+<glyph unicode="¥" horiz-adv-x="1082" d="M22 1456h310l207 -561h6l206 561h310l-318 -719h189v-200h-253v-111h253v-198h-253v-228h-283v228h-271v198h271v111h-271v200h215z" />
+<glyph unicode="¦" horiz-adv-x="467" d="M108 -270v791h251v-791h-251zM108 698v758h251v-758h-251z" />
+<glyph unicode="§" horiz-adv-x="1115" d="M84 533q0 88 36.5 156.5t106.5 113.5q-63 51 -94.5 122t-31.5 171q0 172 123 276.5t329 104.5q210 0 329 -111.5t115 -310.5l-2 -6h-276q0 87 -43.5 143.5t-122.5 56.5q-84 0 -125 -42.5t-41 -108.5q0 -74 43 -112.5t181 -85.5q210 -63 309 -155.5t99 -262.5 q0 -90 -37 -157.5t-105 -110.5q62 -51 93.5 -122t31.5 -171q0 -177 -121.5 -277t-326.5 -100q-201 0 -335 99t-129 317l2 6h276q0 -105 52.5 -149.5t133.5 -44.5q78 0 121 40.5t43 107.5q0 66 -47.5 106t-180.5 89q-208 64 -307 156.5t-99 261.5zM368 564q0 -80 41.5 -121 t177.5 -94q57 -19 67 -22.5l30 -10.5q27 23 41.5 58t14.5 79q0 71 -47.5 116t-177.5 97q-25 7 -49.5 16t-49.5 19q-25 -23 -36.5 -58t-11.5 -79z" />
+<glyph unicode="¨" horiz-adv-x="1000" d="M143 1258v198h251v-198h-251zM592 1258v198h250v-198h-250z" />
+<glyph unicode="©" horiz-adv-x="1433" d="M82 729q0 315 182.5 531t444.5 216q261 0 444.5 -216t183.5 -531q0 -316 -184 -533t-444 -217q-261 0 -444 217t-183 533zM193 729q0 -266 150.5 -448.5t365.5 -181.5q214 -1 364.5 181.5t150.5 448.5q0 264 -150.5 445.5t-364.5 181.5q-215 0 -365.5 -181.5 t-150.5 -445.5zM386 669v119q0 173 83.5 280t226.5 107q140 0 219 -79.5t76 -228.5l-3 -6h-136q0 96 -39 140.5t-117 45.5q-81 -1 -124 -72t-43 -186v-120q0 -118 43 -188.5t124 -70.5q78 0 116.5 43.5t38.5 142.5h136l3 -6q3 -151 -75.5 -229.5t-218.5 -78.5 q-143 0 -226.5 106t-83.5 281z" />
+<glyph unicode="ª" horiz-adv-x="785" d="M98 920q0 109 72.5 169t212.5 60h106v53q0 65 -22.5 99.5t-66.5 34.5q-51 0 -79 -27.5t-28 -78.5l-150 13l-2 6q-5 98 67.5 163t191.5 65q114 0 182 -71.5t68 -204.5v-314q0 -51 5 -95t17 -86h-162q-7 23 -11.5 47t-6.5 48q-27 -48 -73 -78.5t-110 -30.5 q-100 0 -155.5 61.5t-55.5 166.5zM258 924q0 -46 22.5 -70.5t68.5 -24.5q39 0 82 30t58 66v112h-105q-59 0 -92.5 -33.5t-33.5 -79.5z" />
+<glyph unicode="«" horiz-adv-x="904" d="M55 507v19l233 389h176l-195 -399l195 -397h-176zM407 507v19l233 389h176l-195 -399l195 -397h-176z" />
+<glyph unicode="¬" horiz-adv-x="972" d="M115 638v163h710v-426h-183v263h-527z" />
+<glyph unicode="­" horiz-adv-x="748" d="M120 511v228h497v-228h-497z" />
+<glyph unicode="®" horiz-adv-x="1433" d="M82 729q0 315 183 531t445 216q260 0 443.5 -216t183.5 -531q0 -316 -183.5 -533t-443.5 -217q-262 0 -445 217t-183 533zM194 729q0 -266 150.5 -448.5t365.5 -181.5q214 -1 364 181.5t150 448.5q0 264 -150 445.5t-364 181.5q-215 0 -365.5 -181.5t-150.5 -445.5z M445 316v850h251q134 0 212 -68t78 -193q0 -58 -26 -101.5t-76 -74.5q53 -30 77 -84.5t24 -129.5v-56q0 -42 3 -74t11 -53v-16h-143q-8 21 -9.5 61.5t-1.5 82.5v54q0 73 -29.5 107t-94.5 34h-136v-339h-140zM585 783h112q64 0 107.5 31.5t43.5 89.5q0 74 -34 104t-118 30 h-111v-255z" />
+<glyph unicode="¯" horiz-adv-x="949" d="M146 1297v160h654v-160h-654z" />
+<glyph unicode="°" horiz-adv-x="725" d="M117 1203q0 112 72.5 193t173.5 81q99 0 170.5 -81t71.5 -193q0 -113 -71 -192t-171 -79q-102 0 -174 79t-72 192zM255 1203q0 -55 31.5 -92t76.5 -37q43 0 73.5 36.5t30.5 92.5t-30.5 94t-73.5 38q-45 0 -76.5 -38.5t-31.5 -93.5z" />
+<glyph unicode="±" horiz-adv-x="992" d="M87 721v235h281v361h250v-361h279v-235h-279v-382h-250v382h-281zM98 7v230h705v-230h-705z" />
+<glyph unicode="²" horiz-adv-x="748" d="M100 1221q-5 107 71.5 182.5t205.5 75.5q126 0 194.5 -64.5t68.5 -182.5q0 -83 -48.5 -149t-155.5 -163l-79 -96l2 -5h286v-152h-539v152l262 254q47 49 60 82.5t13 77.5q0 41 -16.5 67t-53.5 26q-40 0 -59.5 -30.5t-19.5 -80.5h-190z" />
+<glyph unicode="³" horiz-adv-x="740" d="M85 895l2 6h191q0 -44 23 -69t69 -25q39 0 64.5 24.5t25.5 70.5q0 51 -27.5 79t-78.5 28h-91v127h91q48 0 70.5 25t22.5 73q0 39 -20.5 65t-60.5 26q-39 0 -58.5 -22.5t-19.5 -55.5h-189l-2 6q-6 102 70.5 163.5t193.5 61.5q131 0 208 -59.5t77 -169.5 q0 -55 -30.5 -100.5t-83.5 -71.5q60 -24 93 -71.5t33 -115.5q0 -112 -77 -173.5t-209 -61.5q-130 0 -211.5 63t-75.5 177z" />
+<glyph unicode="´" horiz-adv-x="683" d="M100 1212l176 266h299l3 -6l-249 -260h-229z" />
+<glyph unicode="µ" horiz-adv-x="1113" d="M120 -416v1498h285v-637q1 -137 33.5 -186.5t100.5 -49.5q52 0 88.5 24t59.5 70v779h284v-1082h-267l-4 51q-35 -36 -78 -54t-93 -18q-36 0 -67 9t-57 26v-430h-285z" />
+<glyph unicode="¶" horiz-adv-x="951" d="M56 988q0 207 109.5 337.5t306.5 130.5h351v-1456h-286v520h-65q-197 0 -306.5 129.5t-109.5 338.5z" />
+<glyph unicode="·" horiz-adv-x="566" d="M140 560v262h278v-262h-278z" />
+<glyph unicode="¸" horiz-adv-x="493" d="M83 -134l27 141h213l-9 -59q51 -11 86.5 -52.5t35.5 -120.5q0 -108 -80.5 -172.5t-227.5 -64.5l-6 163q34 0 53.5 20.5t19.5 63.5q0 42 -25.5 59t-86.5 22z" />
+<glyph unicode="¹" horiz-adv-x="513" d="M102 1298v156l303 23v-812h-200v634z" />
+<glyph unicode="º" horiz-adv-x="811" d="M104 1027v115q0 148 81 241.5t217 93.5t216.5 -93.5t80.5 -241.5v-115q0 -150 -80.5 -242.5t-215.5 -92.5q-137 0 -218 92.5t-81 242.5zM265 1027q0 -89 35 -142.5t103 -53.5q65 0 100 53.5t35 142.5v115q0 85 -35 139.5t-101 54.5q-67 0 -102 -54.5t-35 -139.5v-115z " />
+<glyph unicode="»" horiz-adv-x="905" d="M60 152l195 398l-195 399h176l233 -389v-19l-233 -389h-176zM422 152l195 398l-195 399h176l233 -389v-19l-233 -389h-176z" />
+<glyph unicode="¼" horiz-adv-x="1260" d="M99 1297v156l303 23v-812h-200v634zM199 192l601 1138l134 -74l-601 -1138zM593 264l356 534v3h200v-507h75v-154h-75v-140h-200v140h-343zM794 294h155v253l-6 2l-7 -12z" />
+<glyph unicode="½" horiz-adv-x="1317" d="M82 1297v156l303 23v-812h-200v634zM192 192l601 1138l134 -74l-601 -1138zM693 554q-5 107 71.5 182.5t205.5 75.5q126 0 194.5 -64.5t68.5 -182.5q0 -83 -48.5 -149t-155.5 -163l-79 -96l2 -5h286v-152h-539v152l262 254q47 49 60 82.5t13 77.5q0 41 -16.5 67t-53.5 26 q-40 0 -59.5 -30.5t-19.5 -80.5h-190z" />
+<glyph unicode="¾" horiz-adv-x="1452" d="M71 896l2 6h191q0 -44 23 -69t69 -25q39 0 64.5 24.5t25.5 70.5q0 51 -27.5 79t-78.5 28h-91v127h91q48 0 70.5 25t22.5 73q0 39 -20.5 65t-60.5 26q-39 0 -58.5 -22.5t-19.5 -55.5h-189l-2 6q-6 102 70.5 163.5t193.5 61.5q131 0 208 -59.5t77 -169.5 q0 -55 -30.5 -100.5t-83.5 -71.5q60 -24 93 -71.5t33 -115.5q0 -112 -77 -173.5t-209 -61.5q-130 0 -211.5 63t-75.5 177zM384 192l601 1138l134 -74l-601 -1138zM747 264l356 534v3h200v-507h75v-154h-75v-140h-200v140h-343zM948 294h155v253l-6 2l-7 -12z" />
+<glyph unicode="¿" horiz-adv-x="909" d="M63 32q0 125 62.5 231t159.5 179q51 44 69 92.5t18 133.5h284q-2 -137 -41 -203t-147 -158q-57 -57 -89.5 -123t-32.5 -151q0 -91 37 -144.5t107 -53.5q52 0 91.5 46t41.5 131h277l2 -6q1 -196 -113.5 -298t-298.5 -102q-200 0 -313.5 115t-113.5 311zM373 834v248h283 v-248h-283z" />
+<glyph unicode="À" horiz-adv-x="1134" d="M16 0l410 1456h296l409 -1456h-287l-79 309h-384l-78 -309h-287zM243 1819l3 6h298l175 -266h-230zM439 538h268l-131 515h-6z" />
+<glyph unicode="Á" horiz-adv-x="1134" d="M16 0l410 1456h296l409 -1456h-287l-79 309h-384l-78 -309h-287zM429 1560l176 266h299l3 -6l-249 -260h-229zM439 538h268l-131 515h-6z" />
+<glyph unicode="Â" horiz-adv-x="1134" d="M16 0l410 1456h296l409 -1456h-287l-79 309h-384l-78 -309h-287zM244 1591v25l250 237h159l256 -240v-22h-222l-114 134l-112 -134h-217zM439 538h268l-131 515h-6z" />
+<glyph unicode="Ã" horiz-adv-x="1134" d="M16 0l410 1456h296l409 -1456h-287l-79 309h-384l-78 -309h-287zM248 1645q0 93 51.5 161t129.5 68q46 0 129.5 -44t128.5 -44q27 0 46.5 32.5t19.5 80.5l148 -45q0 -95 -51 -161.5t-130 -66.5q-59 0 -136.5 44t-121.5 44q-27 0 -46 -33t-19 -79zM439 538h268l-131 515 h-6z" />
+<glyph unicode="Ä" horiz-adv-x="1134" d="M16 0l410 1456h296l409 -1456h-287l-79 309h-384l-78 -309h-287zM224 1606v198h251v-198h-251zM439 538h268l-131 515h-6zM673 1606v198h250v-198h-250z" />
+<glyph unicode="Å" horiz-adv-x="1134" d="M16 0l410 1456h296l409 -1456h-287l-79 309h-384l-78 -309h-287zM381 1739q0 80 56 135t138 55q80 0 135 -55t55 -135q0 -82 -54.5 -134t-135.5 -52q-82 0 -138 52.5t-56 133.5zM439 538h268l-131 515h-6zM489 1739q0 -42 24 -68.5t62 -26.5q37 0 60 26t23 69t-23 70 t-60 27q-38 0 -62 -27t-24 -70z" />
+<glyph unicode="Æ" horiz-adv-x="1656" d="M9 0l639 1456h891v-231h-481l12 -363h400v-230h-392l15 -402h498v-230h-774l-12 339h-329l-136 -339h-331zM573 578h224l-17 497l-6 1z" />
+<glyph unicode="Ç" horiz-adv-x="1088" d="M74 510v435q0 253 130.5 392.5t349.5 139.5q232 0 350.5 -124t122.5 -367l-3 -6h-274q6 143 -39 206t-157 63q-96 0 -147 -75t-51 -227v-437q0 -154 54.5 -229t159.5 -75q103 0 143.5 59.5t35.5 203.5h274l3 -6q2 -240 -114 -362t-342 -122t-361 138.5t-135 392.5z M414 -134l27 141h213l-9 -59q51 -11 86.5 -52.5t35.5 -120.5q0 -108 -80.5 -172.5t-227.5 -64.5l-6 163q34 0 53.5 20.5t19.5 63.5q0 42 -25.5 59t-86.5 22z" />
+<glyph unicode="È" horiz-adv-x="1008" d="M110 0v1456h858v-228h-573v-362h489v-227h-489v-412h575v-227h-860zM183 1819l3 6h298l175 -266h-230z" />
+<glyph unicode="É" horiz-adv-x="1008" d="M110 0v1456h858v-228h-573v-362h489v-227h-489v-412h575v-227h-860zM369 1560l176 266h299l3 -6l-249 -260h-229z" />
+<glyph unicode="Ê" horiz-adv-x="1008" d="M110 0v1456h858v-228h-573v-362h489v-227h-489v-412h575v-227h-860zM184 1591v25l250 237h159l256 -240v-22h-222l-114 134l-112 -134h-217z" />
+<glyph unicode="Ë" horiz-adv-x="1008" d="M110 0v1456h858v-228h-573v-362h489v-227h-489v-412h575v-227h-860zM164 1606v198h251v-198h-251zM613 1606v198h250v-198h-250z" />
+<glyph unicode="Ì" horiz-adv-x="534" d="M-61 1819l3 6h298l175 -266h-230zM125 0v1456h283v-1456h-283z" />
+<glyph unicode="Í" horiz-adv-x="534" d="M123 1560l176 266h299l3 -6l-249 -260h-229zM125 0v1456h283v-1456h-283z" />
+<glyph unicode="Î" horiz-adv-x="534" d="M-60 1591v25l250 237h159l256 -240v-22h-222l-114 134l-112 -134h-217zM125 0v1456h283v-1456h-283z" />
+<glyph unicode="Ï" horiz-adv-x="534" d="M-80 1606v198h251v-198h-251zM125 0v1456h283v-1456h-283zM369 1606v198h250v-198h-250z" />
+<glyph unicode="Ð" horiz-adv-x="1181" d="M31 655v175h109v626h440q224 0 370 -169.5t146 -436.5v-245q0 -267 -146 -436t-370 -169h-440v655h-109zM425 227h139q119 0 183.5 103.5t64.5 274.5v247q0 170 -64.5 273t-183.5 103h-139v-398h187v-175h-187v-428z" />
+<glyph unicode="Ñ" horiz-adv-x="1241" d="M110 0v1456h285l445 -930l6 2v928h283v-1456h-283l-445 931l-6 -2v-929h-285zM294 1645q0 93 51.5 161t129.5 68q46 0 129.5 -44t128.5 -44q27 0 46.5 32.5t19.5 80.5l148 -45q0 -95 -51 -161.5t-130 -66.5q-59 0 -136.5 44t-121.5 44q-27 0 -46 -33t-19 -79z" />
+<glyph unicode="Ò" horiz-adv-x="1166" d="M75 523v408q0 255 138 400.5t368 145.5t370 -145.5t140 -400.5v-408q0 -256 -139 -400t-370 -144t-369 144t-138 400zM253 1840l3 6h298l175 -266h-230zM357 523q0 -158 57 -237t168 -79q110 0 168 79t58 237v410q0 156 -58 235.5t-169 79.5q-112 0 -168 -79t-56 -236 v-410z" />
+<glyph unicode="Ó" horiz-adv-x="1166" d="M75 523v408q0 255 138 400.5t368 145.5t370 -145.5t140 -400.5v-408q0 -256 -139 -400t-370 -144t-369 144t-138 400zM357 523q0 -158 57 -237t168 -79q110 0 168 79t58 237v410q0 156 -58 235.5t-169 79.5q-112 0 -168 -79t-56 -236v-410zM439 1581l176 266h299l3 -6 l-249 -260h-229z" />
+<glyph unicode="Ô" horiz-adv-x="1166" d="M75 523v408q0 255 138 400.5t368 145.5t370 -145.5t140 -400.5v-408q0 -256 -139 -400t-370 -144t-369 144t-138 400zM254 1612v25l250 237h159l256 -240v-22h-222l-114 134l-112 -134h-217zM357 523q0 -158 57 -237t168 -79q110 0 168 79t58 237v410q0 156 -58 235.5 t-169 79.5q-112 0 -168 -79t-56 -236v-410z" />
+<glyph unicode="Õ" horiz-adv-x="1166" d="M75 523v408q0 255 138 400.5t368 145.5t370 -145.5t140 -400.5v-408q0 -256 -139 -400t-370 -144t-369 144t-138 400zM258 1666q0 93 51.5 161t129.5 68q46 0 129.5 -44t128.5 -44q27 0 46.5 32.5t19.5 80.5l148 -45q0 -95 -51 -161.5t-130 -66.5q-59 0 -136.5 44 t-121.5 44q-27 0 -46 -33t-19 -79zM357 523q0 -158 57 -237t168 -79q110 0 168 79t58 237v410q0 156 -58 235.5t-169 79.5q-112 0 -168 -79t-56 -236v-410z" />
+<glyph unicode="Ö" horiz-adv-x="1166" d="M75 523v408q0 255 138 400.5t368 145.5t370 -145.5t140 -400.5v-408q0 -256 -139 -400t-370 -144t-369 144t-138 400zM234 1627v198h251v-198h-251zM357 523q0 -158 57 -237t168 -79q110 0 168 79t58 237v410q0 156 -58 235.5t-169 79.5q-112 0 -168 -79t-56 -236v-410z M683 1627v198h250v-198h-250z" />
+<glyph unicode="×" horiz-adv-x="978" d="M57 1000l167 163l262 -302l262 302l166 -163l-275 -316l274 -315l-167 -163l-261 300l-260 -300l-165 163l272 314z" />
+<glyph unicode="Ø" horiz-adv-x="1215" d="M81 523v408q0 255 138 400.5t368 145.5q90 0 168.5 -24t141.5 -69l65 132h129l-106 -216q54 -71 83 -164t29 -205v-408q0 -256 -139 -400t-370 -144q-63 0 -120.5 11t-107.5 34l-57 -117h-127l86 176q-87 70 -134 181.5t-47 258.5zM363 523q0 -54 6 -97.5t19 -70.5l6 -1 l382 781q-28 56 -75.5 84.5t-113.5 28.5q-112 0 -168 -79t-56 -236v-410zM466 241q24 -18 54.5 -26t67.5 -8q110 0 168 79t58 237v410v4l-6 1z" />
+<glyph unicode="Ù" horiz-adv-x="1176" d="M94 465v991h284v-991q0 -133 55 -196t155 -63q103 0 157 60t54 199v991h283v-991q0 -233 -136.5 -359.5t-357.5 -126.5t-357.5 127t-136.5 359zM256 1819l3 6h298l175 -266h-230z" />
+<glyph unicode="Ú" horiz-adv-x="1176" d="M94 465v991h284v-991q0 -133 55 -196t155 -63q103 0 157 60t54 199v991h283v-991q0 -233 -136.5 -359.5t-357.5 -126.5t-357.5 127t-136.5 359zM442 1560l176 266h299l3 -6l-249 -260h-229z" />
+<glyph unicode="Û" horiz-adv-x="1176" d="M94 465v991h284v-991q0 -133 55 -196t155 -63q103 0 157 60t54 199v991h283v-991q0 -233 -136.5 -359.5t-357.5 -126.5t-357.5 127t-136.5 359zM257 1591v25l250 237h159l256 -240v-22h-222l-114 134l-112 -134h-217z" />
+<glyph unicode="Ü" horiz-adv-x="1176" d="M94 465v991h284v-991q0 -133 55 -196t155 -63q103 0 157 60t54 199v991h283v-991q0 -233 -136.5 -359.5t-357.5 -126.5t-357.5 127t-136.5 359zM237 1606v198h251v-198h-251zM686 1606v198h250v-198h-250z" />
+<glyph unicode="Ý" horiz-adv-x="1124" d="M7 1456h311l240 -624h6l243 624h309l-416 -938v-518h-284v536zM425 1559l176 266h299l3 -6l-249 -260h-229z" />
+<glyph unicode="Þ" horiz-adv-x="1079" d="M108 0v1456h285v-268h165q223 0 346.5 -124t123.5 -326q0 -203 -123.5 -326.5t-346.5 -123.5h-165v-288h-285zM393 515h165q94 0 139.5 63t45.5 158q0 96 -46 160.5t-139 64.5h-163l-2 2v-448z" />
+<glyph unicode="ß" horiz-adv-x="1132" d="M119 0v1104q0 223 114 344t315 121q161 0 268 -96t107 -269q0 -105 -42.5 -200.5t-42.5 -168.5q0 -53 118.5 -194t118.5 -282q0 -192 -98.5 -286t-296.5 -94q-66 0 -131 15t-98 40l50 224q32 -23 77 -38t100 -15q53 0 84 38t31 105q0 71 -120 203.5t-120 272.5 q0 94 44 192t44 174q0 63 -35.5 108t-80.5 45q-54 0 -88.5 -65.5t-34.5 -177.5v-1100h-283z" />
+<glyph unicode="à" horiz-adv-x="944" d="M57 302q0 159 102.5 247t304.5 88h107v88q0 79 -28.5 121.5t-85.5 42.5q-48 0 -72 -34t-24 -99l-275 1l-3 6q-5 141 104.5 240t288.5 99q169 0 274.5 -98.5t105.5 -280.5v-438q0 -80 11 -149t34 -136h-282q-12 27 -21.5 59t-14.5 65q-40 -66 -97.5 -105.5t-133.5 -39.5 q-134 0 -214.5 88t-80.5 235zM136 1497l3 6h298l175 -266h-230zM341 315q0 -54 25 -88t68 -34t79.5 23t57.5 59v199h-107q-65 0 -94 -45t-29 -114z" />
+<glyph unicode="á" horiz-adv-x="944" d="M57 302q0 159 102.5 247t304.5 88h107v88q0 79 -28.5 121.5t-85.5 42.5q-48 0 -72 -34t-24 -99l-275 1l-3 6q-5 141 104.5 240t288.5 99q169 0 274.5 -98.5t105.5 -280.5v-438q0 -80 11 -149t34 -136h-282q-12 27 -21.5 59t-14.5 65q-40 -66 -97.5 -105.5t-133.5 -39.5 q-134 0 -214.5 88t-80.5 235zM322 1238l176 266h299l3 -6l-249 -260h-229zM341 315q0 -54 25 -88t68 -34t79.5 23t57.5 59v199h-107q-65 0 -94 -45t-29 -114z" />
+<glyph unicode="â" horiz-adv-x="944" d="M57 302q0 159 102.5 247t304.5 88h107v88q0 79 -28.5 121.5t-85.5 42.5q-48 0 -72 -34t-24 -99l-275 1l-3 6q-5 141 104.5 240t288.5 99q169 0 274.5 -98.5t105.5 -280.5v-438q0 -80 11 -149t34 -136h-282q-12 27 -21.5 59t-14.5 65q-40 -66 -97.5 -105.5t-133.5 -39.5 q-134 0 -214.5 88t-80.5 235zM137 1269v25l250 237h159l256 -240v-22h-222l-114 134l-112 -134h-217zM341 315q0 -54 25 -88t68 -34t79.5 23t57.5 59v199h-107q-65 0 -94 -45t-29 -114z" />
+<glyph unicode="ã" horiz-adv-x="944" d="M57 302q0 159 102.5 247t304.5 88h107v88q0 79 -28.5 121.5t-85.5 42.5q-48 0 -72 -34t-24 -99l-275 1l-3 6q-5 141 104.5 240t288.5 99q169 0 274.5 -98.5t105.5 -280.5v-438q0 -80 11 -149t34 -136h-282q-12 27 -21.5 59t-14.5 65q-40 -66 -97.5 -105.5t-133.5 -39.5 q-134 0 -214.5 88t-80.5 235zM141 1323q0 93 51.5 161t129.5 68q46 0 129.5 -44t128.5 -44q27 0 46.5 32.5t19.5 80.5l148 -45q0 -95 -51 -161.5t-130 -66.5q-59 0 -136.5 44t-121.5 44q-27 0 -46 -33t-19 -79zM341 315q0 -54 25 -88t68 -34t79.5 23t57.5 59v199h-107 q-65 0 -94 -45t-29 -114z" />
+<glyph unicode="ä" horiz-adv-x="944" d="M57 302q0 159 102.5 247t304.5 88h107v88q0 79 -28.5 121.5t-85.5 42.5q-48 0 -72 -34t-24 -99l-275 1l-3 6q-5 141 104.5 240t288.5 99q169 0 274.5 -98.5t105.5 -280.5v-438q0 -80 11 -149t34 -136h-282q-12 27 -21.5 59t-14.5 65q-40 -66 -97.5 -105.5t-133.5 -39.5 q-134 0 -214.5 88t-80.5 235zM119 1284v198h251v-198h-251zM341 315q0 -54 25 -88t68 -34t79.5 23t57.5 59v199h-107q-65 0 -94 -45t-29 -114zM568 1284v198h250v-198h-250z" />
+<glyph unicode="å" horiz-adv-x="944" d="M57 302q0 159 102.5 247t304.5 88h107v88q0 79 -28.5 121.5t-85.5 42.5q-48 0 -72 -34t-24 -99l-275 1l-3 6q-5 141 104.5 240t288.5 99q169 0 274.5 -98.5t105.5 -280.5v-438q0 -80 11 -149t34 -136h-282q-12 27 -21.5 59t-14.5 65q-40 -66 -97.5 -105.5t-133.5 -39.5 q-134 0 -214.5 88t-80.5 235zM274 1417q0 80 56 135t138 55q80 0 135 -55t55 -135q0 -82 -54.5 -134t-135.5 -52q-82 0 -138 52.5t-56 133.5zM341 315q0 -54 25 -88t68 -34t79.5 23t57.5 59v199h-107q-65 0 -94 -45t-29 -114zM382 1417q0 -42 24 -68.5t62 -26.5q37 0 60 26 t23 69t-23 70t-60 27q-38 0 -62 -27t-24 -70z" />
+<glyph unicode="æ" horiz-adv-x="1479" d="M41 317q0 159 110.5 245.5t322.5 86.5h136v56q0 79 -30 124t-83 45q-57 0 -89.5 -37t-32.5 -92l-275 17l-2 6q-5 145 106.5 239.5t295.5 94.5q83 0 153 -23.5t121 -68.5q51 44 118 68t149 24q181 0 285 -131t104 -356v-160h-535q5 -117 51.5 -183t144.5 -66 q77 0 127.5 18.5t115.5 59.5l71 -183q-53 -49 -147.5 -85.5t-200.5 -36.5q-108 0 -192 38t-142 110q-52 -65 -135.5 -106.5t-196.5 -41.5q-168 0 -259 90.5t-91 247.5zM324 313q0 -61 30 -95t92 -34q41 0 88 27t76 65v190h-134q-71 0 -111.5 -44.5t-40.5 -108.5zM896 644 h257v27q0 94 -28 148.5t-84 54.5q-73 0 -106 -60t-39 -170z" />
+<glyph unicode="ç" horiz-adv-x="910" d="M52 470v139q0 216 112 354.5t321 138.5q176 0 279.5 -113t100.5 -305l-2 -5h-259q0 88 -29.5 141.5t-89.5 53.5q-85 0 -117 -72t-32 -193v-139q0 -124 32 -194t119 -70q58 0 87.5 43t29.5 124h258l3 -6q3 -170 -104.5 -279t-273.5 -109q-210 0 -322.5 137t-112.5 354z M346 -134l27 141h213l-9 -59q51 -11 86.5 -52.5t35.5 -120.5q0 -108 -80.5 -172.5t-227.5 -64.5l-6 163q34 0 53.5 20.5t19.5 63.5q0 42 -25.5 59t-86.5 22z" />
+<glyph unicode="è" horiz-adv-x="938" d="M69 451v152q0 219 111.5 359t308.5 140q190 0 294 -127t104 -346v-174h-535v-4q0 -107 48.5 -176t146.5 -69q80 0 130 18t119 60l72 -181q-56 -54 -149.5 -89t-205.5 -35q-204 0 -324 134t-120 338zM135 1498l3 6h298l175 -266h-230zM353 645h255v40q0 90 -28 139.5 t-91 49.5q-68 0 -100 -62t-36 -167z" />
+<glyph unicode="é" horiz-adv-x="938" d="M69 451v152q0 219 111.5 359t308.5 140q190 0 294 -127t104 -346v-174h-535v-4q0 -107 48.5 -176t146.5 -69q80 0 130 18t119 60l72 -181q-56 -54 -149.5 -89t-205.5 -35q-204 0 -324 134t-120 338zM321 1239l176 266h299l3 -6l-249 -260h-229zM353 645h255v40 q0 90 -28 139.5t-91 49.5q-68 0 -100 -62t-36 -167z" />
+<glyph unicode="ê" horiz-adv-x="938" d="M69 451v152q0 219 111.5 359t308.5 140q190 0 294 -127t104 -346v-174h-535v-4q0 -107 48.5 -176t146.5 -69q80 0 130 18t119 60l72 -181q-56 -54 -149.5 -89t-205.5 -35q-204 0 -324 134t-120 338zM136 1270v25l250 237h159l256 -240v-22h-222l-114 134l-112 -134h-217z M353 645h255v40q0 90 -28 139.5t-91 49.5q-68 0 -100 -62t-36 -167z" />
+<glyph unicode="ë" horiz-adv-x="938" d="M69 451v152q0 219 111.5 359t308.5 140q190 0 294 -127t104 -346v-174h-535v-4q0 -107 48.5 -176t146.5 -69q80 0 130 18t119 60l72 -181q-56 -54 -149.5 -89t-205.5 -35q-204 0 -324 134t-120 338zM118 1285v198h251v-198h-251zM353 645h255v40q0 90 -28 139.5t-91 49.5 q-68 0 -100 -62t-36 -167zM567 1285v198h250v-198h-250z" />
+<glyph unicode="ì" horiz-adv-x="513" d="M-73 1476l3 6h298l175 -266h-230zM113 0v1082h285v-1082h-285z" />
+<glyph unicode="í" horiz-adv-x="513" d="M111 1217l176 266h299l3 -6l-249 -260h-229zM113 0v1082h285v-1082h-285z" />
+<glyph unicode="î" horiz-adv-x="513" d="M-72 1250v25l250 237h159l256 -240v-22h-222l-114 134l-112 -134h-217zM113 0v1082h285v-1082h-285z" />
+<glyph unicode="ï" horiz-adv-x="513" d="M-92 1263v198h251v-198h-251zM113 0v1082h285v-1082h-285zM357 1263v198h250v-198h-250z" />
+<glyph unicode="ð" horiz-adv-x="1053" d="M38 466q0 227 113.5 362t313.5 135q59 0 112.5 -19t93.5 -52l4 4q-17 69 -47 130.5t-71 111.5l-181 -119l-72 111l155 101q-30 21 -62 38t-65 31l89 217q72 -21 138.5 -56t125.5 -83l173 112l70 -110l-147 -96q98 -117 152.5 -274.5t54.5 -345.5v-71q0 -270 -137.5 -442 t-342.5 -172q-208 0 -339 139.5t-131 347.5zM322 466q0 -111 51.5 -185.5t137.5 -74.5q90 0 141 108t51 279v70q-28 38 -76.5 62.5t-111.5 24.5q-93 0 -143 -77.5t-50 -206.5z" />
+<glyph unicode="ñ" d="M87 0v1082h265l12 -149q45 80 110.5 124.5t144.5 44.5q135 0 212.5 -104t77.5 -327v-671h-285v671q0 112 -31.5 157.5t-97.5 45.5q-38 0 -70 -20t-54 -55v-799h-284zM170 1323q0 93 51.5 161t129.5 68q46 0 129.5 -44t128.5 -44q27 0 46.5 32.5t19.5 80.5l148 -45 q0 -95 -51 -161.5t-130 -66.5q-59 0 -136.5 44t-121.5 44q-27 0 -46 -33t-19 -79z" />
+<glyph unicode="ò" d="M58 473v134q0 218 115.5 356.5t323.5 138.5t324 -138.5t116 -356.5v-134q0 -220 -116 -357t-323 -137q-209 0 -324.5 137t-115.5 357zM165 1497l3 6h298l175 -266h-230zM341 473q0 -125 35 -196t122 -71q84 0 119.5 71.5t35.5 195.5v134q0 122 -35.5 194.5t-120.5 72.5 t-120.5 -73t-35.5 -194v-134z" />
+<glyph unicode="ó" d="M58 473v134q0 218 115.5 356.5t323.5 138.5t324 -138.5t116 -356.5v-134q0 -220 -116 -357t-323 -137q-209 0 -324.5 137t-115.5 357zM341 473q0 -125 35 -196t122 -71q84 0 119.5 71.5t35.5 195.5v134q0 122 -35.5 194.5t-120.5 72.5t-120.5 -73t-35.5 -194v-134z M351 1238l176 266h299l3 -6l-249 -260h-229z" />
+<glyph unicode="ô" d="M58 473v134q0 218 115.5 356.5t323.5 138.5t324 -138.5t116 -356.5v-134q0 -220 -116 -357t-323 -137q-209 0 -324.5 137t-115.5 357zM166 1269v25l250 237h159l256 -240v-22h-222l-114 134l-112 -134h-217zM341 473q0 -125 35 -196t122 -71q84 0 119.5 71.5t35.5 195.5 v134q0 122 -35.5 194.5t-120.5 72.5t-120.5 -73t-35.5 -194v-134z" />
+<glyph unicode="õ" d="M58 473v134q0 218 115.5 356.5t323.5 138.5t324 -138.5t116 -356.5v-134q0 -220 -116 -357t-323 -137q-209 0 -324.5 137t-115.5 357zM170 1323q0 93 51.5 161t129.5 68q46 0 129.5 -44t128.5 -44q27 0 46.5 32.5t19.5 80.5l148 -45q0 -95 -51 -161.5t-130 -66.5 q-59 0 -136.5 44t-121.5 44q-27 0 -46 -33t-19 -79zM341 473q0 -125 35 -196t122 -71q84 0 119.5 71.5t35.5 195.5v134q0 122 -35.5 194.5t-120.5 72.5t-120.5 -73t-35.5 -194v-134z" />
+<glyph unicode="ö" d="M58 473v134q0 218 115.5 356.5t323.5 138.5t324 -138.5t116 -356.5v-134q0 -220 -116 -357t-323 -137q-209 0 -324.5 137t-115.5 357zM146 1284v198h251v-198h-251zM341 473q0 -125 35 -196t122 -71q84 0 119.5 71.5t35.5 195.5v134q0 122 -35.5 194.5t-120.5 72.5 t-120.5 -73t-35.5 -194v-134zM595 1284v198h250v-198h-250z" />
+<glyph unicode="÷" horiz-adv-x="1005" d="M59 577v221h878v-221h-878zM357 167v230h281v-230h-281zM357 971v230h281v-230h-281z" />
+<glyph unicode="ø" horiz-adv-x="997" d="M58 473v134q0 218 115.5 356.5t323.5 138.5q40 0 78 -6t73 -17l57 138h152l-85 -206q80 -66 122.5 -170.5t42.5 -233.5v-134q0 -220 -116 -357t-323 -137q-40 0 -75.5 5.5t-68.5 14.5l-58 -142h-151l85 206q-85 64 -128.5 170t-43.5 240zM341 473q0 -35 1.5 -62t6.5 -43 l6 -1l203 494q-14 6 -29.5 9.5t-31.5 3.5q-85 0 -120.5 -73t-35.5 -194v-134zM444 215q12 -5 25 -7t29 -2q84 0 119.5 71.5t35.5 195.5v134q0 27 -2 49.5t-4 36.5l-6 1z" />
+<glyph unicode="ù" d="M85 428v654h284v-656q0 -115 28 -167t83 -52q47 0 82.5 17.5t60.5 51.5v806h285v-1082h-250l-17 125q-44 -71 -106.5 -108.5t-141.5 -37.5q-145 0 -226.5 109.5t-81.5 339.5zM163 1476l3 6h298l175 -266h-230z" />
+<glyph unicode="ú" d="M85 428v654h284v-656q0 -115 28 -167t83 -52q47 0 82.5 17.5t60.5 51.5v806h285v-1082h-250l-17 125q-44 -71 -106.5 -108.5t-141.5 -37.5q-145 0 -226.5 109.5t-81.5 339.5zM349 1217l176 266h299l3 -6l-249 -260h-229z" />
+<glyph unicode="û" d="M85 428v654h284v-656q0 -115 28 -167t83 -52q47 0 82.5 17.5t60.5 51.5v806h285v-1082h-250l-17 125q-44 -71 -106.5 -108.5t-141.5 -37.5q-145 0 -226.5 109.5t-81.5 339.5zM164 1250v25l250 237h159l256 -240v-22h-222l-114 134l-112 -134h-217z" />
+<glyph unicode="ü" d="M85 428v654h284v-656q0 -115 28 -167t83 -52q47 0 82.5 17.5t60.5 51.5v806h285v-1082h-250l-17 125q-44 -71 -106.5 -108.5t-141.5 -37.5q-145 0 -226.5 109.5t-81.5 339.5zM144 1263v198h251v-198h-251zM593 1263v198h250v-198h-250z" />
+<glyph unicode="ý" horiz-adv-x="909" d="M5 1082h305l130 -638l2 -11l158 649h304l-365 -1245q-41 -117 -109.5 -195.5t-209.5 -78.5q-35 0 -62 6t-72 18l33 212q17 -3 24.5 -4.5t16.5 -1.5q62 0 93.5 32.5t45.5 85.5l25 80zM307 1217l176 266h299l3 -6l-249 -260h-229z" />
+<glyph unicode="þ" horiz-adv-x="999" d="M93 -416v1976h284v-570q38 54 89 83t116 29q171 0 265 -140.5t94 -370.5v-133q0 -216 -94 -347.5t-263 -131.5q-66 0 -117.5 26.5t-89.5 78.5v-500h-284zM377 277q20 -35 52.5 -53t79.5 -18q79 0 113.5 67t34.5 185v133q0 128 -36 205.5t-113 77.5q-45 0 -78 -22.5 t-53 -63.5v-511z" />
+<glyph unicode="ÿ" horiz-adv-x="909" d="M5 1082h305l130 -638l2 -11l158 649h304l-365 -1245q-41 -117 -109.5 -195.5t-209.5 -78.5q-35 0 -62 6t-72 18l33 212q17 -3 24.5 -4.5t16.5 -1.5q62 0 93.5 32.5t45.5 85.5l25 80zM104 1263v198h251v-198h-251zM553 1263v198h250v-198h-250z" />
+<glyph unicode="Œ" horiz-adv-x="1709" d="M87 576v304q0 265 146 431t382 166q57 0 116.5 -6t124.5 -15h781v-228h-573v-362h489v-227h-489v-412h575v-227h-783q-76 -10 -130.5 -15.5t-109.5 -5.5q-235 0 -382 166t-147 431zM371 576q0 -179 65 -274.5t180 -95.5q41 0 82 3t81 10v1018q-41 6 -82 9.5t-82 3.5 q-116 0 -180 -95t-64 -273v-306z" />
+<glyph unicode="œ" horiz-adv-x="1571" d="M75 529v22q0 244 115 397.5t323 153.5q92 0 168 -32.5t132 -92.5q53 60 125 92.5t160 32.5q190 0 294 -130.5t104 -355.5v-161h-532q8 -111 56.5 -180t135.5 -69q80 0 131 18t119 60l73 -181q-57 -54 -151 -89t-206 -35q-94 0 -171.5 33t-134.5 95q-56 -63 -132 -95.5 t-170 -32.5q-209 0 -324 152.5t-115 397.5zM359 529q0 -150 34.5 -236t117.5 -81h6q82 -5 117 81.5t35 235.5v22q0 146 -35.5 233.5t-117.5 83.5h-6q-82 4 -116.5 -83.5t-34.5 -233.5v-22zM965 645h253v27q0 95 -28.5 148.5t-91.5 53.5q-61 0 -93 -61.5t-40 -167.5z" />
+<glyph unicode="Ÿ" horiz-adv-x="1124" d="M7 1456h311l240 -624h6l243 624h309l-416 -938v-518h-284v536zM220 1605v198h251v-198h-251zM669 1605v198h250v-198h-250z" />
+<glyph unicode="ˆ" horiz-adv-x="902" d="M118 1252v25l250 237h159l256 -240v-22h-222l-114 134l-112 -134h-217z" />
+<glyph unicode="˜" horiz-adv-x="867" d="M110 1272q0 93 51.5 161t129.5 68q46 0 129.5 -44t128.5 -44q27 0 46.5 32.5t19.5 80.5l148 -45q0 -95 -51 -161.5t-130 -66.5q-59 0 -136.5 44t-121.5 44q-27 0 -46 -33t-19 -79z" />
+<glyph unicode=" " horiz-adv-x="964" />
+<glyph unicode=" " horiz-adv-x="1929" />
+<glyph unicode=" " horiz-adv-x="964" />
+<glyph unicode=" " horiz-adv-x="1929" />
+<glyph unicode=" " horiz-adv-x="643" />
+<glyph unicode=" " horiz-adv-x="482" />
+<glyph unicode=" " horiz-adv-x="321" />
+<glyph unicode=" " horiz-adv-x="321" />
+<glyph unicode=" " horiz-adv-x="241" />
+<glyph unicode=" " horiz-adv-x="385" />
+<glyph unicode=" " horiz-adv-x="107" />
+<glyph unicode="‐" horiz-adv-x="748" d="M120 511v228h497v-228h-497z" />
+<glyph unicode="‑" horiz-adv-x="748" d="M120 511v228h497v-228h-497z" />
+<glyph unicode="‒" horiz-adv-x="748" d="M120 511v228h497v-228h-497z" />
+<glyph unicode="–" horiz-adv-x="1212" d="M147 600v230h917v-230h-917z" />
+<glyph unicode="—" horiz-adv-x="1430" d="M123 600v230h1128v-230h-1128z" />
+<glyph unicode="‘" horiz-adv-x="366" d="M41 1023v191l146 346h137l-50 -342v-195h-233z" />
+<glyph unicode="’" horiz-adv-x="366" d="M49 1024l49 344v192h234v-192l-146 -344h-137z" />
+<glyph unicode="‚" horiz-adv-x="367" d="M41 -257l52 267v285h234v-271l-132 -281h-154z" />
+<glyph unicode="“" horiz-adv-x="667" d="M41 1023v191l146 346h137l-50 -342v-195h-233zM342 1023v191l146 346h137l-50 -342v-195h-233z" />
+<glyph unicode="”" horiz-adv-x="675" d="M43 1024l49 344v192h234v-192l-146 -344h-137zM350 1024l49 344v192h234v-192l-146 -344h-137z" />
+<glyph unicode="„" horiz-adv-x="661" d="M41 -217l52 254v263h217v-251l-148 -266h-121zM350 -217l52 261v256h217v-251l-148 -266h-121z" />
+<glyph unicode="•" horiz-adv-x="680" d="M126 710v96q0 100 59.5 164t158.5 64q101 0 161 -63.5t60 -164.5v-96q0 -101 -59.5 -163t-159.5 -62t-160 62.5t-60 162.5z" />
+<glyph unicode="…" horiz-adv-x="1347" d="M129 0v258h281v-258h-281zM543 0v258h281v-258h-281zM952 0v258h281v-258h-281z" />
+<glyph unicode=" " horiz-adv-x="385" />
+<glyph unicode="‹" horiz-adv-x="563" d="M99 541v19l233 389h176l-195 -399l195 -397h-176z" />
+<glyph unicode="›" horiz-adv-x="541" d="M72 152l195 398l-195 399h176l233 -389v-19l-233 -389h-176z" />
+<glyph unicode=" " horiz-adv-x="482" />
+<glyph unicode="€" horiz-adv-x="970" d="M65 453v200h121v117h-121v200h121v16q0 204 130 347.5t342 143.5q51 0 100.5 -8t107.5 -23l-20 -232q-45 16 -92.5 25.5t-95.5 9.5q-89 0 -138.5 -78.5t-49.5 -182.5v-18h336v-200h-336v-117h336v-200h-336v-12q0 -97 49.5 -166t139.5 -69q48 0 95.5 8t91.5 25l20 -229 q-50 -15 -102 -23t-105 -8q-212 0 -342.5 134.5t-130.5 327.5v12h-121z" />
+<glyph unicode="™" horiz-adv-x="1116" d="M89 1350v106h340v-106h-104v-433h-137v433h-99zM487 914v542h149l89 -360h6l89 360h143v-542h-120v251h-6l-76 -251h-67l-80 266l-6 -1v-265h-121z" />
+<glyph unicode="" horiz-adv-x="1080" d="M0 0v1080h1080v-1080h-1080z" />
+<glyph unicode="fl" horiz-adv-x="1142" d="M23 876v206h133v120q0 182 94 280.5t264 98.5q33 0 66 -5.5t76 -15.5l-24 -218q-20 4 -38.5 7t-43.5 3q-54 0 -82 -39t-28 -111v-120h174v-206h-174v-876h-284v876h-133zM752 0v1560h284v-1560h-284z" />
+<glyph unicode="ffi" horiz-adv-x="1754" d="M23 876v206h133v120q0 182 94 280.5t264 98.5q33 0 66 -5.5t76 -15.5l-24 -218q-20 4 -38.5 7t-43.5 3q-54 0 -82 -39t-28 -111v-120h174v-206h-174v-876h-284v876h-133zM665 876v206h133v70q0 207 112 318t316 111q70 0 138.5 -15t153.5 -44l-40 -232q-65 22 -115.5 34 t-118.5 12q-80 0 -121 -47t-41 -137v-70h173v-206h-173v-876h-284v876h-133zM1363 0v1082h285v-1082h-285z" />
+<glyph unicode="ffl" horiz-adv-x="1789" d="M23 876v206h133v120q0 182 94 280.5t264 98.5q33 0 66 -5.5t76 -15.5l-24 -218q-20 4 -38.5 7t-43.5 3q-54 0 -82 -39t-28 -111v-120h174v-206h-174v-876h-284v876h-133zM670 876v206h133v120q0 182 94 280.5t264 98.5q33 0 66 -5.5t76 -15.5l-24 -218q-20 4 -38.5 7 t-43.5 3q-54 0 -82 -39t-28 -111v-120h174v-206h-174v-876h-284v876h-133zM1399 0v1560h284v-1560h-284z" />
+<hkern u1=""" u2="w" k="-11" />
+<hkern u1="'" u2="w" k="-11" />
+<hkern u1="(" u2="Ÿ" k="-22" />
+<hkern u1="(" u2="Ý" k="-22" />
+<hkern u1="(" u2="Y" k="-22" />
+<hkern u1="(" u2="W" k="-18" />
+<hkern u1="(" u2="V" k="-20" />
+<hkern u1="/" u2="/" k="224" />
+<hkern u1="A" u2="w" k="33" />
+<hkern u1="A" u2="t" k="17" />
+<hkern u1="A" u2="?" k="61" />
+<hkern u1="C" u2="}" k="17" />
+<hkern u1="C" u2="]" k="12" />
+<hkern u1="C" u2=")" k="26" />
+<hkern u1="D" u2="Æ" k="33" />
+<hkern u1="E" u2="w" k="22" />
+<hkern u1="E" u2="f" k="18" />
+<hkern u1="F" u2="…" k="234" />
+<hkern u1="F" u2="„" k="234" />
+<hkern u1="F" u2="‚" k="234" />
+<hkern u1="F" u2="œ" k="21" />
+<hkern u1="F" u2="ÿ" k="24" />
+<hkern u1="F" u2="ý" k="24" />
+<hkern u1="F" u2="ü" k="22" />
+<hkern u1="F" u2="û" k="22" />
+<hkern u1="F" u2="ú" k="22" />
+<hkern u1="F" u2="ù" k="22" />
+<hkern u1="F" u2="ö" k="21" />
+<hkern u1="F" u2="õ" k="21" />
+<hkern u1="F" u2="ô" k="21" />
+<hkern u1="F" u2="ó" k="21" />
+<hkern u1="F" u2="ò" k="21" />
+<hkern u1="F" u2="ë" k="21" />
+<hkern u1="F" u2="ê" k="21" />
+<hkern u1="F" u2="é" k="21" />
+<hkern u1="F" u2="è" k="21" />
+<hkern u1="F" u2="ç" k="21" />
+<hkern u1="F" u2="å" k="34" />
+<hkern u1="F" u2="ä" k="34" />
+<hkern u1="F" u2="ã" k="34" />
+<hkern u1="F" u2="â" k="34" />
+<hkern u1="F" u2="á" k="34" />
+<hkern u1="F" u2="à" k="34" />
+<hkern u1="F" u2="Å" k="59" />
+<hkern u1="F" u2="Ä" k="59" />
+<hkern u1="F" u2="Ã" k="59" />
+<hkern u1="F" u2="Â" k="59" />
+<hkern u1="F" u2="Á" k="59" />
+<hkern u1="F" u2="À" k="59" />
+<hkern u1="F" u2="y" k="24" />
+<hkern u1="F" u2="v" k="24" />
+<hkern u1="F" u2="u" k="22" />
+<hkern u1="F" u2="r" k="26" />
+<hkern u1="F" u2="q" k="21" />
+<hkern u1="F" u2="o" k="21" />
+<hkern u1="F" u2="g" k="21" />
+<hkern u1="F" u2="e" k="21" />
+<hkern u1="F" u2="d" k="21" />
+<hkern u1="F" u2="c" k="21" />
+<hkern u1="F" u2="a" k="34" />
+<hkern u1="F" u2="T" k="-20" />
+<hkern u1="F" u2="J" k="264" />
+<hkern u1="F" u2="A" k="59" />
+<hkern u1="F" u2="." k="234" />
+<hkern u1="F" u2="," k="234" />
+<hkern u1="K" u2="w" k="63" />
+<hkern u1="L" u2="w" k="92" />
+<hkern u1="O" u2="Æ" k="33" />
+<hkern u1="P" u2="Æ" k="97" />
+<hkern u1="P" u2="t" k="-14" />
+<hkern u1="Q" u2="Ÿ" k="35" />
+<hkern u1="Q" u2="Ý" k="35" />
+<hkern u1="Q" u2="Y" k="35" />
+<hkern u1="Q" u2="W" k="20" />
+<hkern u1="Q" u2="V" k="28" />
+<hkern u1="Q" u2="T" k="43" />
+<hkern u1="R" u2="Ÿ" k="48" />
+<hkern u1="R" u2="Ý" k="48" />
+<hkern u1="R" u2="Y" k="48" />
+<hkern u1="R" u2="V" k="19" />
+<hkern u1="R" u2="T" k="80" />
+<hkern u1="T" u2="ø" k="95" />
+<hkern u1="T" u2="æ" k="114" />
+<hkern u1="T" u2="Æ" k="179" />
+<hkern u1="T" u2="»" k="216" />
+<hkern u1="T" u2="«" k="328" />
+<hkern u1="T" u2="w" k="57" />
+<hkern u1="T" u2="r" k="75" />
+<hkern u1="V" u2="}" k="-19" />
+<hkern u1="V" u2="r" k="30" />
+<hkern u1="V" u2="]" k="-17" />
+<hkern u1="V" u2=")" k="-20" />
+<hkern u1="W" u2="}" k="-14" />
+<hkern u1="W" u2="r" k="21" />
+<hkern u1="W" u2="]" k="-12" />
+<hkern u1="W" u2=")" k="-15" />
+<hkern u1="Y" u2="•" k="45" />
+<hkern u1="Y" u2="ø" k="64" />
+<hkern u1="Y" u2="æ" k="63" />
+<hkern u1="Y" u2="Æ" k="96" />
+<hkern u1="Y" u2="»" k="51" />
+<hkern u1="Y" u2="«" k="82" />
+<hkern u1="Y" u2="}" k="-19" />
+<hkern u1="Y" u2="t" k="22" />
+<hkern u1="Y" u2="r" k="40" />
+<hkern u1="Y" u2="f" k="22" />
+<hkern u1="Y" u2="]" k="-18" />
+<hkern u1="Y" u2="*" k="49" />
+<hkern u1="Y" u2=")" k="-20" />
+<hkern u1="Y" u2="&" k="30" />
+<hkern u1="Z" u2="w" k="27" />
+<hkern u1="[" u2="Ü" k="18" />
+<hkern u1="[" u2="Û" k="18" />
+<hkern u1="[" u2="Ú" k="18" />
+<hkern u1="[" u2="Ù" k="18" />
+<hkern u1="[" u2="U" k="18" />
+<hkern u1="[" u2="J" k="18" />
+<hkern u1="f" u2="”" k="-16" />
+<hkern u1="f" u2="“" k="-16" />
+<hkern u1="f" u2="’" k="-16" />
+<hkern u1="f" u2="‘" k="-16" />
+<hkern u1="f" u2="œ" k="24" />
+<hkern u1="f" u2="ë" k="24" />
+<hkern u1="f" u2="ê" k="24" />
+<hkern u1="f" u2="é" k="24" />
+<hkern u1="f" u2="è" k="24" />
+<hkern u1="f" u2="ç" k="24" />
+<hkern u1="f" u2="}" k="-19" />
+<hkern u1="f" u2="q" k="24" />
+<hkern u1="f" u2="g" k="24" />
+<hkern u1="f" u2="e" k="24" />
+<hkern u1="f" u2="d" k="24" />
+<hkern u1="f" u2="c" k="24" />
+<hkern u1="f" u2="]" k="-18" />
+<hkern u1="f" u2=")" k="-20" />
+<hkern u1="f" u2="'" k="-16" />
+<hkern u1="f" u2=""" k="-16" />
+<hkern u1="k" u2="œ" k="20" />
+<hkern u1="k" u2="ë" k="20" />
+<hkern u1="k" u2="ê" k="20" />
+<hkern u1="k" u2="é" k="20" />
+<hkern u1="k" u2="è" k="20" />
+<hkern u1="k" u2="ç" k="20" />
+<hkern u1="k" u2="q" k="20" />
+<hkern u1="k" u2="g" k="20" />
+<hkern u1="k" u2="e" k="20" />
+<hkern u1="k" u2="d" k="20" />
+<hkern u1="k" u2="c" k="20" />
+<hkern u1="r" u2="w" k="-17" />
+<hkern u1="r" u2="t" k="-32" />
+<hkern u1="r" u2="f" k="-15" />
+<hkern u1="t" u2="ö" k="20" />
+<hkern u1="t" u2="õ" k="20" />
+<hkern u1="t" u2="ô" k="20" />
+<hkern u1="t" u2="ó" k="20" />
+<hkern u1="t" u2="ò" k="20" />
+<hkern u1="t" u2="o" k="20" />
+<hkern u1="v" u2="f" k="-13" />
+<hkern u1="w" u2="…" k="124" />
+<hkern u1="w" u2="„" k="124" />
+<hkern u1="w" u2="‚" k="124" />
+<hkern u1="w" u2="." k="124" />
+<hkern u1="w" u2="," k="124" />
+<hkern u1="y" u2="f" k="-13" />
+<hkern u1="{" u2="Ü" k="20" />
+<hkern u1="{" u2="Û" k="20" />
+<hkern u1="{" u2="Ú" k="20" />
+<hkern u1="{" u2="Ù" k="20" />
+<hkern u1="{" u2="U" k="20" />
+<hkern u1="{" u2="J" k="20" />
+<hkern u1="À" u2="w" k="33" />
+<hkern u1="À" u2="t" k="17" />
+<hkern u1="À" u2="?" k="61" />
+<hkern u1="Á" u2="w" k="33" />
+<hkern u1="Á" u2="t" k="17" />
+<hkern u1="Á" u2="?" k="61" />
+<hkern u1="Â" u2="w" k="33" />
+<hkern u1="Â" u2="t" k="17" />
+<hkern u1="Â" u2="?" k="61" />
+<hkern u1="Ã" u2="w" k="33" />
+<hkern u1="Ã" u2="t" k="17" />
+<hkern u1="Ã" u2="?" k="61" />
+<hkern u1="Ä" u2="w" k="33" />
+<hkern u1="Ä" u2="t" k="17" />
+<hkern u1="Ä" u2="?" k="61" />
+<hkern u1="Å" u2="w" k="33" />
+<hkern u1="Å" u2="t" k="17" />
+<hkern u1="Å" u2="?" k="61" />
+<hkern u1="Ç" u2="}" k="17" />
+<hkern u1="Ç" u2="]" k="12" />
+<hkern u1="Ç" u2=")" k="26" />
+<hkern u1="È" u2="w" k="22" />
+<hkern u1="È" u2="f" k="18" />
+<hkern u1="É" u2="w" k="22" />
+<hkern u1="É" u2="f" k="18" />
+<hkern u1="Ê" u2="w" k="22" />
+<hkern u1="Ê" u2="f" k="18" />
+<hkern u1="Ë" u2="w" k="22" />
+<hkern u1="Ë" u2="f" k="18" />
+<hkern u1="Ð" u2="Æ" k="33" />
+<hkern u1="Ò" u2="Æ" k="33" />
+<hkern u1="Ó" u2="Æ" k="33" />
+<hkern u1="Ô" u2="Æ" k="33" />
+<hkern u1="Õ" u2="Æ" k="33" />
+<hkern u1="Ö" u2="Æ" k="33" />
+<hkern u1="Ý" u2="•" k="45" />
+<hkern u1="Ý" u2="ø" k="64" />
+<hkern u1="Ý" u2="æ" k="63" />
+<hkern u1="Ý" u2="Æ" k="96" />
+<hkern u1="Ý" u2="»" k="51" />
+<hkern u1="Ý" u2="«" k="82" />
+<hkern u1="Ý" u2="}" k="-19" />
+<hkern u1="Ý" u2="t" k="22" />
+<hkern u1="Ý" u2="r" k="40" />
+<hkern u1="Ý" u2="f" k="22" />
+<hkern u1="Ý" u2="]" k="-18" />
+<hkern u1="Ý" u2="*" k="49" />
+<hkern u1="Ý" u2=")" k="-20" />
+<hkern u1="Ý" u2="&" k="30" />
+<hkern u1="ý" u2="f" k="-13" />
+<hkern u1="ÿ" u2="f" k="-13" />
+<hkern u1="Ÿ" u2="•" k="45" />
+<hkern u1="Ÿ" u2="ø" k="64" />
+<hkern u1="Ÿ" u2="æ" k="63" />
+<hkern u1="Ÿ" u2="Æ" k="96" />
+<hkern u1="Ÿ" u2="»" k="51" />
+<hkern u1="Ÿ" u2="«" k="82" />
+<hkern u1="Ÿ" u2="}" k="-19" />
+<hkern u1="Ÿ" u2="t" k="22" />
+<hkern u1="Ÿ" u2="r" k="40" />
+<hkern u1="Ÿ" u2="f" k="22" />
+<hkern u1="Ÿ" u2="]" k="-18" />
+<hkern u1="Ÿ" u2="*" k="49" />
+<hkern u1="Ÿ" u2=")" k="-20" />
+<hkern u1="Ÿ" u2="&" k="30" />
+<hkern u1="‘" u2="w" k="-11" />
+<hkern u1="’" u2="w" k="-11" />
+<hkern u1="“" u2="w" k="-11" />
+<hkern u1="”" u2="w" k="-11" />
+<hkern g1="comma,period,quotesinglbase,quotedblbase,ellipsis" g2="quotedbl,quotesingle,quoteleft,quoteright,quotedblleft,quotedblright" k="170" />
+<hkern g1="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring" g2="quotedbl,quotesingle,quoteleft,quoteright,quotedblleft,quotedblright" k="120" />
+<hkern g1="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring" g2="o,ograve,oacute,ocircumflex,otilde,odieresis" k="12" />
+<hkern g1="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring" g2="C,G,O,Q,Ccedilla,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,OE" k="11" />
+<hkern g1="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring" g2="T" k="129" />
+<hkern g1="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring" g2="U,Ugrave,Uacute,Ucircumflex,Udieresis" k="17" />
+<hkern g1="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring" g2="V" k="87" />
+<hkern g1="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring" g2="W" k="69" />
+<hkern g1="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring" g2="Y,Yacute,Ydieresis" k="94" />
+<hkern g1="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring" g2="u,ugrave,uacute,ucircumflex,udieresis" k="11" />
+<hkern g1="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring" g2="v,y,yacute,ydieresis" k="50" />
+<hkern g1="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring" g2="z" k="-12" />
+<hkern g1="B" g2="T" k="27" />
+<hkern g1="B" g2="V" k="24" />
+<hkern g1="B" g2="Y,Yacute,Ydieresis" k="55" />
+<hkern g1="C,Ccedilla" g2="T" k="29" />
+<hkern g1="D,O,Eth,Ograve,Oacute,Ocircumflex,Otilde,Odieresis" g2="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring" k="21" />
+<hkern g1="D,O,Eth,Ograve,Oacute,Ocircumflex,Otilde,Odieresis" g2="T" k="27" />
+<hkern g1="D,O,Eth,Ograve,Oacute,Ocircumflex,Otilde,Odieresis" g2="V" k="22" />
+<hkern g1="D,O,Eth,Ograve,Oacute,Ocircumflex,Otilde,Odieresis" g2="Y,Yacute,Ydieresis" k="43" />
+<hkern g1="D,O,Eth,Ograve,Oacute,Ocircumflex,Otilde,Odieresis" g2="comma,period,quotesinglbase,quotedblbase,ellipsis" k="102" />
+<hkern g1="D,O,Eth,Ograve,Oacute,Ocircumflex,Otilde,Odieresis" g2="X" k="22" />
+<hkern g1="D,O,Eth,Ograve,Oacute,Ocircumflex,Otilde,Odieresis" g2="Z" k="23" />
+<hkern g1="E,Egrave,Eacute,Ecircumflex,Edieresis" g2="c,d,e,g,q,ccedilla,egrave,eacute,ecircumflex,edieresis,oe" k="19" />
+<hkern g1="E,Egrave,Eacute,Ecircumflex,Edieresis" g2="o,ograve,oacute,ocircumflex,otilde,odieresis" k="19" />
+<hkern g1="E,Egrave,Eacute,Ecircumflex,Edieresis" g2="T" k="-20" />
+<hkern g1="E,Egrave,Eacute,Ecircumflex,Edieresis" g2="u,ugrave,uacute,ucircumflex,udieresis" k="17" />
+<hkern g1="E,Egrave,Eacute,Ecircumflex,Edieresis" g2="v,y,yacute,ydieresis" k="26" />
+<hkern g1="H,I,M,N,Igrave,Iacute,Icircumflex,Idieresis,Ntilde" g2="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring" k="-18" />
+<hkern g1="H,I,M,N,Igrave,Iacute,Icircumflex,Idieresis,Ntilde" g2="T" k="29" />
+<hkern g1="H,I,M,N,Igrave,Iacute,Icircumflex,Idieresis,Ntilde" g2="Y,Yacute,Ydieresis" k="28" />
+<hkern g1="H,I,M,N,Igrave,Iacute,Icircumflex,Idieresis,Ntilde" g2="X" k="-17" />
+<hkern g1="J,U,Ugrave,Uacute,Ucircumflex,Udieresis" g2="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring" k="22" />
+<hkern g1="K" g2="c,d,e,g,q,ccedilla,egrave,eacute,ecircumflex,edieresis,oe" k="26" />
+<hkern g1="K" g2="m,n,p,ntilde" k="23" />
+<hkern g1="K" g2="o,ograve,oacute,ocircumflex,otilde,odieresis" k="27" />
+<hkern g1="K" g2="C,G,O,Q,Ccedilla,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,OE" k="31" />
+<hkern g1="K" g2="u,ugrave,uacute,ucircumflex,udieresis" k="23" />
+<hkern g1="K" g2="v,y,yacute,ydieresis" k="40" />
+<hkern g1="K" g2="hyphen,uni00AD,endash,emdash" k="64" />
+<hkern g1="L" g2="quotedbl,quotesingle,quoteleft,quoteright,quotedblleft,quotedblright" k="138" />
+<hkern g1="L" g2="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring" k="-19" />
+<hkern g1="L" g2="C,G,O,Q,Ccedilla,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,OE" k="65" />
+<hkern g1="L" g2="T" k="275" />
+<hkern g1="L" g2="U,Ugrave,Uacute,Ucircumflex,Udieresis" k="54" />
+<hkern g1="L" g2="V" k="175" />
+<hkern g1="L" g2="W" k="143" />
+<hkern g1="L" g2="Y,Yacute,Ydieresis" k="239" />
+<hkern g1="L" g2="u,ugrave,uacute,ucircumflex,udieresis" k="44" />
+<hkern g1="L" g2="v,y,yacute,ydieresis" k="133" />
+<hkern g1="P" g2="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring" k="138" />
+<hkern g1="P" g2="a,agrave,aacute,acircumflex,atilde,adieresis,aring" k="11" />
+<hkern g1="P" g2="c,d,e,g,q,ccedilla,egrave,eacute,ecircumflex,edieresis,oe" k="13" />
+<hkern g1="P" g2="o,ograve,oacute,ocircumflex,otilde,odieresis" k="13" />
+<hkern g1="P" g2="v,y,yacute,ydieresis" k="-15" />
+<hkern g1="P" g2="comma,period,quotesinglbase,quotedblbase,ellipsis" k="324" />
+<hkern g1="P" g2="X" k="31" />
+<hkern g1="P" g2="Z" k="26" />
+<hkern g1="P" g2="J" k="200" />
+<hkern g1="T" g2="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring" k="79" />
+<hkern g1="T" g2="a,agrave,aacute,acircumflex,atilde,adieresis,aring" k="113" />
+<hkern g1="T" g2="c,d,e,g,q,ccedilla,egrave,eacute,ecircumflex,edieresis,oe" k="99" />
+<hkern g1="T" g2="m,n,p,ntilde" k="109" />
+<hkern g1="T" g2="o,ograve,oacute,ocircumflex,otilde,odieresis" k="99" />
+<hkern g1="T" g2="s" k="116" />
+<hkern g1="T" g2="C,G,O,Q,Ccedilla,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,OE" k="28" />
+<hkern g1="T" g2="T" k="-16" />
+<hkern g1="T" g2="V" k="-16" />
+<hkern g1="T" g2="W" k="-15" />
+<hkern g1="T" g2="Y,Yacute,Ydieresis" k="-16" />
+<hkern g1="T" g2="u,ugrave,uacute,ucircumflex,udieresis" k="95" />
+<hkern g1="T" g2="v,y,yacute,ydieresis" k="72" />
+<hkern g1="T" g2="z" k="60" />
+<hkern g1="T" g2="comma,period,quotesinglbase,quotedblbase,ellipsis" k="218" />
+<hkern g1="T" g2="hyphen,uni00AD,endash,emdash" k="232" />
+<hkern g1="T" g2="J" k="240" />
+<hkern g1="T" g2="S" k="16" />
+<hkern g1="T" g2="x" k="77" />
+<hkern g1="V" g2="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring" k="75" />
+<hkern g1="V" g2="a,agrave,aacute,acircumflex,atilde,adieresis,aring" k="46" />
+<hkern g1="V" g2="c,d,e,g,q,ccedilla,egrave,eacute,ecircumflex,edieresis,oe" k="44" />
+<hkern g1="V" g2="o,ograve,oacute,ocircumflex,otilde,odieresis" k="46" />
+<hkern g1="V" g2="C,G,O,Q,Ccedilla,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,OE" k="13" />
+<hkern g1="V" g2="u,ugrave,uacute,ucircumflex,udieresis" k="28" />
+<hkern g1="V" g2="v,y,yacute,ydieresis" k="11" />
+<hkern g1="V" g2="comma,period,quotesinglbase,quotedblbase,ellipsis" k="225" />
+<hkern g1="V" g2="hyphen,uni00AD,endash,emdash" k="37" />
+<hkern g1="W" g2="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring" k="43" />
+<hkern g1="W" g2="a,agrave,aacute,acircumflex,atilde,adieresis,aring" k="33" />
+<hkern g1="W" g2="c,d,e,g,q,ccedilla,egrave,eacute,ecircumflex,edieresis,oe" k="31" />
+<hkern g1="W" g2="o,ograve,oacute,ocircumflex,otilde,odieresis" k="31" />
+<hkern g1="W" g2="T" k="-14" />
+<hkern g1="W" g2="u,ugrave,uacute,ucircumflex,udieresis" k="19" />
+<hkern g1="W" g2="comma,period,quotesinglbase,quotedblbase,ellipsis" k="123" />
+<hkern g1="W" g2="hyphen,uni00AD,endash,emdash" k="60" />
+<hkern g1="X" g2="c,d,e,g,q,ccedilla,egrave,eacute,ecircumflex,edieresis,oe" k="26" />
+<hkern g1="X" g2="o,ograve,oacute,ocircumflex,otilde,odieresis" k="21" />
+<hkern g1="X" g2="C,G,O,Q,Ccedilla,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,OE" k="25" />
+<hkern g1="X" g2="V" k="-14" />
+<hkern g1="X" g2="u,ugrave,uacute,ucircumflex,udieresis" k="21" />
+<hkern g1="X" g2="v,y,yacute,ydieresis" k="31" />
+<hkern g1="X" g2="hyphen,uni00AD,endash,emdash" k="46" />
+<hkern g1="Y,Yacute,Ydieresis" g2="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring" k="94" />
+<hkern g1="Y,Yacute,Ydieresis" g2="a,agrave,aacute,acircumflex,atilde,adieresis,aring" k="73" />
+<hkern g1="Y,Yacute,Ydieresis" g2="c,d,e,g,q,ccedilla,egrave,eacute,ecircumflex,edieresis,oe" k="65" />
+<hkern g1="Y,Yacute,Ydieresis" g2="m,n,p,ntilde" k="40" />
+<hkern g1="Y,Yacute,Ydieresis" g2="o,ograve,oacute,ocircumflex,otilde,odieresis" k="65" />
+<hkern g1="Y,Yacute,Ydieresis" g2="s" k="58" />
+<hkern g1="Y,Yacute,Ydieresis" g2="C,G,O,Q,Ccedilla,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,OE" k="29" />
+<hkern g1="Y,Yacute,Ydieresis" g2="T" k="-17" />
+<hkern g1="Y,Yacute,Ydieresis" g2="U,Ugrave,Uacute,Ucircumflex,Udieresis" k="96" />
+<hkern g1="Y,Yacute,Ydieresis" g2="V" k="-18" />
+<hkern g1="Y,Yacute,Ydieresis" g2="W" k="-17" />
+<hkern g1="Y,Yacute,Ydieresis" g2="Y,Yacute,Ydieresis" k="-18" />
+<hkern g1="Y,Yacute,Ydieresis" g2="u,ugrave,uacute,ucircumflex,udieresis" k="39" />
+<hkern g1="Y,Yacute,Ydieresis" g2="v,y,yacute,ydieresis" k="20" />
+<hkern g1="Y,Yacute,Ydieresis" g2="z" k="30" />
+<hkern g1="Y,Yacute,Ydieresis" g2="comma,period,quotesinglbase,quotedblbase,ellipsis" k="211" />
+<hkern g1="Y,Yacute,Ydieresis" g2="X" k="-13" />
+<hkern g1="Y,Yacute,Ydieresis" g2="hyphen,uni00AD,endash,emdash" k="52" />
+<hkern g1="Y,Yacute,Ydieresis" g2="J" k="96" />
+<hkern g1="Y,Yacute,Ydieresis" g2="S" k="16" />
+<hkern g1="Y,Yacute,Ydieresis" g2="x" k="23" />
+<hkern g1="Z" g2="A,Agrave,Aacute,Acircumflex,Atilde,Adieresis,Aring" k="-13" />
+<hkern g1="Z" g2="c,d,e,g,q,ccedilla,egrave,eacute,ecircumflex,edieresis,oe" k="21" />
+<hkern g1="Z" g2="o,ograve,oacute,ocircumflex,otilde,odieresis" k="21" />
+<hkern g1="Z" g2="C,G,O,Q,Ccedilla,Ograve,Oacute,Ocircumflex,Otilde,Odieresis,Oslash,OE" k="26" />
+<hkern g1="Z" g2="u,ugrave,uacute,ucircumflex,udieresis" k="19" />
+<hkern g1="Z" g2="v,y,yacute,ydieresis" k="27" />
+<hkern g1="a,agrave,aacute,acircumflex,atilde,adieresis,aring" g2="quotedbl,quotesingle,quoteleft,quoteright,quotedblleft,quotedblright" k="67" />
+<hkern g1="a,agrave,aacute,acircumflex,atilde,adieresis,aring" g2="v,y,yacute,ydieresis" k="15" />
+<hkern g1="b,p,thorn" g2="quotedbl,quotesingle,quoteleft,quoteright,quotedblleft,quotedblright" k="29" />
+<hkern g1="b,p,thorn" g2="v,y,yacute,ydieresis" k="11" />
+<hkern g1="b,p,thorn" g2="z" k="15" />
+<hkern g1="b,p,thorn" g2="x" k="15" />
+<hkern g1="c,ccedilla" g2="quotedbl,quotesingle,quoteleft,quoteright,quotedblleft,quotedblright" k="11" />
+<hkern g1="e,egrave,eacute,ecircumflex,edieresis" g2="quotedbl,quotesingle,quoteleft,quoteright,quotedblleft,quotedblright" k="14" />
+<hkern g1="e,egrave,eacute,ecircumflex,edieresis" g2="v,y,yacute,ydieresis" k="13" />
+<hkern g1="h,m,n,ntilde" g2="quotedbl,quotesingle,quoteleft,quoteright,quotedblleft,quotedblright" k="16" />
+<hkern g1="o,ograve,oacute,ocircumflex,otilde,odieresis" g2="quotedbl,quotesingle,quoteleft,quoteright,quotedblleft,quotedblright" k="20" />
+<hkern g1="o,ograve,oacute,ocircumflex,otilde,odieresis" g2="v,y,yacute,ydieresis" k="15" />
+<hkern g1="o,ograve,oacute,ocircumflex,otilde,odieresis" g2="z" k="16" />
+<hkern g1="o,ograve,oacute,ocircumflex,otilde,odieresis" g2="x" k="21" />
+<hkern g1="r" g2="quotedbl,quotesingle,quoteleft,quoteright,quotedblleft,quotedblright" k="-16" />
+<hkern g1="r" g2="c,d,e,g,q,ccedilla,egrave,eacute,ecircumflex,edieresis,oe" k="19" />
+<hkern g1="r" g2="o,ograve,oacute,ocircumflex,otilde,odieresis" k="20" />
+<hkern g1="r" g2="v,y,yacute,ydieresis" k="-18" />
+<hkern g1="r" g2="comma,period,quotesinglbase,quotedblbase,ellipsis" k="123" />
+<hkern g1="v,y,yacute,ydieresis" g2="quotedbl,quotesingle,quoteleft,quoteright,quotedblleft,quotedblright" k="-15" />
+<hkern g1="v,y,yacute,ydieresis" g2="a,agrave,aacute,acircumflex,atilde,adieresis,aring" k="15" />
+<hkern g1="v,y,yacute,ydieresis" g2="c,d,e,g,q,ccedilla,egrave,eacute,ecircumflex,edieresis,oe" k="13" />
+<hkern g1="v,y,yacute,ydieresis" g2="o,ograve,oacute,ocircumflex,otilde,odieresis" k="15" />
+<hkern g1="v,y,yacute,ydieresis" g2="comma,period,quotesinglbase,quotedblbase,ellipsis" k="107" />
+<hkern g1="x" g2="c,d,e,g,q,ccedilla,egrave,eacute,ecircumflex,edieresis,oe" k="20" />
+<hkern g1="x" g2="o,ograve,oacute,ocircumflex,otilde,odieresis" k="20" />
+<hkern g1="z" g2="c,d,e,g,q,ccedilla,egrave,eacute,ecircumflex,edieresis,oe" k="16" />
+<hkern g1="z" g2="o,ograve,oacute,ocircumflex,otilde,odieresis" k="16" />
+</font>
+</defs></svg>
\ 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 0000000..87256d3
--- /dev/null
+++ b/skyquake/framework/style/fonts/RobotoCondensed-Bold-webfont.ttf
Binary files 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 0000000..235c963
--- /dev/null
+++ b/skyquake/framework/style/fonts/RobotoCondensed-Bold-webfont.woff
Binary files 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 0000000..d08a984
--- /dev/null
+++ b/skyquake/framework/style/fonts/SourceCodePro-Black.otf.woff
Binary files 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 0000000..1061185
--- /dev/null
+++ b/skyquake/framework/style/fonts/SourceCodePro-BlackIt.otf.woff
Binary files 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 0000000..cf96099
--- /dev/null
+++ b/skyquake/framework/style/fonts/SourceCodePro-Bold.otf.woff
Binary files 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 0000000..e8702f9
--- /dev/null
+++ b/skyquake/framework/style/fonts/SourceCodePro-BoldIt.otf.woff
Binary files 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 0000000..54e6b36
--- /dev/null
+++ b/skyquake/framework/style/fonts/SourceCodePro-ExtraLight.otf.woff
Binary files 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 0000000..c57443c
--- /dev/null
+++ b/skyquake/framework/style/fonts/SourceCodePro-ExtraLightIt.otf.woff
Binary files 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 0000000..cc67c99
--- /dev/null
+++ b/skyquake/framework/style/fonts/SourceCodePro-It.otf.woff
Binary files 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 0000000..c62373f
--- /dev/null
+++ b/skyquake/framework/style/fonts/SourceCodePro-Light.otf.woff
Binary files 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 0000000..c4cd646
--- /dev/null
+++ b/skyquake/framework/style/fonts/SourceCodePro-LightIt.otf.woff
Binary files 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 0000000..826a854
--- /dev/null
+++ b/skyquake/framework/style/fonts/SourceCodePro-Medium.otf.woff
Binary files 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 0000000..5c538de
--- /dev/null
+++ b/skyquake/framework/style/fonts/SourceCodePro-MediumIt.otf.woff
Binary files 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 0000000..395436e
--- /dev/null
+++ b/skyquake/framework/style/fonts/SourceCodePro-Regular.otf.woff
Binary files 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 0000000..83ec113
--- /dev/null
+++ b/skyquake/framework/style/fonts/SourceCodePro-Semibold.otf.woff
Binary files 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 0000000..be0bd90
--- /dev/null
+++ b/skyquake/framework/style/fonts/SourceCodePro-SemiboldIt.otf.woff
Binary files 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 0000000..f1a663a
--- /dev/null
+++ b/skyquake/framework/style/fonts/SourceSansPro-Black.otf.woff
Binary files 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 0000000..1d7dfbd
--- /dev/null
+++ b/skyquake/framework/style/fonts/SourceSansPro-BlackIt.otf.woff
Binary files 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 0000000..6700893
--- /dev/null
+++ b/skyquake/framework/style/fonts/SourceSansPro-Bold.otf.woff
Binary files 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 0000000..d5e4a0f
--- /dev/null
+++ b/skyquake/framework/style/fonts/SourceSansPro-BoldIt.otf.woff
Binary files 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 0000000..559b740
--- /dev/null
+++ b/skyquake/framework/style/fonts/SourceSansPro-ExtraLight.otf.woff
Binary files 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 0000000..e8fbeb8
--- /dev/null
+++ b/skyquake/framework/style/fonts/SourceSansPro-ExtraLightIt.otf.woff
Binary files 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 0000000..4b8af41
--- /dev/null
+++ b/skyquake/framework/style/fonts/SourceSansPro-It.otf.woff
Binary files 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 0000000..10490ec
--- /dev/null
+++ b/skyquake/framework/style/fonts/SourceSansPro-Light.otf.woff
Binary files 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 0000000..13532d7
--- /dev/null
+++ b/skyquake/framework/style/fonts/SourceSansPro-LightIt.otf.woff
Binary files 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 0000000..04739e7
--- /dev/null
+++ b/skyquake/framework/style/fonts/SourceSansPro-Regular.otf.woff
Binary files 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 0000000..17d744d
--- /dev/null
+++ b/skyquake/framework/style/fonts/SourceSansPro-Semibold.otf.woff
Binary files 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 0000000..a5b5e1e
--- /dev/null
+++ b/skyquake/framework/style/fonts/SourceSansPro-SemiboldIt.otf.woff
Binary files 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 0000000..e2497d9
--- /dev/null
+++ b/skyquake/framework/style/icons/svg-sprite-action-symbol.svg
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><symbol viewBox="0 0 24 24" id="ic_3d_rotation_24px"><path d="M7.52 21.48A10.487 10.487 0 0 1 1.55 13H.05C.56 19.16 5.71 24 12 24l.66-.03-3.81-3.81-1.33 1.32zm.89-6.52c-.19 0-.37-.03-.52-.08a1.07 1.07 0 0 1-.4-.24.99.99 0 0 1-.26-.37c-.06-.14-.09-.3-.09-.47h-1.3c0 .36.07.68.21.95.14.27.33.5.56.69.24.18.51.32.82.41.3.1.62.15.96.15.37 0 .72-.05 1.03-.15.32-.1.6-.25.83-.44s.42-.43.55-.72c.13-.29.2-.61.2-.97 0-.19-.02-.38-.07-.56a1.67 1.67 0 0 0-.23-.51c-.1-.16-.24-.3-.4-.43-.17-.13-.37-.23-.61-.31a2.098 2.098 0 0 0 .89-.75c.1-.15.17-.3.22-.46.05-.16.07-.32.07-.48 0-.36-.06-.68-.18-.96a1.78 1.78 0 0 0-.51-.69c-.2-.19-.47-.33-.77-.43C9.1 8.05 8.76 8 8.39 8c-.36 0-.69.05-1 .16-.3.11-.57.26-.79.45-.21.19-.38.41-.51.67-.12.26-.18.54-.18.85h1.3c0-.17.03-.32.09-.45s.14-.25.25-.34c.11-.09.23-.17.38-.22.15-.05.3-.08.48-.08.4 0 .7.1.89.31.19.2.29.49.29.86 0 .18-.03.34-.08.49a.87.87 0 0 1-.25.37c-.11.1-.25.18-.41.24-.16.06-.36.09-.58.09H7.5v1.03h.77c.22 0 .42.02.6.07s.33.13.45.23c.12.11.22.24.29.4.07.16.1.35.1.57 0 .41-.12.72-.35.93-.23.23-.55.33-.95.33zm8.55-5.92c-.32-.33-.7-.59-1.14-.77-.43-.18-.92-.27-1.46-.27H12v8h2.3c.55 0 1.06-.09 1.51-.27.45-.18.84-.43 1.16-.76.32-.33.57-.73.74-1.19.17-.47.26-.99.26-1.57v-.4c0-.58-.09-1.1-.26-1.57-.18-.47-.43-.87-.75-1.2zm-.39 3.16c0 .42-.05.79-.14 1.13-.1.33-.24.62-.43.85-.19.23-.43.41-.71.53-.29.12-.62.18-.99.18h-.91V9.12h.97c.72 0 1.27.23 1.64.69.38.46.57 1.12.57 1.99v.4zM12 0l-.66.03 3.81 3.81 1.33-1.33c3.27 1.55 5.61 4.72 5.96 8.48h1.5C23.44 4.84 18.29 0 12 0z"/></symbol><symbol viewBox="0 0 24 24" id="ic_accessibility_24px"><path d="M12 2c1.1 0 2 .9 2 2s-.9 2-2 2-2-.9-2-2 .9-2 2-2zm9 7h-6v13h-2v-6h-2v6H9V9H3V7h18v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_accessible_24px"><circle cx="12" cy="4" r="2"/><path d="M19 13v-2c-1.54.02-3.09-.75-4.07-1.83l-1.29-1.43c-.17-.19-.38-.34-.61-.45-.01 0-.01-.01-.02-.01H13c-.35-.2-.75-.3-1.19-.26C10.76 7.11 10 8.04 10 9.09V15c0 1.1.9 2 2 2h5v5h2v-5.5c0-1.1-.9-2-2-2h-3v-3.45c1.29 1.07 3.25 1.94 5 1.95zm-6.17 5c-.41 1.16-1.52 2-2.83 2-1.66 0-3-1.34-3-3 0-1.31.84-2.41 2-2.83V12.1a5 5 0 1 0 5.9 5.9h-2.07z"/></symbol><symbol viewBox="0 0 24 24" id="ic_account_balance_24px"><path d="M4 10v7h3v-7H4zm6 0v7h3v-7h-3zM2 22h19v-3H2v3zm14-12v7h3v-7h-3zm-4.5-9L2 6v2h19V6l-9.5-5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_account_balance_wallet_24px"><path d="M21 18v1c0 1.1-.9 2-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h14c1.1 0 2 .9 2 2v1h-9a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h9zm-9-2h10V8H12v8zm4-2.5c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_account_box_24px"><path d="M3 5v14a2 2 0 0 0 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2H5a2 2 0 0 0-2 2zm12 4c0 1.66-1.34 3-3 3s-3-1.34-3-3 1.34-3 3-3 3 1.34 3 3zm-9 8c0-2 4-3.1 6-3.1s6 1.1 6 3.1v1H6v-1z"/></symbol><symbol viewBox="0 0 24 24" id="ic_account_circle_24px"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 3c1.66 0 3 1.34 3 3s-1.34 3-3 3-3-1.34-3-3 1.34-3 3-3zm0 14.2a7.2 7.2 0 0 1-6-3.22c.03-1.99 4-3.08 6-3.08 1.99 0 5.97 1.09 6 3.08a7.2 7.2 0 0 1-6 3.22z"/></symbol><symbol viewBox="0 0 24 24" id="ic_add_shopping_cart_24px"><path d="M11 9h2V6h3V4h-3V1h-2v3H8v2h3v3zm-4 9c-1.1 0-1.99.9-1.99 2S5.9 22 7 22s2-.9 2-2-.9-2-2-2zm10 0c-1.1 0-1.99.9-1.99 2s.89 2 1.99 2 2-.9 2-2-.9-2-2-2zm-9.83-3.25l.03-.12.9-1.63h7.45c.75 0 1.41-.41 1.75-1.03l3.86-7.01L19.42 4h-.01l-1.1 2-2.76 5H8.53l-.13-.27L6.16 6l-.95-2-.94-2H1v2h2l3.6 7.59-1.35 2.45c-.16.28-.25.61-.25.96 0 1.1.9 2 2 2h12v-2H7.42c-.13 0-.25-.11-.25-.25z"/></symbol><symbol viewBox="0 0 24 24" id="ic_alarm_24px"><path d="M22 5.72l-4.6-3.86-1.29 1.53 4.6 3.86L22 5.72zM7.88 3.39L6.6 1.86 2 5.71l1.29 1.53 4.59-3.85zM12.5 8H11v6l4.75 2.85.75-1.23-4-2.37V8zM12 4c-4.97 0-9 4.03-9 9s4.02 9 9 9a9 9 0 0 0 0-18zm0 16c-3.87 0-7-3.13-7-7s3.13-7 7-7 7 3.13 7 7-3.13 7-7 7z"/></symbol><symbol viewBox="0 0 24 24" id="ic_alarm_add_24px"><path d="M7.88 3.39L6.6 1.86 2 5.71l1.29 1.53 4.59-3.85zM22 5.72l-4.6-3.86-1.29 1.53 4.6 3.86L22 5.72zM12 4c-4.97 0-9 4.03-9 9s4.02 9 9 9a9 9 0 0 0 0-18zm0 16c-3.87 0-7-3.13-7-7s3.13-7 7-7 7 3.13 7 7-3.13 7-7 7zm1-11h-2v3H8v2h3v3h2v-3h3v-2h-3V9z"/></symbol><symbol viewBox="0 0 24 24" id="ic_alarm_off_24px"><path d="M12 6c3.87 0 7 3.13 7 7 0 .84-.16 1.65-.43 2.4l1.52 1.52c.58-1.19.91-2.51.91-3.92a9 9 0 0 0-9-9c-1.41 0-2.73.33-3.92.91L9.6 6.43C10.35 6.16 11.16 6 12 6zm10-.28l-4.6-3.86-1.29 1.53 4.6 3.86L22 5.72zM2.92 2.29L1.65 3.57 2.98 4.9l-1.11.93 1.42 1.42 1.11-.94.8.8A8.964 8.964 0 0 0 3 13c0 4.97 4.02 9 9 9 2.25 0 4.31-.83 5.89-2.2l2.2 2.2 1.27-1.27L3.89 3.27l-.97-.98zm13.55 16.1C15.26 19.39 13.7 20 12 20c-3.87 0-7-3.13-7-7 0-1.7.61-3.26 1.61-4.47l9.86 9.86zM8.02 3.28L6.6 1.86l-.86.71 1.42 1.42.86-.71z"/></symbol><symbol viewBox="0 0 24 24" id="ic_alarm_on_24px"><path d="M22 5.72l-4.6-3.86-1.29 1.53 4.6 3.86L22 5.72zM7.88 3.39L6.6 1.86 2 5.71l1.29 1.53 4.59-3.85zM12 4c-4.97 0-9 4.03-9 9s4.02 9 9 9a9 9 0 0 0 0-18zm0 16c-3.87 0-7-3.13-7-7s3.13-7 7-7 7 3.13 7 7-3.13 7-7 7zm-1.46-5.47L8.41 12.4l-1.06 1.06 3.18 3.18 6-6-1.06-1.06-4.93 4.95z"/></symbol><symbol viewBox="0 0 24 24" id="ic_all_out_24px"><path d="M16.21 4.16l4 4v-4zm4 12l-4 4h4zm-12 4l-4-4v4zm-4-12l4-4h-4zm12.95-.95c-2.73-2.73-7.17-2.73-9.9 0s-2.73 7.17 0 9.9 7.17 2.73 9.9 0 2.73-7.16 0-9.9zm-1.1 8.8c-2.13 2.13-5.57 2.13-7.7 0s-2.13-5.57 0-7.7 5.57-2.13 7.7 0 2.13 5.57 0 7.7z" fill="#010101"/></symbol><symbol viewBox="0 0 24 24" id="ic_android_24px"><path d="M6 18c0 .55.45 1 1 1h1v3.5c0 .83.67 1.5 1.5 1.5s1.5-.67 1.5-1.5V19h2v3.5c0 .83.67 1.5 1.5 1.5s1.5-.67 1.5-1.5V19h1c.55 0 1-.45 1-1V8H6v10zM3.5 8C2.67 8 2 8.67 2 9.5v7c0 .83.67 1.5 1.5 1.5S5 17.33 5 16.5v-7C5 8.67 4.33 8 3.5 8zm17 0c-.83 0-1.5.67-1.5 1.5v7c0 .83.67 1.5 1.5 1.5s1.5-.67 1.5-1.5v-7c0-.83-.67-1.5-1.5-1.5zm-4.97-5.84l1.3-1.3c.2-.2.2-.51 0-.71-.2-.2-.51-.2-.71 0l-1.48 1.48A5.84 5.84 0 0 0 12 1c-.96 0-1.86.23-2.66.63L7.85.15c-.2-.2-.51-.2-.71 0-.2.2-.2.51 0 .71l1.31 1.31A5.983 5.983 0 0 0 6 7h12c0-1.99-.97-3.75-2.47-4.84zM10 5H9V4h1v1zm5 0h-1V4h1v1z"/></symbol><symbol viewBox="0 0 24 24" id="ic_announcement_24px"><path d="M20 2H4c-1.1 0-1.99.9-1.99 2L2 22l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-7 9h-2V5h2v6zm0 4h-2v-2h2v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_aspect_ratio_24px"><path d="M19 12h-2v3h-3v2h5v-5zM7 9h3V7H5v5h2V9zm14-6H3c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16.01H3V4.99h18v14.02z"/></symbol><symbol viewBox="0 0 24 24" id="ic_assessment_24px"><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM9 17H7v-7h2v7zm4 0h-2V7h2v10zm4 0h-2v-4h2v4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_assignment_24px"><path d="M19 3h-4.18C14.4 1.84 13.3 1 12 1c-1.3 0-2.4.84-2.82 2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-7 0c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zm2 14H7v-2h7v2zm3-4H7v-2h10v2zm0-4H7V7h10v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_assignment_ind_24px"><path d="M19 3h-4.18C14.4 1.84 13.3 1 12 1c-1.3 0-2.4.84-2.82 2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-7 0c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zm0 4c1.66 0 3 1.34 3 3s-1.34 3-3 3-3-1.34-3-3 1.34-3 3-3zm6 12H6v-1.4c0-2 4-3.1 6-3.1s6 1.1 6 3.1V19z"/></symbol><symbol viewBox="0 0 24 24" id="ic_assignment_late_24px"><path d="M19 3h-4.18C14.4 1.84 13.3 1 12 1c-1.3 0-2.4.84-2.82 2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-6 15h-2v-2h2v2zm0-4h-2V8h2v6zm-1-9c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1z"/></symbol><symbol viewBox="0 0 24 24" id="ic_assignment_return_24px"><path d="M19 3h-4.18C14.4 1.84 13.3 1 12 1c-1.3 0-2.4.84-2.82 2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-7 0c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zm4 12h-4v3l-5-5 5-5v3h4v4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_assignment_returned_24px"><path d="M19 3h-4.18C14.4 1.84 13.3 1 12 1c-1.3 0-2.4.84-2.82 2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-7 0c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zm0 15l-5-5h3V9h4v4h3l-5 5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_assignment_turned_in_24px"><path d="M19 3h-4.18C14.4 1.84 13.3 1 12 1c-1.3 0-2.4.84-2.82 2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-7 0c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zm-2 14l-4-4 1.41-1.41L10 14.17l6.59-6.59L18 9l-8 8z"/></symbol><symbol viewBox="0 0 24 24" id="ic_autorenew_24px"><path d="M12 6v3l4-4-4-4v3c-4.42 0-8 3.58-8 8 0 1.57.46 3.03 1.24 4.26L6.7 14.8A5.87 5.87 0 0 1 6 12c0-3.31 2.69-6 6-6zm6.76 1.74L17.3 9.2c.44.84.7 1.79.7 2.8 0 3.31-2.69 6-6 6v-3l-4 4 4 4v-3c4.42 0 8-3.58 8-8 0-1.57-.46-3.03-1.24-4.26z"/></symbol><symbol viewBox="0 0 24 24" id="ic_backup_24px"><path d="M19.35 10.04A7.49 7.49 0 0 0 12 4C9.11 4 6.6 5.64 5.35 8.04A5.994 5.994 0 0 0 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96zM14 13v4h-4v-4H7l5-5 5 5h-3z"/></symbol><symbol viewBox="0 0 24 24" id="ic_book_24px"><path d="M18 2H6c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zM6 4h5v8l-2.5-1.5L6 12V4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_bookmark_24px"><path d="M17 3H7c-1.1 0-1.99.9-1.99 2L5 21l7-3 7 3V5c0-1.1-.9-2-2-2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_bookmark_border_24px"><path d="M17 3H7c-1.1 0-1.99.9-1.99 2L5 21l7-3 7 3V5c0-1.1-.9-2-2-2zm0 15l-5-2.18L7 18V5h10v13z"/></symbol><symbol viewBox="0 0 24 24" id="ic_bug_report_24px"><path d="M20 8h-2.81a5.985 5.985 0 0 0-1.82-1.96L17 4.41 15.59 3l-2.17 2.17C12.96 5.06 12.49 5 12 5c-.49 0-.96.06-1.41.17L8.41 3 7 4.41l1.62 1.63C7.88 6.55 7.26 7.22 6.81 8H4v2h2.09c-.05.33-.09.66-.09 1v1H4v2h2v1c0 .34.04.67.09 1H4v2h2.81c1.04 1.79 2.97 3 5.19 3s4.15-1.21 5.19-3H20v-2h-2.09c.05-.33.09-.66.09-1v-1h2v-2h-2v-1c0-.34-.04-.67-.09-1H20V8zm-6 8h-4v-2h4v2zm0-4h-4v-2h4v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_build_24px"><path d="M22.7 19l-9.1-9.1c.9-2.3.4-5-1.5-6.9-2-2-5-2.4-7.4-1.3L9 6 6 9 1.6 4.7C.4 7.1.9 10.1 2.9 12.1c1.9 1.9 4.6 2.4 6.9 1.5l9.1 9.1c.4.4 1 .4 1.4 0l2.3-2.3c.5-.4.5-1.1.1-1.4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_cached_24px"><path d="M19 8l-4 4h3c0 3.31-2.69 6-6 6a5.87 5.87 0 0 1-2.8-.7l-1.46 1.46A7.93 7.93 0 0 0 12 20c4.42 0 8-3.58 8-8h3l-4-4zM6 12c0-3.31 2.69-6 6-6 1.01 0 1.97.25 2.8.7l1.46-1.46A7.93 7.93 0 0 0 12 4c-4.42 0-8 3.58-8 8H1l4 4 4-4H6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_camera_enhance_24px"><path d="M9 3L7.17 5H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2h-3.17L15 3H9zm3 15c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-1l1.25-2.75L16 13l-2.75-1.25L12 9l-1.25 2.75L8 13l2.75 1.25z"/></symbol><symbol viewBox="0 0 24 24" id="ic_card_giftcard_24px"><path d="M20 6h-2.18c.11-.31.18-.65.18-1a2.996 2.996 0 0 0-5.5-1.65l-.5.67-.5-.68A3.01 3.01 0 0 0 9 2C7.34 2 6 3.34 6 5c0 .35.07.69.18 1H4c-1.11 0-1.99.89-1.99 2L2 19c0 1.11.89 2 2 2h16c1.11 0 2-.89 2-2V8c0-1.11-.89-2-2-2zm-5-2c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zM9 4c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zm11 15H4v-2h16v2zm0-5H4V8h5.08L7 10.83 8.62 12 11 8.76l1-1.36 1 1.36L15.38 12 17 10.83 14.92 8H20v6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_card_membership_24px"><path d="M20 2H4c-1.11 0-2 .89-2 2v11c0 1.11.89 2 2 2h4v5l4-2 4 2v-5h4c1.11 0 2-.89 2-2V4c0-1.11-.89-2-2-2zm0 13H4v-2h16v2zm0-5H4V4h16v6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_card_travel_24px"><path d="M20 6h-3V4c0-1.11-.89-2-2-2H9c-1.11 0-2 .89-2 2v2H4c-1.11 0-2 .89-2 2v11c0 1.11.89 2 2 2h16c1.11 0 2-.89 2-2V8c0-1.11-.89-2-2-2zM9 4h6v2H9V4zm11 15H4v-2h16v2zm0-5H4V8h3v2h2V8h6v2h2V8h3v6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_change_history_24px"><path d="M12 7.77L18.39 18H5.61L12 7.77M12 4L2 20h20L12 4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_check_circle_24px"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/></symbol><symbol viewBox="0 0 24 24" id="ic_chrome_reader_mode_24px"><path d="M13 12h7v1.5h-7zm0-2.5h7V11h-7zm0 5h7V16h-7zM21 4H3c-1.1 0-2 .9-2 2v13c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 15h-9V6h9v13z"/></symbol><symbol viewBox="0 0 24 24" id="ic_class_24px"><path d="M18 2H6c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zM6 4h5v8l-2.5-1.5L6 12V4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_code_24px"><path d="M9.4 16.6L4.8 12l4.6-4.6L8 6l-6 6 6 6 1.4-1.4zm5.2 0l4.6-4.6-4.6-4.6L16 6l6 6-6 6-1.4-1.4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_compare_arrows_24px"><path d="M9.01 14H2v2h7.01v3L13 15l-3.99-4v3zm5.98-1v-3H22V8h-7.01V5L11 9l3.99 4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_copyright_24px"><path d="M10.08 10.86c.05-.33.16-.62.3-.87s.34-.46.59-.62c.24-.15.54-.22.91-.23.23.01.44.05.63.13.2.09.38.21.52.36s.25.33.34.53.13.42.14.64h1.79c-.02-.47-.11-.9-.28-1.29s-.4-.73-.7-1.01-.66-.5-1.08-.66-.88-.23-1.39-.23c-.65 0-1.22.11-1.7.34s-.88.53-1.2.92-.56.84-.71 1.36S8 11.29 8 11.87v.27c0 .58.08 1.12.23 1.64s.39.97.71 1.35.72.69 1.2.91 1.05.34 1.7.34c.47 0 .91-.08 1.32-.23s.77-.36 1.08-.63.56-.58.74-.94.29-.74.3-1.15h-1.79c-.01.21-.06.4-.15.58s-.21.33-.36.46-.32.23-.52.3c-.19.07-.39.09-.6.1-.36-.01-.66-.08-.89-.23-.25-.16-.45-.37-.59-.62s-.25-.55-.3-.88-.08-.67-.08-1v-.27c0-.35.03-.68.08-1.01zM12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"/></symbol><symbol viewBox="0 0 24 24" id="ic_credit_card_24px"><path d="M20 4H4c-1.11 0-1.99.89-1.99 2L2 18c0 1.11.89 2 2 2h16c1.11 0 2-.89 2-2V6c0-1.11-.89-2-2-2zm0 14H4v-6h16v6zm0-10H4V6h16v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_dashboard_24px"><path d="M3 13h8V3H3v10zm0 8h8v-6H3v6zm10 0h8V11h-8v10zm0-18v6h8V3h-8z"/></symbol><symbol viewBox="0 0 24 24" id="ic_date_range_24px"><path d="M9 11H7v2h2v-2zm4 0h-2v2h2v-2zm4 0h-2v2h2v-2zm2-7h-1V2h-2v2H8V2H6v2H5c-1.11 0-1.99.9-1.99 2L3 20a2 2 0 0 0 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 16H5V9h14v11z"/></symbol><symbol viewBox="0 0 24 24" id="ic_delete_24px"><path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_delete_forever_24px"><path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zm2.46-7.12l1.41-1.41L12 12.59l2.12-2.12 1.41 1.41L13.41 14l2.12 2.12-1.41 1.41L12 15.41l-2.12 2.12-1.41-1.41L10.59 14l-2.13-2.12zM15.5 4l-1-1h-5l-1 1H5v2h14V4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_description_24px"><path d="M14 2H6c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6zm2 16H8v-2h8v2zm0-4H8v-2h8v2zm-3-5V3.5L18.5 9H13z"/></symbol><symbol viewBox="0 0 24 24" id="ic_dns_24px"><path d="M20 13H4c-.55 0-1 .45-1 1v6c0 .55.45 1 1 1h16c.55 0 1-.45 1-1v-6c0-.55-.45-1-1-1zM7 19c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zM20 3H4c-.55 0-1 .45-1 1v6c0 .55.45 1 1 1h16c.55 0 1-.45 1-1V4c0-.55-.45-1-1-1zM7 9c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_done_24px"><path d="M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_done_all_24px"><path d="M18 7l-1.41-1.41-6.34 6.34 1.41 1.41L18 7zm4.24-1.41L11.66 16.17 7.48 12l-1.41 1.41L11.66 19l12-12-1.42-1.41zM.41 13.41L6 19l1.41-1.41L1.83 12 .41 13.41z"/></symbol><symbol viewBox="0 0 24 24" id="ic_donut_large_24px"><path d="M11 5.08V2c-5 .5-9 4.81-9 10s4 9.5 9 10v-3.08c-3-.48-6-3.4-6-6.92s3-6.44 6-6.92zM18.97 11H22c-.47-5-4-8.53-9-9v3.08C16 5.51 18.54 8 18.97 11zM13 18.92V22c5-.47 8.53-4 9-9h-3.03c-.43 3-2.97 5.49-5.97 5.92z"/></symbol><symbol viewBox="0 0 24 24" id="ic_donut_small_24px"><path d="M11 9.16V2c-5 .5-9 4.79-9 10s4 9.5 9 10v-7.16c-1-.41-2-1.52-2-2.84s1-2.43 2-2.84zM14.86 11H22c-.48-4.75-4-8.53-9-9v7.16c1 .3 1.52.98 1.86 1.84zM13 14.84V22c5-.47 8.52-4.25 9-9h-7.14c-.34.86-.86 1.54-1.86 1.84z"/></symbol><symbol viewBox="0 0 24 24" id="ic_eject_24px"><path d="M5 17h14v2H5zm7-12L5.33 15h13.34z"/></symbol><symbol viewBox="0 0 24 24" id="ic_euro_symbol_24px"><path d="M15 18.5A6.48 6.48 0 0 1 9.24 15H15v-2H8.58c-.05-.33-.08-.66-.08-1s.03-.67.08-1H15V9H9.24A6.49 6.49 0 0 1 15 5.5c1.61 0 3.09.59 4.23 1.57L21 5.3A8.955 8.955 0 0 0 15 3c-3.92 0-7.24 2.51-8.48 6H3v2h3.06a8.262 8.262 0 0 0 0 2H3v2h3.52c1.24 3.49 4.56 6 8.48 6 2.31 0 4.41-.87 6-2.3l-1.78-1.77c-1.13.98-2.6 1.57-4.22 1.57z"/></symbol><symbol viewBox="0 0 24 24" id="ic_event_24px"><path d="M17 12h-5v5h5v-5zM16 1v2H8V1H6v2H5c-1.11 0-1.99.9-1.99 2L3 19a2 2 0 0 0 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2h-1V1h-2zm3 18H5V8h14v11z"/></symbol><symbol viewBox="0 0 24 24" id="ic_event_seat_24px"><path d="M4 18v3h3v-3h10v3h3v-6H4zm15-8h3v3h-3zM2 10h3v3H2zm15 3H7V5c0-1.1.9-2 2-2h6c1.1 0 2 .9 2 2v8z"/></symbol><symbol viewBox="0 0 24 24" id="ic_exit_to_app_24px"><path d="M10.09 15.59L11.5 17l5-5-5-5-1.41 1.41L12.67 11H3v2h9.67l-2.58 2.59zM19 3H5a2 2 0 0 0-2 2v4h2V5h14v14H5v-4H3v4a2 2 0 0 0 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_explore_24px"><path d="M12 10.9c-.61 0-1.1.49-1.1 1.1s.49 1.1 1.1 1.1c.61 0 1.1-.49 1.1-1.1s-.49-1.1-1.1-1.1zM12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm2.19 12.19L6 18l3.81-8.19L18 6l-3.81 8.19z"/></symbol><symbol viewBox="0 0 24 24" id="ic_extension_24px"><path d="M20.5 11H19V7c0-1.1-.9-2-2-2h-4V3.5a2.5 2.5 0 0 0-5 0V5H4c-1.1 0-1.99.9-1.99 2v3.8H3.5c1.49 0 2.7 1.21 2.7 2.7s-1.21 2.7-2.7 2.7H2V20c0 1.1.9 2 2 2h3.8v-1.5a2.7 2.7 0 0 1 2.7-2.7 2.7 2.7 0 0 1 2.7 2.7V22H17c1.1 0 2-.9 2-2v-4h1.5a2.5 2.5 0 0 0 0-5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_face_24px"><path d="M9 11.75a1.25 1.25 0 1 0 0 2.5 1.25 1.25 0 0 0 0-2.5zm6 0a1.25 1.25 0 1 0 0 2.5 1.25 1.25 0 0 0 0-2.5zM12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8 0-.29.02-.58.05-.86 2.36-1.05 4.23-2.98 5.21-5.37a9.974 9.974 0 0 0 10.41 3.97c.21.71.33 1.47.33 2.26 0 4.41-3.59 8-8 8z"/></symbol><symbol viewBox="0 0 24 24" id="ic_favorite_24px"><path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/></symbol><symbol viewBox="0 0 24 24" id="ic_favorite_border_24px"><path d="M16.5 3c-1.74 0-3.41.81-4.5 2.09C10.91 3.81 9.24 3 7.5 3 4.42 3 2 5.42 2 8.5c0 3.78 3.4 6.86 8.55 11.54L12 21.35l1.45-1.32C18.6 15.36 22 12.28 22 8.5 22 5.42 19.58 3 16.5 3zm-4.4 15.55l-.1.1-.1-.1C7.14 14.24 4 11.39 4 8.5 4 6.5 5.5 5 7.5 5c1.54 0 3.04.99 3.57 2.36h1.87C13.46 5.99 14.96 5 16.5 5c2 0 3.5 1.5 3.5 3.5 0 2.89-3.14 5.74-7.9 10.05z"/></symbol><symbol viewBox="0 0 24 24" id="ic_feedback_24px"><path d="M20 2H4c-1.1 0-1.99.9-1.99 2L2 22l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-7 12h-2v-2h2v2zm0-4h-2V6h2v4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_find_in_page_24px"><path d="M20 19.59V8l-6-6H6c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c.45 0 .85-.15 1.19-.4l-4.43-4.43c-.8.52-1.74.83-2.76.83-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5c0 1.02-.31 1.96-.83 2.75L20 19.59zM9 13c0 1.66 1.34 3 3 3s3-1.34 3-3-1.34-3-3-3-3 1.34-3 3z"/></symbol><symbol viewBox="0 0 24 24" id="ic_find_replace_24px"><path d="M11 6c1.38 0 2.63.56 3.54 1.46L12 10h6V4l-2.05 2.05A6.976 6.976 0 0 0 11 4c-3.53 0-6.43 2.61-6.92 6H6.1A5 5 0 0 1 11 6zm5.64 9.14A6.89 6.89 0 0 0 17.92 12H15.9a5 5 0 0 1-4.9 4c-1.38 0-2.63-.56-3.54-1.46L10 12H4v6l2.05-2.05A6.976 6.976 0 0 0 11 18c1.55 0 2.98-.51 4.14-1.36L20 21.49 21.49 20l-4.85-4.86z"/></symbol><symbol viewBox="0 0 24 24" id="ic_fingerprint_24px"><path d="M17.81 4.47c-.08 0-.16-.02-.23-.06C15.66 3.42 14 3 12.01 3c-1.98 0-3.86.47-5.57 1.41a.51.51 0 0 1-.68-.2.506.506 0 0 1 .2-.68C7.82 2.52 9.86 2 12.01 2c2.13 0 3.99.47 6.03 1.52.25.13.34.43.21.67a.49.49 0 0 1-.44.28zM3.5 9.72a.5.5 0 0 1-.41-.79c.99-1.4 2.25-2.5 3.75-3.27C9.98 4.04 14 4.03 17.15 5.65c1.5.77 2.76 1.86 3.75 3.25a.5.5 0 0 1-.12.7.5.5 0 0 1-.7-.12 9.388 9.388 0 0 0-3.39-2.94c-2.87-1.47-6.54-1.47-9.4.01-1.36.7-2.5 1.7-3.4 2.96-.08.14-.23.21-.39.21zm6.25 12.07a.47.47 0 0 1-.35-.15c-.87-.87-1.34-1.43-2.01-2.64-.69-1.23-1.05-2.73-1.05-4.34 0-2.97 2.54-5.39 5.66-5.39s5.66 2.42 5.66 5.39c0 .28-.22.5-.5.5s-.5-.22-.5-.5c0-2.42-2.09-4.39-4.66-4.39-2.57 0-4.66 1.97-4.66 4.39 0 1.44.32 2.77.93 3.85.64 1.15 1.08 1.64 1.85 2.42.19.2.19.51 0 .71-.11.1-.24.15-.37.15zm7.17-1.85c-1.19 0-2.24-.3-3.1-.89-1.49-1.01-2.38-2.65-2.38-4.39 0-.28.22-.5.5-.5s.5.22.5.5c0 1.41.72 2.74 1.94 3.56.71.48 1.54.71 2.54.71.24 0 .64-.03 1.04-.1.27-.05.53.13.58.41.05.27-.13.53-.41.58-.57.11-1.07.12-1.21.12zM14.91 22c-.04 0-.09-.01-.13-.02-1.59-.44-2.63-1.03-3.72-2.1a7.297 7.297 0 0 1-2.17-5.22c0-1.62 1.38-2.94 3.08-2.94 1.7 0 3.08 1.32 3.08 2.94 0 1.07.93 1.94 2.08 1.94s2.08-.87 2.08-1.94c0-3.77-3.25-6.83-7.25-6.83-2.84 0-5.44 1.58-6.61 4.03-.39.81-.59 1.76-.59 2.8 0 .78.07 2.01.67 3.61.1.26-.03.55-.29.64-.26.1-.55-.04-.64-.29a11.14 11.14 0 0 1-.73-3.96c0-1.2.23-2.29.68-3.24 1.33-2.79 4.28-4.6 7.51-4.6 4.55 0 8.25 3.51 8.25 7.83 0 1.62-1.38 2.94-3.08 2.94s-3.08-1.32-3.08-2.94c0-1.07-.93-1.94-2.08-1.94s-2.08.87-2.08 1.94c0 1.71.66 3.31 1.87 4.51.95.94 1.86 1.46 3.27 1.85.27.07.42.35.35.61-.05.23-.26.38-.47.38z"/></symbol><symbol viewBox="0 0 24 24" id="ic_flight_land_24px"><path d="M2.5 19h19v2h-19zm7.18-5.73l4.35 1.16 5.31 1.42c.8.21 1.62-.26 1.84-1.06.21-.8-.26-1.62-1.06-1.84l-5.31-1.42-2.76-9.02L10.12 2v8.28L5.15 8.95l-.93-2.32-1.45-.39v5.17l1.6.43 5.31 1.43z"/></symbol><symbol viewBox="0 0 24 24" id="ic_flight_takeoff_24px"><path d="M2.5 19h19v2h-19zm19.57-9.36c-.21-.8-1.04-1.28-1.84-1.06L14.92 10l-6.9-6.43-1.93.51 4.14 7.17-4.97 1.33-1.97-1.54-1.45.39 1.82 3.16.77 1.33 1.6-.43 5.31-1.42 4.35-1.16L21 11.49c.81-.23 1.28-1.05 1.07-1.85z"/></symbol><symbol viewBox="0 0 24 24" id="ic_flip_to_back_24px"><path d="M9 7H7v2h2V7zm0 4H7v2h2v-2zm0-8a2 2 0 0 0-2 2h2V3zm4 12h-2v2h2v-2zm6-12v2h2c0-1.1-.9-2-2-2zm-6 0h-2v2h2V3zM9 17v-2H7a2 2 0 0 0 2 2zm10-4h2v-2h-2v2zm0-4h2V7h-2v2zm0 8c1.1 0 2-.9 2-2h-2v2zM5 7H3v12a2 2 0 0 0 2 2h12v-2H5V7zm10-2h2V3h-2v2zm0 12h2v-2h-2v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_flip_to_front_24px"><path d="M3 13h2v-2H3v2zm0 4h2v-2H3v2zm2 4v-2H3a2 2 0 0 0 2 2zM3 9h2V7H3v2zm12 12h2v-2h-2v2zm4-18H9a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 12H9V5h10v10zm-8 6h2v-2h-2v2zm-4 0h2v-2H7v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_g_translate_24px"><path d="M20 5h-9.12L10 2H4c-1.1 0-2 .9-2 2v13c0 1.1.9 2 2 2h7l1 3h8c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zM7.17 14.59c-2.25 0-4.09-1.83-4.09-4.09s1.83-4.09 4.09-4.09c1.04 0 1.99.37 2.74 1.07l.07.06-1.23 1.18-.06-.05c-.29-.27-.78-.59-1.52-.59-1.31 0-2.38 1.09-2.38 2.42s1.07 2.42 2.38 2.42c1.37 0 1.96-.87 2.12-1.46H7.08V9.91h3.95l.01.07c.04.21.05.4.05.61 0 2.35-1.61 4-3.92 4zm6.03-1.71c.33.6.74 1.18 1.19 1.7l-.54.53-.65-2.23zm.77-.76h-.99l-.31-1.04h3.99s-.34 1.31-1.56 2.74a9.18 9.18 0 0 1-1.13-1.7zM21 20c0 .55-.45 1-1 1h-7l2-2-.81-2.77.92-.92L17.79 18l.73-.73-2.71-2.68c.9-1.03 1.6-2.25 1.92-3.51H19v-1.04h-3.64V9h-1.04v1.04h-1.96L11.18 6H20c.55 0 1 .45 1 1v13z"/></symbol><symbol viewBox="0 0 24 24" id="ic_gavel_24px"><path d="M1 21h12v2H1zM5.245 8.07l2.83-2.827 14.14 14.142-2.828 2.828zM12.317 1l5.657 5.656-2.83 2.83-5.654-5.66zM3.825 9.485l5.657 5.657-2.828 2.828-5.657-5.657z"/></symbol><symbol viewBox="0 0 24 24" id="ic_get_app_24px"><path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_gif_24px"><path d="M11.5 9H13v6h-1.5zM9 9H6c-.6 0-1 .5-1 1v4c0 .5.4 1 1 1h3c.6 0 1-.5 1-1v-2H8.5v1.5h-2v-3H10V10c0-.5-.4-1-1-1zm10 1.5V9h-4.5v6H16v-2h2v-1.5h-2v-1z"/></symbol><symbol viewBox="0 0 24 24" id="ic_grade_24px"><path d="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z"/></symbol><symbol viewBox="0 0 24 24" id="ic_group_work_24px"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zM8 17.5a2.5 2.5 0 0 1 0-5 2.5 2.5 0 0 1 0 5zM9.5 8a2.5 2.5 0 0 1 5 0 2.5 2.5 0 0 1-5 0zm6.5 9.5a2.5 2.5 0 0 1 0-5 2.5 2.5 0 0 1 0 5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_help_24px"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 17h-2v-2h2v2zm2.07-7.75l-.9.92C13.45 12.9 13 13.5 13 15h-2v-.5c0-1.1.45-2.1 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41 0-1.1-.9-2-2-2s-2 .9-2 2H8c0-2.21 1.79-4 4-4s4 1.79 4 4c0 .88-.36 1.68-.93 2.25z"/></symbol><symbol viewBox="0 0 24 24" id="ic_help_outline_24px"><path d="M11 18h2v-2h-2v2zm1-16C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm0-14a4 4 0 0 0-4 4h2c0-1.1.9-2 2-2s2 .9 2 2c0 2-3 1.75-3 5h2c0-2.25 3-2.5 3-5a4 4 0 0 0-4-4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_highlight_off_24px"><path d="M14.59 8L12 10.59 9.41 8 8 9.41 10.59 12 8 14.59 9.41 16 12 13.41 14.59 16 16 14.59 13.41 12 16 9.41 14.59 8zM12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"/></symbol><symbol viewBox="0 0 24 24" id="ic_history_24px"><path d="M13 3a9 9 0 0 0-9 9H1l3.89 3.89.07.14L9 12H6c0-3.87 3.13-7 7-7s7 3.13 7 7-3.13 7-7 7c-1.93 0-3.68-.79-4.94-2.06l-1.42 1.42A8.954 8.954 0 0 0 13 21a9 9 0 0 0 0-18zm-1 5v5l4.28 2.54.72-1.21-3.5-2.08V8H12z"/></symbol><symbol viewBox="0 0 24 24" id="ic_home_24px"><path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"/></symbol><symbol viewBox="0 0 24 24" id="ic_hourglass_empty_24px"><path d="M6 2v6h.01L6 8.01 10 12l-4 4 .01.01H6V22h12v-5.99h-.01L18 16l-4-4 4-3.99-.01-.01H18V2H6zm10 14.5V20H8v-3.5l4-4 4 4zm-4-5l-4-4V4h8v3.5l-4 4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_hourglass_full_24px"><path d="M6 2v6h.01L6 8.01 10 12l-4 4 .01.01H6V22h12v-5.99h-.01L18 16l-4-4 4-3.99-.01-.01H18V2H6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_http_24px"><path d="M4.5 11h-2V9H1v6h1.5v-2.5h2V15H6V9H4.5v2zm2.5-.5h1.5V15H10v-4.5h1.5V9H7v1.5zm5.5 0H14V15h1.5v-4.5H17V9h-4.5v1.5zm9-1.5H18v6h1.5v-2h2c.8 0 1.5-.7 1.5-1.5v-1c0-.8-.7-1.5-1.5-1.5zm0 2.5h-2v-1h2v1z"/></symbol><symbol viewBox="0 0 24 24" id="ic_https_24px"><path d="M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm-6 9c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zm3.1-9H8.9V6c0-1.71 1.39-3.1 3.1-3.1 1.71 0 3.1 1.39 3.1 3.1v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_important_devices_24px"><path d="M23 11.01L18 11c-.55 0-1 .45-1 1v9c0 .55.45 1 1 1h5c.55 0 1-.45 1-1v-9c0-.55-.45-.99-1-.99zM23 20h-5v-7h5v7zM20 2H2C.89 2 0 2.89 0 4v12a2 2 0 0 0 2 2h7v2H7v2h8v-2h-2v-2h2v-2H2V4h18v5h2V4a2 2 0 0 0-2-2zm-8.03 7L11 6l-.97 3H7l2.47 1.76-.94 2.91 2.47-1.8 2.47 1.8-.94-2.91L15 9h-3.03z"/></symbol><symbol viewBox="0 0 24 24" id="ic_info_24px"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_info_outline_24px"><path d="M11 17h2v-6h-2v6zm1-15C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zM11 9h2V7h-2v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_input_24px"><path d="M21 3.01H3c-1.1 0-2 .9-2 2V9h2V4.99h18v14.03H3V15H1v4.01c0 1.1.9 1.98 2 1.98h18c1.1 0 2-.88 2-1.98v-14a2 2 0 0 0-2-2zM11 16l4-4-4-4v3H1v2h10v3z"/></symbol><symbol viewBox="0 0 24 24" id="ic_invert_colors_24px"><path d="M17.66 7.93L12 2.27 6.34 7.93c-3.12 3.12-3.12 8.19 0 11.31A7.98 7.98 0 0 0 12 21.58c2.05 0 4.1-.78 5.66-2.34 3.12-3.12 3.12-8.19 0-11.31zM12 19.59c-1.6 0-3.11-.62-4.24-1.76C6.62 16.69 6 15.19 6 13.59s.62-3.11 1.76-4.24L12 5.1v14.49z"/></symbol><symbol viewBox="0 0 24 24" id="ic_label_24px"><path d="M17.63 5.84C17.27 5.33 16.67 5 16 5L5 5.01C3.9 5.01 3 5.9 3 7v10c0 1.1.9 1.99 2 1.99L16 19c.67 0 1.27-.33 1.63-.84L22 12l-4.37-6.16z"/></symbol><symbol viewBox="0 0 24 24" id="ic_label_outline_24px"><path d="M17.63 5.84C17.27 5.33 16.67 5 16 5L5 5.01C3.9 5.01 3 5.9 3 7v10c0 1.1.9 1.99 2 1.99L16 19c.67 0 1.27-.33 1.63-.84L22 12l-4.37-6.16zM16 17H5V7h11l3.55 5L16 17z"/></symbol><symbol viewBox="0 0 24 24" id="ic_language_24px"><path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zm6.93 6h-2.95a15.65 15.65 0 0 0-1.38-3.56A8.03 8.03 0 0 1 18.92 8zM12 4.04c.83 1.2 1.48 2.53 1.91 3.96h-3.82c.43-1.43 1.08-2.76 1.91-3.96zM4.26 14C4.1 13.36 4 12.69 4 12s.1-1.36.26-2h3.38c-.08.66-.14 1.32-.14 2 0 .68.06 1.34.14 2H4.26zm.82 2h2.95c.32 1.25.78 2.45 1.38 3.56A7.987 7.987 0 0 1 5.08 16zm2.95-8H5.08a7.987 7.987 0 0 1 4.33-3.56A15.65 15.65 0 0 0 8.03 8zM12 19.96c-.83-1.2-1.48-2.53-1.91-3.96h3.82c-.43 1.43-1.08 2.76-1.91 3.96zM14.34 14H9.66c-.09-.66-.16-1.32-.16-2 0-.68.07-1.35.16-2h4.68c.09.65.16 1.32.16 2 0 .68-.07 1.34-.16 2zm.25 5.56c.6-1.11 1.06-2.31 1.38-3.56h2.95a8.03 8.03 0 0 1-4.33 3.56zM16.36 14c.08-.66.14-1.32.14-2 0-.68-.06-1.34-.14-2h3.38c.16.64.26 1.31.26 2s-.1 1.36-.26 2h-3.38z"/></symbol><symbol viewBox="0 0 24 24" id="ic_launch_24px"><path d="M19 19H5V5h7V3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14c1.1 0 2-.9 2-2v-7h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"/></symbol><symbol viewBox="0 0 24 24" id="ic_lightbulb_outline_24px"><path d="M9 21c0 .55.45 1 1 1h4c.55 0 1-.45 1-1v-1H9v1zm3-19C8.14 2 5 5.14 5 9c0 2.38 1.19 4.47 3 5.74V17c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.86-3.14-7-7-7zm2.85 11.1l-.85.6V16h-4v-2.3l-.85-.6A4.997 4.997 0 0 1 7 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 1.63-.8 3.16-2.15 4.1z"/></symbol><symbol viewBox="0 0 24 24" id="ic_line_style_24px"><path d="M3 16h5v-2H3v2zm6.5 0h5v-2h-5v2zm6.5 0h5v-2h-5v2zM3 20h2v-2H3v2zm4 0h2v-2H7v2zm4 0h2v-2h-2v2zm4 0h2v-2h-2v2zm4 0h2v-2h-2v2zM3 12h8v-2H3v2zm10 0h8v-2h-8v2zM3 4v4h18V4H3z"/></symbol><symbol viewBox="0 0 24 24" id="ic_line_weight_24px"><path d="M3 17h18v-2H3v2zm0 3h18v-1H3v1zm0-7h18v-3H3v3zm0-9v4h18V4H3z"/></symbol><symbol viewBox="0 0 24 24" id="ic_list_24px"><path d="M3 13h2v-2H3v2zm0 4h2v-2H3v2zm0-8h2V7H3v2zm4 4h14v-2H7v2zm0 4h14v-2H7v2zM7 7v2h14V7H7z"/></symbol><symbol viewBox="0 0 24 24" id="ic_lock_24px"><path d="M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm-6 9c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zm3.1-9H8.9V6c0-1.71 1.39-3.1 3.1-3.1 1.71 0 3.1 1.39 3.1 3.1v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_lock_open_24px"><path d="M12 17c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm6-9h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6h1.9c0-1.71 1.39-3.1 3.1-3.1 1.71 0 3.1 1.39 3.1 3.1v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm0 12H6V10h12v10z"/></symbol><symbol viewBox="0 0 24 24" id="ic_lock_outline_24px"><path d="M12 17c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm6-9h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zM8.9 6c0-1.71 1.39-3.1 3.1-3.1s3.1 1.39 3.1 3.1v2H8.9V6zM18 20H6V10h12v10z"/></symbol><symbol viewBox="0 0 24 24" id="ic_loyalty_24px"><path d="M21.41 11.58l-9-9C12.05 2.22 11.55 2 11 2H4c-1.1 0-2 .9-2 2v7c0 .55.22 1.05.59 1.42l9 9c.36.36.86.58 1.41.58.55 0 1.05-.22 1.41-.59l7-7c.37-.36.59-.86.59-1.41 0-.55-.23-1.06-.59-1.42zM5.5 7C4.67 7 4 6.33 4 5.5S4.67 4 5.5 4 7 4.67 7 5.5 6.33 7 5.5 7zm11.77 8.27L13 19.54l-4.27-4.27A2.5 2.5 0 0 1 10.5 11c.69 0 1.32.28 1.77.74l.73.72.73-.73a2.5 2.5 0 0 1 3.54 3.54z"/></symbol><symbol viewBox="0 0 24 24" id="ic_markunread_mailbox_24px"><path d="M20 6H10v6H8V4h6V0H6v6H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_motorcycle_24px"><path d="M19.44 9.03L15.41 5H11v2h3.59l2 2H5c-2.8 0-5 2.2-5 5s2.2 5 5 5c2.46 0 4.45-1.69 4.9-4h1.65l2.77-2.77c-.21.54-.32 1.14-.32 1.77 0 2.8 2.2 5 5 5s5-2.2 5-5c0-2.65-1.97-4.77-4.56-4.97zM7.82 15C7.4 16.15 6.28 17 5 17c-1.63 0-3-1.37-3-3s1.37-3 3-3c1.28 0 2.4.85 2.82 2H5v2h2.82zM19 17c-1.66 0-3-1.34-3-3s1.34-3 3-3 3 1.34 3 3-1.34 3-3 3z"/></symbol><symbol viewBox="0 0 24 24" id="ic_note_add_24px"><path d="M14 2H6c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6zm2 14h-3v3h-2v-3H8v-2h3v-3h2v3h3v2zm-3-7V3.5L18.5 9H13z"/></symbol><symbol viewBox="0 0 24 24" id="ic_offline_pin_24px"><path d="M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10 10-4.5 10-10S17.5 2 12 2zm5 16H7v-2h10v2zm-6.7-4L7 10.7l1.4-1.4 1.9 1.9 5.3-5.3L17 7.3 10.3 14z"/></symbol><symbol viewBox="0 0 24 24" id="ic_opacity_24px"><path d="M17.66 8L12 2.35 6.34 8A8.02 8.02 0 0 0 4 13.64c0 2 .78 4.11 2.34 5.67a7.99 7.99 0 0 0 11.32 0c1.56-1.56 2.34-3.67 2.34-5.67S19.22 9.56 17.66 8zM6 14c.01-2 .62-3.27 1.76-4.4L12 5.27l4.24 4.38C17.38 10.77 17.99 12 18 14H6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_open_in_browser_24px"><path d="M19 4H5a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h4v-2H5V8h14v10h-4v2h4c1.1 0 2-.9 2-2V6a2 2 0 0 0-2-2zm-7 6l-4 4h3v6h2v-6h3l-4-4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_open_in_new_24px"><path d="M19 19H5V5h7V3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14c1.1 0 2-.9 2-2v-7h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"/></symbol><symbol viewBox="0 0 24 24" id="ic_open_with_24px"><path d="M10 9h4V6h3l-5-5-5 5h3v3zm-1 1H6V7l-5 5 5 5v-3h3v-4zm14 2l-5-5v3h-3v4h3v3l5-5zm-9 3h-4v3H7l5 5 5-5h-3v-3z"/></symbol><symbol viewBox="0 0 24 24" id="ic_pageview_24px"><path d="M11.5 9a2.5 2.5 0 0 0 0 5 2.5 2.5 0 0 0 0-5zM20 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm-3.21 14.21l-2.91-2.91c-.69.44-1.51.7-2.39.7C9.01 16 7 13.99 7 11.5S9.01 7 11.5 7 16 9.01 16 11.5c0 .88-.26 1.69-.7 2.39l2.91 2.9-1.42 1.42z"/></symbol><symbol viewBox="0 0 24 24" id="ic_pan_tool_24px"><path d="M23 5.5V20c0 2.2-1.8 4-4 4h-7.3c-1.08 0-2.1-.43-2.85-1.19L1 14.83s1.26-1.23 1.3-1.25c.22-.19.49-.29.79-.29.22 0 .42.06.6.16.04.01 4.31 2.46 4.31 2.46V4c0-.83.67-1.5 1.5-1.5S11 3.17 11 4v7h1V1.5c0-.83.67-1.5 1.5-1.5S15 .67 15 1.5V11h1V2.5c0-.83.67-1.5 1.5-1.5s1.5.67 1.5 1.5V11h1V5.5c0-.83.67-1.5 1.5-1.5s1.5.67 1.5 1.5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_payment_24px"><path d="M20 4H4c-1.11 0-1.99.89-1.99 2L2 18c0 1.11.89 2 2 2h16c1.11 0 2-.89 2-2V6c0-1.11-.89-2-2-2zm0 14H4v-6h16v6zm0-10H4V6h16v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_perm_camera_mic_24px"><path d="M20 5h-3.17L15 3H9L7.17 5H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h7v-2.09c-2.83-.48-5-2.94-5-5.91h2c0 2.21 1.79 4 4 4s4-1.79 4-4h2c0 2.97-2.17 5.43-5 5.91V21h7c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm-6 8c0 1.1-.9 2-2 2s-2-.9-2-2V9c0-1.1.9-2 2-2s2 .9 2 2v4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_perm_contact_calendar_24px"><path d="M19 3h-1V1h-2v2H8V1H6v2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-7 3c1.66 0 3 1.34 3 3s-1.34 3-3 3-3-1.34-3-3 1.34-3 3-3zm6 12H6v-1c0-2 4-3.1 6-3.1s6 1.1 6 3.1v1z"/></symbol><symbol viewBox="0 0 24 24" id="ic_perm_data_setting_24px"><path d="M18.99 11.5c.34 0 .67.03 1 .07L20 0 0 20h11.56c-.04-.33-.07-.66-.07-1 0-4.14 3.36-7.5 7.5-7.5zm3.71 7.99c.02-.16.04-.32.04-.49 0-.17-.01-.33-.04-.49l1.06-.83a.26.26 0 0 0 .06-.32l-1-1.73c-.06-.11-.19-.15-.31-.11l-1.24.5c-.26-.2-.54-.37-.85-.49l-.19-1.32c-.01-.12-.12-.21-.24-.21h-2c-.12 0-.23.09-.25.21l-.19 1.32c-.3.13-.59.29-.85.49l-1.24-.5c-.11-.04-.24 0-.31.11l-1 1.73c-.06.11-.04.24.06.32l1.06.83a3.908 3.908 0 0 0 0 .98l-1.06.83a.26.26 0 0 0-.06.32l1 1.73c.06.11.19.15.31.11l1.24-.5c.26.2.54.37.85.49l.19 1.32c.02.12.12.21.25.21h2c.12 0 .23-.09.25-.21l.19-1.32c.3-.13.59-.29.84-.49l1.25.5c.11.04.24 0 .31-.11l1-1.73a.26.26 0 0 0-.06-.32l-1.07-.83zm-3.71 1.01c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_perm_device_information_24px"><path d="M13 7h-2v2h2V7zm0 4h-2v6h2v-6zm4-9.99L7 1c-1.1 0-2 .9-2 2v18c0 1.1.9 2 2 2h10c1.1 0 2-.9 2-2V3c0-1.1-.9-1.99-2-1.99zM17 19H7V5h10v14z"/></symbol><symbol viewBox="0 0 24 24" id="ic_perm_identity_24px"><path d="M12 5.9a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2m0 9c2.97 0 6.1 1.46 6.1 2.1v1.1H5.9V17c0-.64 3.13-2.1 6.1-2.1M12 4C9.79 4 8 5.79 8 8s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm0 9c-2.67 0-8 1.34-8 4v3h16v-3c0-2.66-5.33-4-8-4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_perm_media_24px"><path d="M2 6H0v5h.01L0 20c0 1.1.9 2 2 2h18v-2H2V6zm20-2h-8l-2-2H6c-1.1 0-1.99.9-1.99 2L4 16c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zM7 15l4.5-6 3.5 4.51 2.5-3.01L21 15H7z"/></symbol><symbol viewBox="0 0 24 24" id="ic_perm_phone_msg_24px"><path d="M20 15.5c-1.25 0-2.45-.2-3.57-.57a1.02 1.02 0 0 0-1.02.24l-2.2 2.2a15.074 15.074 0 0 1-6.59-6.58l2.2-2.21c.28-.27.36-.66.25-1.01A11.36 11.36 0 0 1 8.5 4c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1 0 9.39 7.61 17 17 17 .55 0 1-.45 1-1v-3.5c0-.55-.45-1-1-1zM12 3v10l3-3h6V3h-9z"/></symbol><symbol viewBox="0 0 24 24" id="ic_perm_scan_wifi_24px"><path d="M12 3C6.95 3 3.15 4.85 0 7.23L12 22 24 7.25C20.85 4.87 17.05 3 12 3zm1 13h-2v-6h2v6zm-2-8V6h2v2h-2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_pets_24px"><circle cx="4.5" cy="9.5" r="2.5"/><circle cx="9" cy="5.5" r="2.5"/><circle cx="15" cy="5.5" r="2.5"/><circle cx="19.5" cy="9.5" r="2.5"/><path d="M17.34 14.86c-.87-1.02-1.6-1.89-2.48-2.91-.46-.54-1.05-1.08-1.75-1.32-.11-.04-.22-.07-.33-.09-.25-.04-.52-.04-.78-.04s-.53 0-.79.05c-.11.02-.22.05-.33.09-.7.24-1.28.78-1.75 1.32-.87 1.02-1.6 1.89-2.48 2.91-1.31 1.31-2.92 2.76-2.62 4.79.29 1.02 1.02 2.03 2.33 2.32.73.15 3.06-.44 5.54-.44h.18c2.48 0 4.81.58 5.54.44 1.31-.29 2.04-1.31 2.33-2.32.31-2.04-1.3-3.49-2.61-4.8z"/></symbol><symbol viewBox="0 0 24 24" id="ic_picture_in_picture_24px"><path d="M19 7h-8v6h8V7zm2-4H3c-1.1 0-2 .9-2 2v14c0 1.1.9 1.98 2 1.98h18c1.1 0 2-.88 2-1.98V5c0-1.1-.9-2-2-2zm0 16.01H3V4.98h18v14.03z"/></symbol><symbol viewBox="0 0 24 24" id="ic_picture_in_picture_alt_24px"><path d="M19 11h-8v6h8v-6zm4 8V4.98C23 3.88 22.1 3 21 3H3c-1.1 0-2 .88-2 1.98V19c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2zm-2 .02H3V4.97h18v14.05z"/></symbol><symbol viewBox="0 0 24 24" id="ic_play_for_work_24px"><path d="M11 5v5.59H7.5l4.5 4.5 4.5-4.5H13V5h-2zm-5 9c0 3.31 2.69 6 6 6s6-2.69 6-6h-2c0 2.21-1.79 4-4 4s-4-1.79-4-4H6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_polymer_24px"><path d="M19 4h-4L7.11 16.63 4.5 12 9 4H5L.5 12 5 20h4l7.89-12.63L19.5 12 15 20h4l4.5-8z"/></symbol><symbol viewBox="0 0 24 24" id="ic_power_settings_new_24px"><path d="M13 3h-2v10h2V3zm4.83 2.17l-1.42 1.42A6.92 6.92 0 0 1 19 12c0 3.87-3.13 7-7 7A6.995 6.995 0 0 1 7.58 6.58L6.17 5.17A8.932 8.932 0 0 0 3 12a9 9 0 0 0 18 0c0-2.74-1.23-5.18-3.17-6.83z"/></symbol><symbol viewBox="0 0 24 24" id="ic_pregnant_woman_24px"><path d="M9 4c0-1.11.89-2 2-2s2 .89 2 2-.89 2-2 2-2-.89-2-2zm7 9a3.285 3.285 0 0 0-2-3c0-1.66-1.34-3-3-3s-3 1.34-3 3v7h2v5h3v-5h3v-4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_print_24px"><path d="M19 8H5c-1.66 0-3 1.34-3 3v6h4v4h12v-4h4v-6c0-1.66-1.34-3-3-3zm-3 11H8v-5h8v5zm3-7c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1zm-1-9H6v4h12V3z"/></symbol><symbol viewBox="0 0 24 24" id="ic_query_builder_24px"><path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm.5-13H11v6l5.25 3.15.75-1.23-4.5-2.67z"/></symbol><symbol viewBox="0 0 24 24" id="ic_question_answer_24px"><path d="M21 6h-2v9H6v2c0 .55.45 1 1 1h11l4 4V7c0-.55-.45-1-1-1zm-4 6V3c0-.55-.45-1-1-1H3c-.55 0-1 .45-1 1v14l4-4h10c.55 0 1-.45 1-1z"/></symbol><symbol viewBox="0 0 24 24" id="ic_receipt_24px"><path d="M18 17H6v-2h12v2zm0-4H6v-2h12v2zm0-4H6V7h12v2zM3 22l1.5-1.5L6 22l1.5-1.5L9 22l1.5-1.5L12 22l1.5-1.5L15 22l1.5-1.5L18 22l1.5-1.5L21 22V2l-1.5 1.5L18 2l-1.5 1.5L15 2l-1.5 1.5L12 2l-1.5 1.5L9 2 7.5 3.5 6 2 4.5 3.5 3 2v20z"/></symbol><symbol viewBox="0 0 24 24" id="ic_record_voice_over_24px"><circle cx="9" cy="9" r="4"/><path d="M9 15c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4zm7.76-9.64l-1.68 1.69c.84 1.18.84 2.71 0 3.89l1.68 1.69c2.02-2.02 2.02-5.07 0-7.27zM20.07 2l-1.63 1.63c2.77 3.02 2.77 7.56 0 10.74L20.07 16c3.9-3.89 3.91-9.95 0-14z"/></symbol><symbol viewBox="0 0 24 24" id="ic_redeem_24px"><path d="M20 6h-2.18c.11-.31.18-.65.18-1a2.996 2.996 0 0 0-5.5-1.65l-.5.67-.5-.68A3.01 3.01 0 0 0 9 2C7.34 2 6 3.34 6 5c0 .35.07.69.18 1H4c-1.11 0-1.99.89-1.99 2L2 19c0 1.11.89 2 2 2h16c1.11 0 2-.89 2-2V8c0-1.11-.89-2-2-2zm-5-2c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zM9 4c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zm11 15H4v-2h16v2zm0-5H4V8h5.08L7 10.83 8.62 12 11 8.76l1-1.36 1 1.36L15.38 12 17 10.83 14.92 8H20v6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_remove_shopping_cart_24px"><path d="M22.73 22.73L2.77 2.77 2 2l-.73-.73L0 2.54l4.39 4.39 2.21 4.66-1.35 2.45c-.16.28-.25.61-.25.96 0 1.1.9 2 2 2h7.46l1.38 1.38A1.997 1.997 0 0 0 17 22c.67 0 1.26-.33 1.62-.84L21.46 24l1.27-1.27zM7.42 15c-.14 0-.25-.11-.25-.25l.03-.12.9-1.63h2.36l2 2H7.42zm8.13-2c.75 0 1.41-.41 1.75-1.03l3.58-6.49A1.003 1.003 0 0 0 20 4H6.54l9.01 9zM7 18c-1.1 0-1.99.9-1.99 2S5.9 22 7 22s2-.9 2-2-.9-2-2-2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_reorder_24px"><path d="M3 15h18v-2H3v2zm0 4h18v-2H3v2zm0-8h18V9H3v2zm0-6v2h18V5H3z"/></symbol><symbol viewBox="0 0 24 24" id="ic_report_problem_24px"><path d="M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_restore_24px"><path d="M13 3a9 9 0 0 0-9 9H1l3.89 3.89.07.14L9 12H6c0-3.87 3.13-7 7-7s7 3.13 7 7-3.13 7-7 7c-1.93 0-3.68-.79-4.94-2.06l-1.42 1.42A8.954 8.954 0 0 0 13 21a9 9 0 0 0 0-18zm-1 5v5l4.28 2.54.72-1.21-3.5-2.08V8H12z"/></symbol><symbol viewBox="0 0 24 24" id="ic_restore_page_24px"><path d="M14 2H6c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6zm-2 16c-2.05 0-3.81-1.24-4.58-3h1.71c.63.9 1.68 1.5 2.87 1.5 1.93 0 3.5-1.57 3.5-3.5S13.93 9.5 12 9.5a3.5 3.5 0 0 0-3.1 1.9l1.6 1.6h-4V9l1.3 1.3C8.69 8.92 10.23 8 12 8c2.76 0 5 2.24 5 5s-2.24 5-5 5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_room_24px"><path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5a2.5 2.5 0 0 1 0-5 2.5 2.5 0 0 1 0 5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_rounded_corner_24px"><path d="M19 19h2v2h-2v-2zm0-2h2v-2h-2v2zM3 13h2v-2H3v2zm0 4h2v-2H3v2zm0-8h2V7H3v2zm0-4h2V3H3v2zm4 0h2V3H7v2zm8 16h2v-2h-2v2zm-4 0h2v-2h-2v2zm4 0h2v-2h-2v2zm-8 0h2v-2H7v2zm-4 0h2v-2H3v2zM21 8c0-2.76-2.24-5-5-5h-5v2h5c1.65 0 3 1.35 3 3v5h2V8z"/></symbol><symbol viewBox="0 0 24 24" id="ic_rowing_24px"><path d="M8.5 14.5L4 19l1.5 1.5L9 17h2l-2.5-2.5zM15 1c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm6 20.01L18 24l-2.99-3.01V19.5l-7.1-7.09c-.31.05-.61.07-.91.07v-2.16c1.66.03 3.61-.87 4.67-2.04l1.4-1.55c.19-.21.43-.38.69-.5.29-.14.62-.23.96-.23h.03C15.99 6.01 17 7.02 17 8.26v5.75a3 3 0 0 1-.92 2.16l-3.58-3.58v-2.27c-.63.52-1.43 1.02-2.29 1.39L16.5 18H18l3 3.01z"/></symbol><symbol viewBox="0 0 24 24" id="ic_schedule_24px"><path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm.5-13H11v6l5.25 3.15.75-1.23-4.5-2.67z"/></symbol><symbol viewBox="0 0 24 24" id="ic_search_24px"><path d="M15.5 14h-.79l-.28-.27A6.47 6.47 0 0 0 16 9.5 6.5 6.5 0 1 0 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/></symbol><symbol viewBox="0 0 24 24" id="ic_settings_24px"><path d="M19.43 12.98c.04-.32.07-.64.07-.98s-.03-.66-.07-.98l2.11-1.65c.19-.15.24-.42.12-.64l-2-3.46c-.12-.22-.39-.3-.61-.22l-2.49 1c-.52-.4-1.08-.73-1.69-.98l-.38-2.65A.488.488 0 0 0 14 2h-4c-.25 0-.46.18-.49.42l-.38 2.65c-.61.25-1.17.59-1.69.98l-2.49-1c-.23-.09-.49 0-.61.22l-2 3.46c-.13.22-.07.49.12.64l2.11 1.65c-.04.32-.07.65-.07.98s.03.66.07.98l-2.11 1.65c-.19.15-.24.42-.12.64l2 3.46c.12.22.39.3.61.22l2.49-1c.52.4 1.08.73 1.69.98l.38 2.65c.03.24.24.42.49.42h4c.25 0 .46-.18.49-.42l.38-2.65c.61-.25 1.17-.59 1.69-.98l2.49 1c.23.09.49 0 .61-.22l2-3.46c.12-.22.07-.49-.12-.64l-2.11-1.65zM12 15.5c-1.93 0-3.5-1.57-3.5-3.5s1.57-3.5 3.5-3.5 3.5 1.57 3.5 3.5-1.57 3.5-3.5 3.5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_settings_applications_24px"><path d="M12 10c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm7-7H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2zm-1.75 9c0 .23-.02.46-.05.68l1.48 1.16c.13.11.17.3.08.45l-1.4 2.42c-.09.15-.27.21-.43.15l-1.74-.7c-.36.28-.76.51-1.18.69l-.26 1.85c-.03.17-.18.3-.35.3h-2.8c-.17 0-.32-.13-.35-.29l-.26-1.85c-.43-.18-.82-.41-1.18-.69l-1.74.7c-.16.06-.34 0-.43-.15l-1.4-2.42a.353.353 0 0 1 .08-.45l1.48-1.16c-.03-.23-.05-.46-.05-.69 0-.23.02-.46.05-.68l-1.48-1.16a.353.353 0 0 1-.08-.45l1.4-2.42c.09-.15.27-.21.43-.15l1.74.7c.36-.28.76-.51 1.18-.69l.26-1.85c.03-.17.18-.3.35-.3h2.8c.17 0 .32.13.35.29l.26 1.85c.43.18.82.41 1.18.69l1.74-.7c.16-.06.34 0 .43.15l1.4 2.42c.09.15.05.34-.08.45l-1.48 1.16c.03.23.05.46.05.69z"/></symbol><symbol viewBox="0 0 24 24" id="ic_settings_backup_restore_24px"><path d="M14 12c0-1.1-.9-2-2-2s-2 .9-2 2 .9 2 2 2 2-.9 2-2zm-2-9a9 9 0 0 0-9 9H0l4 4 4-4H5c0-3.87 3.13-7 7-7s7 3.13 7 7a6.995 6.995 0 0 1-11.06 5.7l-1.42 1.44A9 9 0 1 0 12 3z"/></symbol><symbol viewBox="0 0 24 24" id="ic_settings_bluetooth_24px"><path d="M11 24h2v-2h-2v2zm-4 0h2v-2H7v2zm8 0h2v-2h-2v2zm2.71-18.29L12 0h-1v7.59L6.41 3 5 4.41 10.59 10 5 15.59 6.41 17 11 12.41V20h1l5.71-5.71-4.3-4.29 4.3-4.29zM13 3.83l1.88 1.88L13 7.59V3.83zm1.88 10.46L13 16.17v-3.76l1.88 1.88z"/></symbol><symbol viewBox="0 0 24 24" id="ic_settings_brightness_24px"><path d="M21 3H3c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16.01H3V4.99h18v14.02zM8 16h2.5l1.5 1.5 1.5-1.5H16v-2.5l1.5-1.5-1.5-1.5V8h-2.5L12 6.5 10.5 8H8v2.5L6.5 12 8 13.5V16zm4-7c1.66 0 3 1.34 3 3s-1.34 3-3 3V9z"/></symbol><symbol viewBox="0 0 24 24" id="ic_settings_cell_24px"><path d="M7 24h2v-2H7v2zm4 0h2v-2h-2v2zm4 0h2v-2h-2v2zM16 .01L8 0C6.9 0 6 .9 6 2v16c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V2c0-1.1-.9-1.99-2-1.99zM16 16H8V4h8v12z"/></symbol><symbol viewBox="0 0 24 24" id="ic_settings_ethernet_24px"><path d="M7.77 6.76L6.23 5.48.82 12l5.41 6.52 1.54-1.28L3.42 12l4.35-5.24zM7 13h2v-2H7v2zm10-2h-2v2h2v-2zm-6 2h2v-2h-2v2zm6.77-7.52l-1.54 1.28L20.58 12l-4.35 5.24 1.54 1.28L23.18 12l-5.41-6.52z"/></symbol><symbol viewBox="0 0 24 24" id="ic_settings_input_antenna_24px"><path d="M12 5c-3.87 0-7 3.13-7 7h2c0-2.76 2.24-5 5-5s5 2.24 5 5h2c0-3.87-3.13-7-7-7zm1 9.29c.88-.39 1.5-1.26 1.5-2.29a2.5 2.5 0 0 0-5 0c0 1.02.62 1.9 1.5 2.29v3.3L7.59 21 9 22.41l3-3 3 3L16.41 21 13 17.59v-3.3zM12 1C5.93 1 1 5.93 1 12h2a9 9 0 0 1 18 0h2c0-6.07-4.93-11-11-11z"/></symbol><symbol viewBox="0 0 24 24" id="ic_settings_input_component_24px"><path d="M5 2c0-.55-.45-1-1-1s-1 .45-1 1v4H1v6h6V6H5V2zm4 14c0 1.3.84 2.4 2 2.82V23h2v-4.18c1.16-.41 2-1.51 2-2.82v-2H9v2zm-8 0c0 1.3.84 2.4 2 2.82V23h2v-4.18C6.16 18.4 7 17.3 7 16v-2H1v2zM21 6V2c0-.55-.45-1-1-1s-1 .45-1 1v4h-2v6h6V6h-2zm-8-4c0-.55-.45-1-1-1s-1 .45-1 1v4H9v6h6V6h-2V2zm4 14c0 1.3.84 2.4 2 2.82V23h2v-4.18c1.16-.41 2-1.51 2-2.82v-2h-6v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_settings_input_composite_24px"><path d="M5 2c0-.55-.45-1-1-1s-1 .45-1 1v4H1v6h6V6H5V2zm4 14c0 1.3.84 2.4 2 2.82V23h2v-4.18c1.16-.41 2-1.51 2-2.82v-2H9v2zm-8 0c0 1.3.84 2.4 2 2.82V23h2v-4.18C6.16 18.4 7 17.3 7 16v-2H1v2zM21 6V2c0-.55-.45-1-1-1s-1 .45-1 1v4h-2v6h6V6h-2zm-8-4c0-.55-.45-1-1-1s-1 .45-1 1v4H9v6h6V6h-2V2zm4 14c0 1.3.84 2.4 2 2.82V23h2v-4.18c1.16-.41 2-1.51 2-2.82v-2h-6v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_settings_input_hdmi_24px"><path d="M18 7V4c0-1.1-.9-2-2-2H8c-1.1 0-2 .9-2 2v3H5v6l3 6v3h8v-3l3-6V7h-1zM8 4h8v3h-2V5h-1v2h-2V5h-1v2H8V4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_settings_input_svideo_24px"><path d="M8 11.5c0-.83-.67-1.5-1.5-1.5S5 10.67 5 11.5 5.67 13 6.5 13 8 12.33 8 11.5zm7-5c0-.83-.67-1.5-1.5-1.5h-3C9.67 5 9 5.67 9 6.5S9.67 8 10.5 8h3c.83 0 1.5-.67 1.5-1.5zM8.5 15c-.83 0-1.5.67-1.5 1.5S7.67 18 8.5 18s1.5-.67 1.5-1.5S9.33 15 8.5 15zM12 1C5.93 1 1 5.93 1 12s4.93 11 11 11 11-4.93 11-11S18.07 1 12 1zm0 20c-4.96 0-9-4.04-9-9s4.04-9 9-9 9 4.04 9 9-4.04 9-9 9zm5.5-11c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5zm-2 5c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_settings_overscan_24px"><path d="M12.01 5.5L10 8h4l-1.99-2.5zM18 10v4l2.5-1.99L18 10zM6 10l-2.5 2.01L6 14v-4zm8 6h-4l2.01 2.5L14 16zm7-13H3c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16.01H3V4.99h18v14.02z"/></symbol><symbol viewBox="0 0 24 24" id="ic_settings_phone_24px"><path d="M13 9h-2v2h2V9zm4 0h-2v2h2V9zm3 6.5c-1.25 0-2.45-.2-3.57-.57a1.02 1.02 0 0 0-1.02.24l-2.2 2.2a15.074 15.074 0 0 1-6.59-6.58l2.2-2.21c.28-.27.36-.66.25-1.01A11.36 11.36 0 0 1 8.5 4c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1 0 9.39 7.61 17 17 17 .55 0 1-.45 1-1v-3.5c0-.55-.45-1-1-1zM19 9v2h2V9h-2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_settings_power_24px"><path d="M7 24h2v-2H7v2zm4 0h2v-2h-2v2zm2-22h-2v10h2V2zm3.56 2.44l-1.45 1.45A5.97 5.97 0 0 1 18 11c0 3.31-2.69 6-6 6s-6-2.69-6-6c0-2.17 1.16-4.06 2.88-5.12L7.44 4.44A7.96 7.96 0 0 0 4 11c0 4.42 3.58 8 8 8s8-3.58 8-8a7.96 7.96 0 0 0-3.44-6.56zM15 24h2v-2h-2v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_settings_remote_24px"><path d="M15 9H9c-.55 0-1 .45-1 1v12c0 .55.45 1 1 1h6c.55 0 1-.45 1-1V10c0-.55-.45-1-1-1zm-3 6c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zM7.05 6.05l1.41 1.41a5.022 5.022 0 0 1 7.08 0l1.41-1.41C15.68 4.78 13.93 4 12 4s-3.68.78-4.95 2.05zM12 0C8.96 0 6.21 1.23 4.22 3.22l1.41 1.41C7.26 3.01 9.51 2 12 2s4.74 1.01 6.36 2.64l1.41-1.41C17.79 1.23 15.04 0 12 0z"/></symbol><symbol viewBox="0 0 24 24" id="ic_settings_voice_24px"><path d="M7 24h2v-2H7v2zm5-11c1.66 0 2.99-1.34 2.99-3L15 4c0-1.66-1.34-3-3-3S9 2.34 9 4v6c0 1.66 1.34 3 3 3zm-1 11h2v-2h-2v2zm4 0h2v-2h-2v2zm4-14h-1.7c0 3-2.54 5.1-5.3 5.1S6.7 13 6.7 10H5c0 3.41 2.72 6.23 6 6.72V20h2v-3.28c3.28-.49 6-3.31 6-6.72z"/></symbol><symbol viewBox="0 0 24 24" id="ic_shop_24px"><path d="M16 6V4c0-1.11-.89-2-2-2h-4c-1.11 0-2 .89-2 2v2H2v13c0 1.11.89 2 2 2h16c1.11 0 2-.89 2-2V6h-6zm-6-2h4v2h-4V4zM9 18V9l7.5 4L9 18z"/></symbol><symbol viewBox="0 0 24 24" id="ic_shop_two_24px"><path d="M3 9H1v11c0 1.11.89 2 2 2h14c1.11 0 2-.89 2-2H3V9zm15-4V3c0-1.11-.89-2-2-2h-4c-1.11 0-2 .89-2 2v2H5v11c0 1.11.89 2 2 2h14c1.11 0 2-.89 2-2V5h-5zm-6-2h4v2h-4V3zm0 12V8l5.5 3-5.5 4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_shopping_basket_24px"><path d="M17.21 9l-4.38-6.56a.993.993 0 0 0-.83-.42c-.32 0-.64.14-.83.43L6.79 9H2c-.55 0-1 .45-1 1 0 .09.01.18.04.27l2.54 9.27c.23.84 1 1.46 1.92 1.46h13c.92 0 1.69-.62 1.93-1.46l2.54-9.27L23 10c0-.55-.45-1-1-1h-4.79zM9 9l3-4.4L15 9H9zm3 8c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_shopping_cart_24px"><path d="M7 18c-1.1 0-1.99.9-1.99 2S5.9 22 7 22s2-.9 2-2-.9-2-2-2zM1 2v2h2l3.6 7.59-1.35 2.45c-.16.28-.25.61-.25.96 0 1.1.9 2 2 2h12v-2H7.42c-.14 0-.25-.11-.25-.25l.03-.12.9-1.63h7.45c.75 0 1.41-.41 1.75-1.03l3.58-6.49A1.003 1.003 0 0 0 20 4H5.21l-.94-2H1zm16 16c-1.1 0-1.99.9-1.99 2s.89 2 1.99 2 2-.9 2-2-.9-2-2-2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_speaker_notes_24px"><path d="M20 2H4c-1.1 0-1.99.9-1.99 2L2 22l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zM8 14H6v-2h2v2zm0-3H6V9h2v2zm0-3H6V6h2v2zm7 6h-5v-2h5v2zm3-3h-8V9h8v2zm0-3h-8V6h8v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_speaker_notes_off_24px"><path d="M10.54 11l-.54-.54L7.54 8 6 6.46 2.38 2.84 1.27 1.73 0 3l2.01 2.01L2 22l4-4h9l5.73 5.73L22 22.46 17.54 18l-7-7zM8 14H6v-2h2v2zm-2-3V9l2 2H6zm14-9H4.08L10 7.92V6h8v2h-7.92l1 1H18v2h-4.92l6.99 6.99C21.14 17.95 22 17.08 22 16V4c0-1.1-.9-2-2-2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_spellcheck_24px"><path d="M12.45 16h2.09L9.43 3H7.57L2.46 16h2.09l1.12-3h5.64l1.14 3zm-6.02-5L8.5 5.48 10.57 11H6.43zm15.16.59l-8.09 8.09L9.83 16l-1.41 1.41 5.09 5.09L23 13l-1.41-1.41z"/></symbol><symbol viewBox="0 0 24 24" id="ic_stars_24px"><path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zm4.24 16L12 15.45 7.77 18l1.12-4.81-3.73-3.23 4.92-.42L12 5l1.92 4.53 4.92.42-3.73 3.23L16.23 18z"/></symbol><symbol viewBox="0 0 24 24" id="ic_store_24px"><path d="M20 4H4v2h16V4zm1 10v-2l-1-5H4l-1 5v2h1v6h10v-6h4v6h2v-6h1zm-9 4H6v-4h6v4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_subject_24px"><path d="M14 17H4v2h10v-2zm6-8H4v2h16V9zM4 15h16v-2H4v2zM4 5v2h16V5H4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_supervisor_account_24px"><path d="M16.5 12c1.38 0 2.49-1.12 2.49-2.5S17.88 7 16.5 7a2.5 2.5 0 0 0 0 5zM9 11c1.66 0 2.99-1.34 2.99-3S10.66 5 9 5C7.34 5 6 6.34 6 8s1.34 3 3 3zm7.5 3c-1.83 0-5.5.92-5.5 2.75V19h11v-2.25c0-1.83-3.67-2.75-5.5-2.75zM9 13c-2.33 0-7 1.17-7 3.5V19h7v-2.25c0-.85.33-2.34 2.37-3.47C10.5 13.1 9.66 13 9 13z"/></symbol><symbol viewBox="0 0 24 24" id="ic_swap_horiz_24px"><path d="M6.99 11L3 15l3.99 4v-3H14v-2H6.99v-3zM21 9l-3.99-4v3H10v2h7.01v3L21 9z"/></symbol><symbol viewBox="0 0 24 24" id="ic_swap_vert_24px"><path d="M16 17.01V10h-2v7.01h-3L15 21l4-3.99h-3zM9 3L5 6.99h3V14h2V6.99h3L9 3z"/></symbol><symbol viewBox="0 0 24 24" id="ic_swap_vertical_circle_24px"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zM6.5 9L10 5.5 13.5 9H11v4H9V9H6.5zm11 6L14 18.5 10.5 15H13v-4h2v4h2.5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_system_update_alt_24px"><path d="M12 16.5l4-4h-3v-9h-2v9H8l4 4zm9-13h-6v1.99h6v14.03H3V5.49h6V3.5H3c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2v-14c0-1.1-.9-2-2-2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_tab_24px"><path d="M21 3H3c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H3V5h10v4h8v10z"/></symbol><symbol viewBox="0 0 24 24" id="ic_tab_unselected_24px"><path d="M1 9h2V7H1v2zm0 4h2v-2H1v2zm0-8h2V3c-1.1 0-2 .9-2 2zm8 16h2v-2H9v2zm-8-4h2v-2H1v2zm2 4v-2H1c0 1.1.9 2 2 2zM21 3h-8v6h10V5c0-1.1-.9-2-2-2zm0 14h2v-2h-2v2zM9 5h2V3H9v2zM5 21h2v-2H5v2zM5 5h2V3H5v2zm16 16c1.1 0 2-.9 2-2h-2v2zm0-8h2v-2h-2v2zm-8 8h2v-2h-2v2zm4 0h2v-2h-2v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_theaters_24px"><path d="M18 3v2h-2V3H8v2H6V3H4v18h2v-2h2v2h8v-2h2v2h2V3h-2zM8 17H6v-2h2v2zm0-4H6v-2h2v2zm0-4H6V7h2v2zm10 8h-2v-2h2v2zm0-4h-2v-2h2v2zm0-4h-2V7h2v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_thumb_down_24px"><path d="M15 3H6c-.83 0-1.54.5-1.84 1.22l-3.02 7.05c-.09.23-.14.47-.14.73v1.91l.01.01L1 14c0 1.1.9 2 2 2h6.31l-.95 4.57-.03.32c0 .41.17.79.44 1.06L9.83 23l6.59-6.59c.36-.36.58-.86.58-1.41V5c0-1.1-.9-2-2-2zm4 0v12h4V3h-4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_thumb_up_24px"><path d="M1 21h4V9H1v12zm22-11c0-1.1-.9-2-2-2h-6.31l.95-4.57.03-.32c0-.41-.17-.79-.44-1.06L14.17 1 7.59 7.59C7.22 7.95 7 8.45 7 9v10c0 1.1.9 2 2 2h9c.83 0 1.54-.5 1.84-1.22l3.02-7.05c.09-.23.14-.47.14-.73v-1.91l-.01-.01L23 10z"/></symbol><symbol viewBox="0 0 24 24" id="ic_thumbs_up_down_24px"><path d="M12 6c0-.55-.45-1-1-1H5.82l.66-3.18.02-.23c0-.31-.13-.59-.33-.8L5.38 0 .44 4.94C.17 5.21 0 5.59 0 6v6.5c0 .83.67 1.5 1.5 1.5h6.75c.62 0 1.15-.38 1.38-.91l2.26-5.29c.07-.17.11-.36.11-.55V6zm10.5 4h-6.75c-.62 0-1.15.38-1.38.91l-2.26 5.29c-.07.17-.11.36-.11.55V18c0 .55.45 1 1 1h5.18l-.66 3.18-.02.24c0 .31.13.59.33.8l.79.78 4.94-4.94c.27-.27.44-.65.44-1.06v-6.5c0-.83-.67-1.5-1.5-1.5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_timeline_24px"><path d="M23 8c0 1.1-.9 2-2 2a1.7 1.7 0 0 1-.51-.07l-3.56 3.55c.05.16.07.34.07.52 0 1.1-.9 2-2 2s-2-.9-2-2c0-.18.02-.36.07-.52l-2.55-2.55c-.16.05-.34.07-.52.07s-.36-.02-.52-.07l-4.55 4.56c.05.16.07.33.07.51 0 1.1-.9 2-2 2s-2-.9-2-2 .9-2 2-2c.18 0 .35.02.51.07l4.56-4.55C8.02 9.36 8 9.18 8 9c0-1.1.9-2 2-2s2 .9 2 2c0 .18-.02.36-.07.52l2.55 2.55c.16-.05.34-.07.52-.07s.36.02.52.07l3.55-3.56A1.7 1.7 0 0 1 19 8c0-1.1.9-2 2-2s2 .9 2 2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_toc_24px"><path d="M3 9h14V7H3v2zm0 4h14v-2H3v2zm0 4h14v-2H3v2zm16 0h2v-2h-2v2zm0-10v2h2V7h-2zm0 6h2v-2h-2v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_today_24px"><path d="M19 3h-1V1h-2v2H8V1H6v2H5c-1.11 0-1.99.9-1.99 2L3 19a2 2 0 0 0 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v11zM7 10h5v5H7z"/></symbol><symbol viewBox="0 0 24 24" id="ic_toll_24px"><path d="M15 4c-4.42 0-8 3.58-8 8s3.58 8 8 8 8-3.58 8-8-3.58-8-8-8zm0 14c-3.31 0-6-2.69-6-6s2.69-6 6-6 6 2.69 6 6-2.69 6-6 6zM3 12a5.99 5.99 0 0 1 4-5.65V4.26C3.55 5.15 1 8.27 1 12s2.55 6.85 6 7.74v-2.09A5.99 5.99 0 0 1 3 12z"/></symbol><symbol viewBox="0 0 24 24" id="ic_touch_app_24px"><path d="M9 11.24V7.5a2.5 2.5 0 0 1 5 0v3.74c1.21-.81 2-2.18 2-3.74C16 5.01 13.99 3 11.5 3S7 5.01 7 7.5c0 1.56.79 2.93 2 3.74zm9.84 4.63l-4.54-2.26c-.17-.07-.35-.11-.54-.11H13v-6c0-.83-.67-1.5-1.5-1.5S10 6.67 10 7.5v10.74l-3.43-.72c-.08-.01-.15-.03-.24-.03-.31 0-.59.13-.79.33l-.79.8 4.94 4.94c.27.27.65.44 1.06.44h6.79c.75 0 1.33-.55 1.44-1.28l.75-5.27c.01-.07.02-.14.02-.2 0-.62-.38-1.16-.91-1.38z"/></symbol><symbol viewBox="0 0 24 24" id="ic_track_changes_24px"><path d="M19.07 4.93l-1.41 1.41A8.014 8.014 0 0 1 20 12c0 4.42-3.58 8-8 8s-8-3.58-8-8c0-4.08 3.05-7.44 7-7.93v2.02C8.16 6.57 6 9.03 6 12c0 3.31 2.69 6 6 6s6-2.69 6-6c0-1.66-.67-3.16-1.76-4.24l-1.41 1.41C15.55 9.9 16 10.9 16 12c0 2.21-1.79 4-4 4s-4-1.79-4-4c0-1.86 1.28-3.41 3-3.86v2.14c-.6.35-1 .98-1 1.72 0 1.1.9 2 2 2s2-.9 2-2c0-.74-.4-1.38-1-1.72V2h-1C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10a9.97 9.97 0 0 0-2.93-7.07z"/></symbol><symbol viewBox="0 0 24 24" id="ic_translate_24px"><path d="M12.87 15.07l-2.54-2.51.03-.03A17.52 17.52 0 0 0 14.07 6H17V4h-7V2H8v2H1v1.99h11.17C11.5 7.92 10.44 9.75 9 11.35 8.07 10.32 7.3 9.19 6.69 8h-2c.73 1.63 1.73 3.17 2.98 4.56l-5.09 5.02L4 19l5-5 3.11 3.11.76-2.04zM18.5 10h-2L12 22h2l1.12-3h4.75L21 22h2l-4.5-12zm-2.62 7l1.62-4.33L19.12 17h-3.24z"/></symbol><symbol viewBox="0 0 24 24" id="ic_trending_down_24px"><path d="M16 18l2.29-2.29-4.88-4.88-4 4L2 7.41 3.41 6l6 6 4-4 6.3 6.29L22 12v6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_trending_flat_24px"><path d="M22 12l-4-4v3H3v2h15v3z"/></symbol><symbol viewBox="0 0 24 24" id="ic_trending_up_24px"><path d="M16 6l2.29 2.29-4.88 4.88-4-4L2 16.59 3.41 18l6-6 4 4 6.3-6.29L22 12V6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_turned_in_24px"><path d="M17 3H7c-1.1 0-1.99.9-1.99 2L5 21l7-3 7 3V5c0-1.1-.9-2-2-2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_turned_in_not_24px"><path d="M17 3H7c-1.1 0-1.99.9-1.99 2L5 21l7-3 7 3V5c0-1.1-.9-2-2-2zm0 15l-5-2.18L7 18V5h10v13z"/></symbol><symbol viewBox="0 0 24 24" id="ic_update_24px"><path d="M21 10.12h-6.78l2.74-2.82c-2.73-2.7-7.15-2.8-9.88-.1a6.875 6.875 0 0 0 0 9.79 7.02 7.02 0 0 0 9.88 0C18.32 15.65 19 14.08 19 12.1h2c0 1.98-.88 4.55-2.64 6.29-3.51 3.48-9.21 3.48-12.72 0-3.5-3.47-3.53-9.11-.02-12.58a8.987 8.987 0 0 1 12.65 0L21 3v7.12zM12.5 8v4.25l3.5 2.08-.72 1.21L11 13V8h1.5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_verified_user_24px"><path d="M12 1L3 5v6c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V5l-9-4zm-2 16l-4-4 1.41-1.41L10 14.17l6.59-6.59L18 9l-8 8z"/></symbol><symbol viewBox="0 0 24 24" id="ic_view_agenda_24px"><path d="M20 13H3c-.55 0-1 .45-1 1v6c0 .55.45 1 1 1h17c.55 0 1-.45 1-1v-6c0-.55-.45-1-1-1zm0-10H3c-.55 0-1 .45-1 1v6c0 .55.45 1 1 1h17c.55 0 1-.45 1-1V4c0-.55-.45-1-1-1z"/></symbol><symbol viewBox="0 0 24 24" id="ic_view_array_24px"><path d="M4 18h3V5H4v13zM18 5v13h3V5h-3zM8 18h9V5H8v13z"/></symbol><symbol viewBox="0 0 24 24" id="ic_view_carousel_24px"><path d="M7 19h10V4H7v15zm-5-2h4V6H2v11zM18 6v11h4V6h-4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_view_column_24px"><path d="M10 18h5V5h-5v13zm-6 0h5V5H4v13zM16 5v13h5V5h-5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_view_day_24px"><path d="M2 21h19v-3H2v3zM20 8H3c-.55 0-1 .45-1 1v6c0 .55.45 1 1 1h17c.55 0 1-.45 1-1V9c0-.55-.45-1-1-1zM2 3v3h19V3H2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_view_headline_24px"><path d="M4 15h16v-2H4v2zm0 4h16v-2H4v2zm0-8h16V9H4v2zm0-6v2h16V5H4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_view_list_24px"><path d="M4 14h4v-4H4v4zm0 5h4v-4H4v4zM4 9h4V5H4v4zm5 5h12v-4H9v4zm0 5h12v-4H9v4zM9 5v4h12V5H9z"/></symbol><symbol viewBox="0 0 24 24" id="ic_view_module_24px"><path d="M4 11h5V5H4v6zm0 7h5v-6H4v6zm6 0h5v-6h-5v6zm6 0h5v-6h-5v6zm-6-7h5V5h-5v6zm6-6v6h5V5h-5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_view_quilt_24px"><path d="M10 18h5v-6h-5v6zm-6 0h5V5H4v13zm12 0h5v-6h-5v6zM10 5v6h11V5H10z"/></symbol><symbol viewBox="0 0 24 24" id="ic_view_stream_24px"><path d="M4 18h17v-6H4v6zM4 5v6h17V5H4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_view_week_24px"><path d="M6 5H3c-.55 0-1 .45-1 1v12c0 .55.45 1 1 1h3c.55 0 1-.45 1-1V6c0-.55-.45-1-1-1zm14 0h-3c-.55 0-1 .45-1 1v12c0 .55.45 1 1 1h3c.55 0 1-.45 1-1V6c0-.55-.45-1-1-1zm-7 0h-3c-.55 0-1 .45-1 1v12c0 .55.45 1 1 1h3c.55 0 1-.45 1-1V6c0-.55-.45-1-1-1z"/></symbol><symbol viewBox="0 0 24 24" id="ic_visibility_24px"><path d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z"/></symbol><symbol viewBox="0 0 24 24" id="ic_visibility_off_24px"><path d="M12 7c2.76 0 5 2.24 5 5 0 .65-.13 1.26-.36 1.83l2.92 2.92c1.51-1.26 2.7-2.89 3.43-4.75-1.73-4.39-6-7.5-11-7.5-1.4 0-2.74.25-3.98.7l2.16 2.16C10.74 7.13 11.35 7 12 7zM2 4.27l2.28 2.28.46.46A11.804 11.804 0 0 0 1 12c1.73 4.39 6 7.5 11 7.5 1.55 0 3.03-.3 4.38-.84l.42.42L19.73 22 21 20.73 3.27 3 2 4.27zM7.53 9.8l1.55 1.55c-.05.21-.08.43-.08.65 0 1.66 1.34 3 3 3 .22 0 .44-.03.65-.08l1.55 1.55c-.67.33-1.41.53-2.2.53-2.76 0-5-2.24-5-5 0-.79.2-1.53.53-2.2zm4.31-.78l3.15 3.15.02-.16c0-1.66-1.34-3-3-3l-.17.01z"/></symbol><symbol viewBox="0 0 24 24" id="ic_watch_later_24px"><path d="M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10 10-4.5 10-10S17.5 2 12 2zm4.2 14.2L11 13V7h1.5v5.2l4.5 2.7-.8 1.3z"/></symbol><symbol viewBox="0 0 24 24" id="ic_work_24px"><path d="M20 6h-4V4c0-1.11-.89-2-2-2h-4c-1.11 0-2 .89-2 2v2H4c-1.11 0-1.99.89-1.99 2L2 19c0 1.11.89 2 2 2h16c1.11 0 2-.89 2-2V8c0-1.11-.89-2-2-2zm-6 0h-4V4h4v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_youtube_searched_for_24px"><path d="M17.01 14h-.8l-.27-.27a6.45 6.45 0 0 0 1.57-4.23c0-3.59-2.91-6.5-6.5-6.5s-6.5 3-6.5 6.5H2l3.84 4 4.16-4H6.51a4.5 4.5 0 0 1 9 0 4.507 4.507 0 0 1-6.32 4.12L7.71 15.1a6.474 6.474 0 0 0 7.52-.67l.27.27v.79l5.01 4.99L22 19l-4.99-5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_zoom_in_24px"><path d="M15.5 14h-.79l-.28-.27A6.47 6.47 0 0 0 16 9.5 6.5 6.5 0 1 0 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14zm2.5-4h-2v2H9v-2H7V9h2V7h1v2h2v1z"/></symbol><symbol viewBox="0 0 24 24" id="ic_zoom_out_24px"><path d="M15.5 14h-.79l-.28-.27A6.47 6.47 0 0 0 16 9.5 6.5 6.5 0 1 0 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14zM7 9h5v1H7z"/></symbol></svg>
\ 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 0000000..5c350e8
--- /dev/null
+++ b/skyquake/framework/style/icons/svg-sprite-alert-symbol.svg
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><symbol viewBox="0 0 24 24" id="ic_add_alert_24px"><path d="M10.01 21.01c0 1.1.89 1.99 1.99 1.99s1.99-.89 1.99-1.99h-3.98zm8.87-4.19V11c0-3.25-2.25-5.97-5.29-6.69v-.72C13.59 2.71 12.88 2 12 2s-1.59.71-1.59 1.59v.72A6.873 6.873 0 0 0 5.12 11v5.82L3 18.94V20h18v-1.06l-2.12-2.12zM16 13.01h-3v3h-2v-3H8V11h3V8h2v3h3v2.01z"/></symbol><symbol viewBox="0 0 24 24" id="ic_error_24px"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_error_outline_24px"><path d="M11 15h2v2h-2zm0-8h2v6h-2zm.99-5C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z"/></symbol><symbol viewBox="0 0 24 24" id="ic_warning_24px"><path d="M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z"/></symbol></svg>
\ 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 0000000..84aaa88
--- /dev/null
+++ b/skyquake/framework/style/icons/svg-sprite-av-symbol.svg
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><symbol viewBox="0 0 24 24" id="ic_add_to_queue_24px"><path d="M21 3H3c-1.11 0-2 .89-2 2v12a2 2 0 0 0 2 2h5v2h8v-2h5c1.1 0 1.99-.9 1.99-2L23 5a2 2 0 0 0-2-2zm0 14H3V5h18v12zm-5-7v2h-3v3h-2v-3H8v-2h3V7h2v3h3z"/></symbol><symbol viewBox="0 0 24 24" id="ic_airplay_24px"><path d="M6 22h12l-6-6zM21 3H3c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h4v-2H3V5h18v12h-4v2h4c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_album_24px"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 14.5c-2.49 0-4.5-2.01-4.5-4.5S9.51 7.5 12 7.5s4.5 2.01 4.5 4.5-2.01 4.5-4.5 4.5zm0-5.5c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1z"/></symbol><symbol viewBox="0 0 24 24" id="ic_art_track_24px"><path fill="#010101" d="M22 13h-8v-2h8v2zm0-6h-8v2h8V7zm-8 10h8v-2h-8v2zm-2-8v6c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V9c0-1.1.9-2 2-2h6c1.1 0 2 .9 2 2zm-1.5 6l-2.25-3-1.75 2.26-1.25-1.51L3.5 15h7z"/></symbol><symbol viewBox="0 0 24 24" id="ic_av_timer_24px"><path d="M11 17c0 .55.45 1 1 1s1-.45 1-1-.45-1-1-1-1 .45-1 1zm0-14v4h2V5.08c3.39.49 6 3.39 6 6.92 0 3.87-3.13 7-7 7s-7-3.13-7-7c0-1.68.59-3.22 1.58-4.42L12 13l1.41-1.41-6.8-6.8v.02C4.42 6.45 3 9.05 3 12c0 4.97 4.02 9 9 9a9 9 0 0 0 0-18h-1zm7 9c0-.55-.45-1-1-1s-1 .45-1 1 .45 1 1 1 1-.45 1-1zM6 12c0 .55.45 1 1 1s1-.45 1-1-.45-1-1-1-1 .45-1 1z"/></symbol><symbol viewBox="0 0 24 24" id="ic_branding_watermark_24px"><path d="M21 3H3c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16h-9v-6h9v6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_call_to_action_24px"><path d="M21 3H3c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H3v-3h18v3z"/></symbol><symbol viewBox="0 0 24 24" id="ic_closed_caption_24px"><path d="M19 4H5a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm-8 7H9.5v-.5h-2v3h2V13H11v1c0 .55-.45 1-1 1H7c-.55 0-1-.45-1-1v-4c0-.55.45-1 1-1h3c.55 0 1 .45 1 1v1zm7 0h-1.5v-.5h-2v3h2V13H18v1c0 .55-.45 1-1 1h-3c-.55 0-1-.45-1-1v-4c0-.55.45-1 1-1h3c.55 0 1 .45 1 1v1z"/></symbol><symbol viewBox="0 0 24 24" id="ic_equalizer_24px"><path d="M10 20h4V4h-4v16zm-6 0h4v-8H4v8zM16 9v11h4V9h-4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_explicit_24px"><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-4 6h-4v2h4v2h-4v2h4v2H9V7h6v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_fast_forward_24px"><path d="M4 18l8.5-6L4 6v12zm9-12v12l8.5-6L13 6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_fast_rewind_24px"><path d="M11 18V6l-8.5 6 8.5 6zm.5-6l8.5 6V6l-8.5 6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_featured_play_list_24px"><path d="M21 3H3c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-9 8H3V9h9v2zm0-4H3V5h9v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_featured_video_24px"><path d="M21 3H3c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-9 9H3V5h9v7z"/></symbol><symbol viewBox="0 0 24 24" id="ic_fiber_dvr_24px"><path d="M17.5 10.5h2v1h-2zm-13 0h2v3h-2zM21 3H3c-1.11 0-2 .89-2 2v14a2 2 0 0 0 2 2h18a2 2 0 0 0 2-2V5c0-1.11-.89-2-2-2zM8 13.5c0 .85-.65 1.5-1.5 1.5H3V9h3.5c.85 0 1.5.65 1.5 1.5v3zm4.62 1.5h-1.5L9.37 9h1.5l1 3.43 1-3.43h1.5l-1.75 6zM21 11.5c0 .6-.4 1.15-.9 1.4L21 15h-1.5l-.85-2H17.5v2H16V9h3.5c.85 0 1.5.65 1.5 1.5v1z"/></symbol><symbol viewBox="0 0 24 24" id="ic_fiber_manual_record_24px"><circle fill="#010101" cx="12" cy="12" r="8"/></symbol><symbol viewBox="0 0 24 24" id="ic_fiber_new_24px"><path d="M20 4H4c-1.11 0-1.99.89-1.99 2L2 18c0 1.11.89 2 2 2h16c1.11 0 2-.89 2-2V6c0-1.11-.89-2-2-2zM8.5 15H7.3l-2.55-3.5V15H3.5V9h1.25l2.5 3.5V9H8.5v6zm5-4.74H11v1.12h2.5v1.26H11v1.11h2.5V15h-4V9h4v1.26zm7 3.74c0 .55-.45 1-1 1h-4c-.55 0-1-.45-1-1V9h1.25v4.51h1.13V9.99h1.25v3.51h1.12V9h1.25v5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_fiber_pin_24px"><path d="M5.5 10.5h2v1h-2zM20 4H4c-1.11 0-1.99.89-1.99 2L2 18c0 1.11.89 2 2 2h16c1.11 0 2-.89 2-2V6c0-1.11-.89-2-2-2zM9 11.5c0 .85-.65 1.5-1.5 1.5h-2v2H4V9h3.5c.85 0 1.5.65 1.5 1.5v1zm3.5 3.5H11V9h1.5v6zm7.5 0h-1.2l-2.55-3.5V15H15V9h1.25l2.5 3.5V9H20v6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_fiber_smart_record_24px"><g fill="#010101"><circle cx="9" cy="12" r="8"/><path d="M17 4.26v2.09a5.99 5.99 0 0 1 0 11.3v2.09c3.45-.89 6-4.01 6-7.74s-2.55-6.85-6-7.74z"/></g></symbol><symbol viewBox="0 0 24 24" id="ic_forward_10_24px"><path d="M4 13c0 4.4 3.6 8 8 8s8-3.6 8-8h-2c0 3.3-2.7 6-6 6s-6-2.7-6-6 2.7-6 6-6v4l5-5-5-5v4c-4.4 0-8 3.6-8 8zm6.8 3H10v-3.3L9 13v-.7l1.8-.6h.1V16zm4.3-1.8c0 .3 0 .6-.1.8l-.3.6s-.3.3-.5.3-.4.1-.6.1-.4 0-.6-.1-.3-.2-.5-.3-.2-.3-.3-.6-.1-.5-.1-.8v-.7c0-.3 0-.6.1-.8l.3-.6s.3-.3.5-.3.4-.1.6-.1.4 0 .6.1.3.2.5.3.2.3.3.6.1.5.1.8v.7zm-.8-.8v-.5s-.1-.2-.1-.3-.1-.1-.2-.2-.2-.1-.3-.1-.2 0-.3.1l-.2.2s-.1.2-.1.3v2s.1.2.1.3.1.1.2.2.2.1.3.1.2 0 .3-.1l.2-.2s.1-.2.1-.3v-1.5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_forward_30_24px"><path d="M9.6 13.5h.4c.2 0 .4-.1.5-.2s.2-.2.2-.4v-.2s-.1-.1-.1-.2-.1-.1-.2-.1h-.5s-.1.1-.2.1-.1.1-.1.2v.2h-1c0-.2 0-.3.1-.5s.2-.3.3-.4.3-.2.4-.2.4-.1.5-.1c.2 0 .4 0 .6.1s.3.1.5.2.2.2.3.4.1.3.1.5v.3s-.1.2-.1.3-.1.2-.2.2-.2.1-.3.2c.2.1.4.2.5.4s.2.4.2.6c0 .2 0 .4-.1.5s-.2.3-.3.4-.3.2-.5.2-.4.1-.6.1c-.2 0-.4 0-.5-.1s-.3-.1-.5-.2-.2-.2-.3-.4-.1-.4-.1-.6h.8v.2s.1.1.1.2.1.1.2.1h.5s.1-.1.2-.1.1-.1.1-.2v-.5s-.1-.1-.1-.2-.1-.1-.2-.1h-.6v-.7zm5.7.7c0 .3 0 .6-.1.8l-.3.6s-.3.3-.5.3-.4.1-.6.1-.4 0-.6-.1-.3-.2-.5-.3-.2-.3-.3-.6-.1-.5-.1-.8v-.7c0-.3 0-.6.1-.8l.3-.6s.3-.3.5-.3.4-.1.6-.1.4 0 .6.1.3.2.5.3.2.3.3.6.1.5.1.8v.7zm-.9-.8v-.5s-.1-.2-.1-.3-.1-.1-.2-.2-.2-.1-.3-.1-.2 0-.3.1l-.2.2s-.1.2-.1.3v2s.1.2.1.3.1.1.2.2.2.1.3.1.2 0 .3-.1l.2-.2s.1-.2.1-.3v-1.5zM4 13c0 4.4 3.6 8 8 8s8-3.6 8-8h-2c0 3.3-2.7 6-6 6s-6-2.7-6-6 2.7-6 6-6v4l5-5-5-5v4c-4.4 0-8 3.6-8 8z"/></symbol><symbol viewBox="0 0 24 24" id="ic_forward_5_24px"><path d="M4 13c0 4.4 3.6 8 8 8s8-3.6 8-8h-2c0 3.3-2.7 6-6 6s-6-2.7-6-6 2.7-6 6-6v4l5-5-5-5v4c-4.4 0-8 3.6-8 8zm6.7.9l.2-2.2h2.4v.7h-1.7l-.1.9s.1 0 .1-.1.1 0 .1-.1.1 0 .2 0h.2c.2 0 .4 0 .5.1s.3.2.4.3.2.3.3.5.1.4.1.6c0 .2 0 .4-.1.5s-.1.3-.3.5-.3.2-.5.3-.4.1-.6.1c-.2 0-.4 0-.5-.1s-.3-.1-.5-.2-.2-.2-.3-.4-.1-.3-.1-.5h.8c0 .2.1.3.2.4s.2.1.4.1c.1 0 .2 0 .3-.1l.2-.2s.1-.2.1-.3v-.6l-.1-.2-.2-.2s-.2-.1-.3-.1h-.2s-.1 0-.2.1-.1 0-.1.1-.1.1-.1.1h-.6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_games_24px"><path d="M15 7.5V2H9v5.5l3 3 3-3zM7.5 9H2v6h5.5l3-3-3-3zM9 16.5V22h6v-5.5l-3-3-3 3zM16.5 9l-3 3 3 3H22V9h-5.5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_hd_24px"><path d="M19 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-8 12H9.5v-2h-2v2H6V9h1.5v2.5h2V9H11v6zm2-6h4c.55 0 1 .45 1 1v4c0 .55-.45 1-1 1h-4V9zm1.5 4.5h2v-3h-2v3z"/></symbol><symbol viewBox="0 0 24 24" id="ic_hearing_24px"><path d="M17 20c-.29 0-.56-.06-.76-.15-.71-.37-1.21-.88-1.71-2.38-.51-1.56-1.47-2.29-2.39-3-.79-.61-1.61-1.24-2.32-2.53C9.29 10.98 9 9.93 9 9c0-2.8 2.2-5 5-5s5 2.2 5 5h2c0-3.93-3.07-7-7-7S7 5.07 7 9c0 1.26.38 2.65 1.07 3.9.91 1.65 1.98 2.48 2.85 3.15.81.62 1.39 1.07 1.71 2.05.6 1.82 1.37 2.84 2.73 3.55A4 4 0 0 0 21 18h-2c0 1.1-.9 2-2 2zM7.64 2.64L6.22 1.22C4.23 3.21 3 5.96 3 9s1.23 5.79 3.22 7.78l1.41-1.41C6.01 13.74 5 11.49 5 9s1.01-4.74 2.64-6.36zM11.5 9a2.5 2.5 0 0 0 5 0 2.5 2.5 0 0 0-5 0z"/></symbol><symbol viewBox="0 0 24 24" id="ic_high_quality_24px"><path d="M19 4H5a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm-8 11H9.5v-2h-2v2H6V9h1.5v2.5h2V9H11v6zm7-1c0 .55-.45 1-1 1h-.75v1.5h-1.5V15H14c-.55 0-1-.45-1-1v-4c0-.55.45-1 1-1h3c.55 0 1 .45 1 1v4zm-3.5-.5h2v-3h-2v3z"/></symbol><symbol viewBox="0 0 24 24" id="ic_library_add_24px"><path d="M4 6H2v14c0 1.1.9 2 2 2h14v-2H4V6zm16-4H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-1 9h-4v4h-2v-4H9V9h4V5h2v4h4v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_library_books_24px"><path d="M4 6H2v14c0 1.1.9 2 2 2h14v-2H4V6zm16-4H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-1 9H9V9h10v2zm-4 4H9v-2h6v2zm4-8H9V5h10v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_library_music_24px"><path d="M20 2H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-2 5h-3v5.5a2.5 2.5 0 0 1-5 0 2.5 2.5 0 0 1 2.5-2.5c.57 0 1.08.19 1.5.51V5h4v2zM4 6H2v14c0 1.1.9 2 2 2h14v-2H4V6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_loop_24px"><path d="M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6 0 1.01-.25 1.97-.7 2.8l1.46 1.46A7.93 7.93 0 0 0 20 12c0-4.42-3.58-8-8-8zm0 14c-3.31 0-6-2.69-6-6 0-1.01.25-1.97.7-2.8L5.24 7.74A7.93 7.93 0 0 0 4 12c0 4.42 3.58 8 8 8v3l4-4-4-4v3z"/></symbol><symbol viewBox="0 0 24 24" id="ic_mic_24px"><path d="M12 14c1.66 0 2.99-1.34 2.99-3L15 5c0-1.66-1.34-3-3-3S9 3.34 9 5v6c0 1.66 1.34 3 3 3zm5.3-3c0 3-2.54 5.1-5.3 5.1S6.7 14 6.7 11H5c0 3.41 2.72 6.23 6 6.72V21h2v-3.28c3.28-.48 6-3.3 6-6.72h-1.7z"/></symbol><symbol viewBox="0 0 24 24" id="ic_mic_none_24px"><path d="M12 14c1.66 0 2.99-1.34 2.99-3L15 5c0-1.66-1.34-3-3-3S9 3.34 9 5v6c0 1.66 1.34 3 3 3zm-1.2-9.1c0-.66.54-1.2 1.2-1.2.66 0 1.2.54 1.2 1.2l-.01 6.2c0 .66-.53 1.2-1.19 1.2-.66 0-1.2-.54-1.2-1.2V4.9zm6.5 6.1c0 3-2.54 5.1-5.3 5.1S6.7 14 6.7 11H5c0 3.41 2.72 6.23 6 6.72V21h2v-3.28c3.28-.48 6-3.3 6-6.72h-1.7z"/></symbol><symbol viewBox="0 0 24 24" id="ic_mic_off_24px"><path d="M19 11h-1.7c0 .74-.16 1.43-.43 2.05l1.23 1.23c.56-.98.9-2.09.9-3.28zm-4.02.17c0-.06.02-.11.02-.17V5c0-1.66-1.34-3-3-3S9 3.34 9 5v.18l5.98 5.99zM4.27 3L3 4.27l6.01 6.01V11c0 1.66 1.33 3 2.99 3 .22 0 .44-.03.65-.08l1.66 1.66c-.71.33-1.5.52-2.31.52-2.76 0-5.3-2.1-5.3-5.1H5c0 3.41 2.72 6.23 6 6.72V21h2v-3.28c.91-.13 1.77-.45 2.54-.9L19.73 21 21 19.73 4.27 3z"/></symbol><symbol viewBox="0 0 24 24" id="ic_movie_24px"><path d="M18 4l2 4h-3l-2-4h-2l2 4h-3l-2-4H8l2 4H7L5 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V4h-4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_music_video_24px"><path d="M21 3H3c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H3V5h18v14zM8 15c0-1.66 1.34-3 3-3 .35 0 .69.07 1 .18V6h5v2h-3v7.03A3.003 3.003 0 0 1 11 18c-1.66 0-3-1.34-3-3z"/></symbol><symbol viewBox="0 0 24 24" id="ic_new_releases_24px"><path d="M23 12l-2.44-2.78.34-3.68-3.61-.82-1.89-3.18L12 3 8.6 1.54 6.71 4.72l-3.61.81.34 3.68L1 12l2.44 2.78-.34 3.69 3.61.82 1.89 3.18L12 21l3.4 1.46 1.89-3.18 3.61-.82-.34-3.68L23 12zm-10 5h-2v-2h2v2zm0-4h-2V7h2v6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_not_interested_24px"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8 0-1.85.63-3.55 1.69-4.9L16.9 18.31A7.902 7.902 0 0 1 12 20zm6.31-3.1L7.1 5.69A7.902 7.902 0 0 1 12 4c4.42 0 8 3.58 8 8 0 1.85-.63 3.55-1.69 4.9z"/></symbol><symbol viewBox="0 0 24 24" id="ic_note_24px"><path d="M22 10l-6-6H4c-1.1 0-2 .9-2 2v12.01c0 1.1.9 1.99 2 1.99l16-.01c1.1 0 2-.89 2-1.99v-8zm-7-4.5l5.5 5.5H15V5.5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_pause_24px"><path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_pause_circle_filled_24px"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 14H9V8h2v8zm4 0h-2V8h2v8z"/></symbol><symbol viewBox="0 0 24 24" id="ic_pause_circle_outline_24px"><path d="M9 16h2V8H9v8zm3-14C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm1-4h2V8h-2v8z"/></symbol><symbol viewBox="0 0 24 24" id="ic_play_arrow_24px"><path d="M8 5v14l11-7z"/></symbol><symbol viewBox="0 0 24 24" id="ic_play_circle_filled_24px"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 14.5v-9l6 4.5-6 4.5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_play_circle_outline_24px"><path d="M10 16.5l6-4.5-6-4.5v9zM12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"/></symbol><symbol viewBox="0 0 24 24" id="ic_playlist_add_24px"><path d="M14 10H2v2h12v-2zm0-4H2v2h12V6zm4 8v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zM2 16h8v-2H2v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_playlist_add_check_24px"><path d="M14 10H2v2h12v-2zm0-4H2v2h12V6zM2 16h8v-2H2v2zm19.5-4.5L23 13l-6.99 7-4.51-4.5L13 14l3.01 3 5.49-5.5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_playlist_play_24px"><path d="M19 9H2v2h17V9zm0-4H2v2h17V5zM2 15h13v-2H2v2zm15-2v6l5-3-5-3z"/></symbol><symbol viewBox="0 0 24 24" id="ic_queue_24px"><path d="M4 6H2v14c0 1.1.9 2 2 2h14v-2H4V6zm16-4H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-1 9h-4v4h-2v-4H9V9h4V5h2v4h4v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_queue_music_24px"><path d="M15 6H3v2h12V6zm0 4H3v2h12v-2zM3 16h8v-2H3v2zM17 6v8.18c-.31-.11-.65-.18-1-.18-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3V8h3V6h-5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_queue_play_next_24px"><path d="M21 3H3c-1.11 0-2 .89-2 2v12a2 2 0 0 0 2 2h5v2h8v-2h2v-2H3V5h18v8h2V5a2 2 0 0 0-2-2zm-8 7V7h-2v3H8v2h3v3h2v-3h3v-2h-3zm11 8l-4.5 4.5L18 21l3-3-3-3 1.5-1.5L24 18z"/></symbol><symbol viewBox="0 0 24 24" id="ic_radio_24px"><path d="M3.24 6.15C2.51 6.43 2 7.17 2 8v12a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V8c0-1.11-.89-2-2-2H8.3l8.26-3.34L15.88 1 3.24 6.15zM7 20c-1.66 0-3-1.34-3-3s1.34-3 3-3 3 1.34 3 3-1.34 3-3 3zm13-8h-2v-2h-2v2H4V8h16v4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_recent_actors_24px"><path d="M21 5v14h2V5h-2zm-4 14h2V5h-2v14zM14 5H2c-.55 0-1 .45-1 1v12c0 .55.45 1 1 1h12c.55 0 1-.45 1-1V6c0-.55-.45-1-1-1zM8 7.75c1.24 0 2.25 1.01 2.25 2.25S9.24 12.25 8 12.25 5.75 11.24 5.75 10 6.76 7.75 8 7.75zM12.5 17h-9v-.75c0-1.5 3-2.25 4.5-2.25s4.5.75 4.5 2.25V17z"/></symbol><symbol viewBox="0 0 24 24" id="ic_remove_from_queue_24px"><path d="M21 3H3c-1.11 0-2 .89-2 2v12a2 2 0 0 0 2 2h5v2h8v-2h5c1.1 0 1.99-.9 1.99-2L23 5a2 2 0 0 0-2-2zm0 14H3V5h18v12zm-5-7v2H8v-2h8z"/></symbol><symbol viewBox="0 0 24 24" id="ic_repeat_24px"><path d="M7 7h10v3l4-4-4-4v3H5v6h2V7zm10 10H7v-3l-4 4 4 4v-3h12v-6h-2v4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_repeat_one_24px"><path d="M7 7h10v3l4-4-4-4v3H5v6h2V7zm10 10H7v-3l-4 4 4 4v-3h12v-6h-2v4zm-4-2V9h-1l-2 1v1h1.5v4H13z"/></symbol><symbol viewBox="0 0 24 24" id="ic_replay_10_24px"><path d="M12 5V1L7 6l5 5V7c3.3 0 6 2.7 6 6s-2.7 6-6 6-6-2.7-6-6H4c0 4.4 3.6 8 8 8s8-3.6 8-8-3.6-8-8-8zm-1.1 11H10v-3.3L9 13v-.7l1.8-.6h.1V16zm4.3-1.8c0 .3 0 .6-.1.8l-.3.6s-.3.3-.5.3-.4.1-.6.1-.4 0-.6-.1-.3-.2-.5-.3-.2-.3-.3-.6-.1-.5-.1-.8v-.7c0-.3 0-.6.1-.8l.3-.6s.3-.3.5-.3.4-.1.6-.1.4 0 .6.1c.2.1.3.2.5.3s.2.3.3.6.1.5.1.8v.7zm-.9-.8v-.5s-.1-.2-.1-.3-.1-.1-.2-.2-.2-.1-.3-.1-.2 0-.3.1l-.2.2s-.1.2-.1.3v2s.1.2.1.3.1.1.2.2.2.1.3.1.2 0 .3-.1l.2-.2s.1-.2.1-.3v-1.5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_replay_24px"><path d="M12 5V1L7 6l5 5V7c3.31 0 6 2.69 6 6s-2.69 6-6 6-6-2.69-6-6H4c0 4.42 3.58 8 8 8s8-3.58 8-8-3.58-8-8-8z"/></symbol><symbol viewBox="0 0 24 24" id="ic_replay_30_24px"><path d="M12 5V1L7 6l5 5V7c3.3 0 6 2.7 6 6s-2.7 6-6 6-6-2.7-6-6H4c0 4.4 3.6 8 8 8s8-3.6 8-8-3.6-8-8-8zm-2.4 8.5h.4c.2 0 .4-.1.5-.2s.2-.2.2-.4v-.2s-.1-.1-.1-.2-.1-.1-.2-.1h-.5s-.1.1-.2.1-.1.1-.1.2v.2h-1c0-.2 0-.3.1-.5s.2-.3.3-.4.3-.2.4-.2.4-.1.5-.1c.2 0 .4 0 .6.1s.3.1.5.2.2.2.3.4.1.3.1.5v.3s-.1.2-.1.3-.1.2-.2.2-.2.1-.3.2c.2.1.4.2.5.4s.2.4.2.6c0 .2 0 .4-.1.5s-.2.3-.3.4-.3.2-.5.2-.4.1-.6.1c-.2 0-.4 0-.5-.1s-.3-.1-.5-.2-.2-.2-.3-.4-.1-.4-.1-.6h.8v.2s.1.1.1.2.1.1.2.1h.5s.1-.1.2-.1.1-.1.1-.2v-.5s-.1-.1-.1-.2-.1-.1-.2-.1h-.6v-.7zm5.7.7c0 .3 0 .6-.1.8l-.3.6s-.3.3-.5.3-.4.1-.6.1-.4 0-.6-.1-.3-.2-.5-.3-.2-.3-.3-.6-.1-.5-.1-.8v-.7c0-.3 0-.6.1-.8l.3-.6s.3-.3.5-.3.4-.1.6-.1.4 0 .6.1.3.2.5.3.2.3.3.6.1.5.1.8v.7zm-.8-.8v-.5c0-.1-.1-.2-.1-.3s-.1-.1-.2-.2-.2-.1-.3-.1-.2 0-.3.1l-.2.2s-.1.2-.1.3v2s.1.2.1.3.1.1.2.2.2.1.3.1.2 0 .3-.1l.2-.2s.1-.2.1-.3v-1.5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_replay_5_24px"><path d="M12 5V1L7 6l5 5V7c3.3 0 6 2.7 6 6s-2.7 6-6 6-6-2.7-6-6H4c0 4.4 3.6 8 8 8s8-3.6 8-8-3.6-8-8-8zm-1.3 8.9l.2-2.2h2.4v.7h-1.7l-.1.9s.1 0 .1-.1.1 0 .1-.1.1 0 .2 0h.2c.2 0 .4 0 .5.1s.3.2.4.3.2.3.3.5.1.4.1.6c0 .2 0 .4-.1.5s-.1.3-.3.5-.3.2-.4.3-.4.1-.6.1c-.2 0-.4 0-.5-.1s-.3-.1-.5-.2-.2-.2-.3-.4-.1-.3-.1-.5h.8c0 .2.1.3.2.4s.2.1.4.1c.1 0 .2 0 .3-.1l.2-.2s.1-.2.1-.3v-.6l-.1-.2-.2-.2s-.2-.1-.3-.1h-.2s-.1 0-.2.1-.1 0-.1.1-.1.1-.1.1h-.7z"/></symbol><symbol viewBox="0 0 24 24" id="ic_shuffle_24px"><path d="M10.59 9.17L5.41 4 4 5.41l5.17 5.17 1.42-1.41zM14.5 4l2.04 2.04L4 18.59 5.41 20 17.96 7.46 20 9.5V4h-5.5zm.33 9.41l-1.41 1.41 3.13 3.13L14.5 20H20v-5.5l-2.04 2.04-3.13-3.13z"/></symbol><symbol viewBox="0 0 24 24" id="ic_skip_next_24px"><path d="M6 18l8.5-6L6 6v12zM16 6v12h2V6h-2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_skip_previous_24px"><path d="M6 6h2v12H6zm3.5 6l8.5 6V6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_slow_motion_video_24px"><path d="M13.05 9.79L10 7.5v9l3.05-2.29L16 12zm0 0L10 7.5v9l3.05-2.29L16 12zm0 0L10 7.5v9l3.05-2.29L16 12zM11 4.07V2.05c-2.01.2-3.84 1-5.32 2.21L7.1 5.69A7.94 7.94 0 0 1 11 4.07zM5.69 7.1L4.26 5.68A9.95 9.95 0 0 0 2.05 11h2.02a7.94 7.94 0 0 1 1.62-3.9zM4.07 13H2.05c.2 2.01 1 3.84 2.21 5.32l1.43-1.43A7.868 7.868 0 0 1 4.07 13zm1.61 6.74A9.98 9.98 0 0 0 11 21.95v-2.02a7.94 7.94 0 0 1-3.9-1.62l-1.42 1.43zM22 12c0 5.16-3.92 9.42-8.95 9.95v-2.02C16.97 19.41 20 16.05 20 12s-3.03-7.41-6.95-7.93V2.05C18.08 2.58 22 6.84 22 12z"/></symbol><symbol viewBox="0 0 24 24" id="ic_snooze_24px"><path d="M7.88 3.39L6.6 1.86 2 5.71l1.29 1.53 4.59-3.85zM22 5.72l-4.6-3.86-1.29 1.53 4.6 3.86L22 5.72zM12 4c-4.97 0-9 4.03-9 9s4.02 9 9 9a9 9 0 0 0 0-18zm0 16c-3.87 0-7-3.13-7-7s3.13-7 7-7 7 3.13 7 7-3.13 7-7 7zm-3-9h3.63L9 15.2V17h6v-2h-3.63L15 10.8V9H9v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_sort_by_alpha_24px"><path d="M14.94 4.66h-4.72l2.36-2.36zm-4.69 14.71h4.66l-2.33 2.33zM6.1 6.27L1.6 17.73h1.84l.92-2.45h5.11l.92 2.45h1.84L7.74 6.27H6.1zm-1.13 7.37l1.94-5.18 1.94 5.18H4.97zm10.76 2.5h6.12v1.59h-8.53v-1.29l5.92-8.56h-5.88v-1.6h8.3v1.26l-5.93 8.6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_stop_24px"><path d="M6 6h12v12H6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_subscriptions_24px"><path d="M20 8H4V6h16v2zm-2-6H6v2h12V2zm4 10v8c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2v-8c0-1.1.9-2 2-2h16c1.1 0 2 .9 2 2zm-6 4l-6-3.27v6.53L16 16z"/></symbol><symbol viewBox="0 0 24 24" id="ic_subtitles_24px"><path d="M20 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zM4 12h4v2H4v-2zm10 6H4v-2h10v2zm6 0h-4v-2h4v2zm0-4H10v-2h10v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_surround_sound_24px"><path d="M20 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zM7.76 16.24l-1.41 1.41A7.91 7.91 0 0 1 4 12c0-2.05.78-4.1 2.34-5.66l1.41 1.41C6.59 8.93 6 10.46 6 12s.59 3.07 1.76 4.24zM12 16c-2.21 0-4-1.79-4-4s1.79-4 4-4 4 1.79 4 4-1.79 4-4 4zm5.66 1.66l-1.41-1.41C17.41 15.07 18 13.54 18 12s-.59-3.07-1.76-4.24l1.41-1.41A7.91 7.91 0 0 1 20 12c0 2.05-.78 4.1-2.34 5.66zM12 10c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_video_call_24px"><path d="M17 10.5V7c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.55 0 1-.45 1-1v-3.5l4 4v-11l-4 4zM14 13h-3v3H9v-3H6v-2h3V8h2v3h3v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_video_label_24px"><path d="M21 3H3c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 13H3V5h18v11z"/></symbol><symbol viewBox="0 0 24 24" id="ic_video_library_24px"><path d="M4 6H2v14c0 1.1.9 2 2 2h14v-2H4V6zm16-4H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-8 12.5v-9l6 4.5-6 4.5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_videocam_24px"><path d="M17 10.5V7c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.55 0 1-.45 1-1v-3.5l4 4v-11l-4 4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_videocam_off_24px"><path d="M21 6.5l-4 4V7c0-.55-.45-1-1-1H9.82L21 17.18V6.5zM3.27 2L2 3.27 4.73 6H4c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.21 0 .39-.08.54-.18L19.73 21 21 19.73 3.27 2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_volume_down_24px"><path d="M18.5 12A4.5 4.5 0 0 0 16 7.97v8.05c1.48-.73 2.5-2.25 2.5-4.02zM5 9v6h4l5 5V4L9 9H5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_volume_mute_24px"><path d="M7 9v6h4l5 5V4l-5 5H7z"/></symbol><symbol viewBox="0 0 24 24" id="ic_volume_off_24px"><path d="M16.5 12A4.5 4.5 0 0 0 14 7.97v2.21l2.45 2.45c.03-.2.05-.41.05-.63zm2.5 0c0 .94-.2 1.82-.54 2.64l1.51 1.51A8.796 8.796 0 0 0 21 12c0-4.28-2.99-7.86-7-8.77v2.06c2.89.86 5 3.54 5 6.71zM4.27 3L3 4.27 7.73 9H3v6h4l5 5v-6.73l4.25 4.25c-.67.52-1.42.93-2.25 1.18v2.06a8.99 8.99 0 0 0 3.69-1.81L19.73 21 21 19.73l-9-9L4.27 3zM12 4L9.91 6.09 12 8.18V4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_volume_up_24px"><path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3A4.5 4.5 0 0 0 14 7.97v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z"/></symbol><symbol viewBox="0 0 24 24" id="ic_web_24px"><path d="M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm-5 14H4v-4h11v4zm0-5H4V9h11v4zm5 5h-4V9h4v9z"/></symbol><symbol viewBox="0 0 24 24" id="ic_web_asset_24px"><path fill="#010101" d="M19 4H5a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h14c1.1 0 2-.9 2-2V6a2 2 0 0 0-2-2zm0 14H5V8h14v10z"/></symbol></svg>
\ 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 0000000..7b0a620
--- /dev/null
+++ b/skyquake/framework/style/icons/svg-sprite-communication-symbol.svg
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><symbol viewBox="0 0 24 24" id="ic_business_24px"><path d="M12 7V3H2v18h20V7H12zM6 19H4v-2h2v2zm0-4H4v-2h2v2zm0-4H4V9h2v2zm0-4H4V5h2v2zm4 12H8v-2h2v2zm0-4H8v-2h2v2zm0-4H8V9h2v2zm0-4H8V5h2v2zm10 12h-8v-2h2v-2h-2v-2h2v-2h-2V9h8v10zm-2-8h-2v2h2v-2zm0 4h-2v2h2v-2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_call_24px"><path d="M6.62 10.79a15.15 15.15 0 0 0 6.59 6.59l2.2-2.2c.27-.27.67-.36 1.02-.24 1.12.37 2.33.57 3.57.57.55 0 1 .45 1 1V20c0 .55-.45 1-1 1-9.39 0-17-7.61-17-17 0-.55.45-1 1-1h3.5c.55 0 1 .45 1 1 0 1.25.2 2.45.57 3.57.11.35.03.74-.25 1.02l-2.2 2.2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_call_end_24px"><path d="M12 9c-1.6 0-3.15.25-4.6.72v3.1c0 .39-.23.74-.56.9-.98.49-1.87 1.12-2.66 1.85-.18.18-.43.28-.7.28-.28 0-.53-.11-.71-.29L.29 13.08a.956.956 0 0 1-.29-.7c0-.28.11-.53.29-.71C3.34 8.78 7.46 7 12 7s8.66 1.78 11.71 4.67c.18.18.29.43.29.71 0 .28-.11.53-.29.71l-2.48 2.48c-.18.18-.43.29-.71.29-.27 0-.52-.11-.7-.28a11.27 11.27 0 0 0-2.67-1.85.996.996 0 0 1-.56-.9v-3.1C15.15 9.25 13.6 9 12 9z"/></symbol><symbol viewBox="0 0 24 24" id="ic_call_made_24px"><path d="M9 5v2h6.59L4 18.59 5.41 20 17 8.41V15h2V5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_call_merge_24px"><path d="M17 20.41L18.41 19 15 15.59 13.59 17 17 20.41zM7.5 8H11v5.59L5.59 19 7 20.41l6-6V8h3.5L12 3.5 7.5 8z"/></symbol><symbol viewBox="0 0 24 24" id="ic_call_missed_24px"><path d="M19.59 7L12 14.59 6.41 9H11V7H3v8h2v-4.59l7 7 9-9z"/></symbol><symbol viewBox="0 0 24 24" id="ic_call_missed_outgoing_24px"><path d="M3 8.41l9 9 7-7V15h2V7h-8v2h4.59L12 14.59 4.41 7 3 8.41z"/></symbol><symbol viewBox="0 0 24 24" id="ic_call_received_24px"><path d="M20 5.41L18.59 4 7 15.59V9H5v10h10v-2H8.41z"/></symbol><symbol viewBox="0 0 24 24" id="ic_call_split_24px"><path d="M14 4l2.29 2.29-2.88 2.88 1.42 1.42 2.88-2.88L20 10V4zm-4 0H4v6l2.29-2.29 4.71 4.7V20h2v-8.41l-5.29-5.3z"/></symbol><symbol viewBox="0 0 24 24" id="ic_chat_24px"><path d="M20 2H4c-1.1 0-1.99.9-1.99 2L2 22l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zM6 9h12v2H6V9zm8 5H6v-2h8v2zm4-6H6V6h12v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_chat_bubble_24px"><path d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_chat_bubble_outline_24px"><path d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H6l-2 2V4h16v12z"/></symbol><symbol viewBox="0 0 24 24" id="ic_clear_all_24px"><path d="M5 13h14v-2H5v2zm-2 4h14v-2H3v2zM7 7v2h14V7H7z"/></symbol><symbol viewBox="0 0 24 24" id="ic_comment_24px"><path d="M21.99 4c0-1.1-.89-2-1.99-2H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h14l4 4-.01-18zM18 14H6v-2h12v2zm0-3H6V9h12v2zm0-3H6V6h12v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_contact_mail_24px"><path d="M21 8V7l-3 2-3-2v1l3 2 3-2zm1-5H2C.9 3 0 3.9 0 5v14c0 1.1.9 2 2 2h20c1.1 0 1.99-.9 1.99-2L24 5c0-1.1-.9-2-2-2zM8 6c1.66 0 3 1.34 3 3s-1.34 3-3 3-3-1.34-3-3 1.34-3 3-3zm6 12H2v-1c0-2 4-3.1 6-3.1s6 1.1 6 3.1v1zm8-6h-8V6h8v6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_contact_phone_24px"><path d="M22 3H2C.9 3 0 3.9 0 5v14c0 1.1.9 2 2 2h20c1.1 0 1.99-.9 1.99-2L24 5c0-1.1-.9-2-2-2zM8 6c1.66 0 3 1.34 3 3s-1.34 3-3 3-3-1.34-3-3 1.34-3 3-3zm6 12H2v-1c0-2 4-3.1 6-3.1s6 1.1 6 3.1v1zm3.85-4h1.64L21 16l-1.99 1.99A7.512 7.512 0 0 1 16.28 14c-.18-.64-.28-1.31-.28-2s.1-1.36.28-2a7.474 7.474 0 0 1 2.73-3.99L21 8l-1.51 2h-1.64c-.22.63-.35 1.3-.35 2s.13 1.37.35 2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_contacts_24px"><path d="M20 0H4v2h16V0zM4 24h16v-2H4v2zM20 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm-8 2.75c1.24 0 2.25 1.01 2.25 2.25s-1.01 2.25-2.25 2.25S9.75 10.24 9.75 9 10.76 6.75 12 6.75zM17 17H7v-1.5c0-1.67 3.33-2.5 5-2.5s5 .83 5 2.5V17z"/></symbol><symbol viewBox="0 0 24 24" id="ic_dialer_sip_24px"><path d="M17 3h-1v5h1V3zm-2 2h-2V4h2V3h-3v3h2v1h-2v1h3V5zm3-2v5h1V6h2V3h-3zm2 2h-1V4h1v1zm0 10.5c-1.25 0-2.45-.2-3.57-.57a.998.998 0 0 0-1.01.24l-2.2 2.2a15.045 15.045 0 0 1-6.59-6.59l2.2-2.21c.27-.26.35-.65.24-1A11.36 11.36 0 0 1 8.5 4c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1 0 9.39 7.61 17 17 17 .55 0 1-.45 1-1v-3.5c0-.55-.45-1-1-1z"/></symbol><symbol viewBox="0 0 24 24" id="ic_dialpad_24px"><path d="M12 19c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zM6 1c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm12-8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm-6 8c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm6 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0-6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm-6 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0-6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_email_24px"><path d="M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_forum_24px"><path d="M21 6h-2v9H6v2c0 .55.45 1 1 1h11l4 4V7c0-.55-.45-1-1-1zm-4 6V3c0-.55-.45-1-1-1H3c-.55 0-1 .45-1 1v14l4-4h10c.55 0 1-.45 1-1z"/></symbol><symbol viewBox="0 0 24 24" id="ic_import_contacts_24px"><path d="M21 5c-1.11-.35-2.33-.5-3.5-.5-1.95 0-4.05.4-5.5 1.5-1.45-1.1-3.55-1.5-5.5-1.5S2.45 4.9 1 6v14.65c0 .25.25.5.5.5.1 0 .15-.05.25-.05C3.1 20.45 5.05 20 6.5 20c1.95 0 4.05.4 5.5 1.5 1.35-.85 3.8-1.5 5.5-1.5 1.65 0 3.35.3 4.75 1.05.1.05.15.05.25.05.25 0 .5-.25.5-.5V6c-.6-.45-1.25-.75-2-1zm0 13.5c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5V8c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5v11.5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_import_export_24px"><path d="M9 3L5 6.99h3V14h2V6.99h3L9 3zm7 14.01V10h-2v7.01h-3L15 21l4-3.99h-3z"/></symbol><symbol viewBox="0 0 24 24" id="ic_invert_colors_off_24px"><path d="M20.65 20.87l-2.35-2.35-6.3-6.29-3.56-3.57-1.42-1.41L4.27 4.5 3 5.77l2.78 2.78a8.005 8.005 0 0 0 .56 10.69A7.98 7.98 0 0 0 12 21.58c1.79 0 3.57-.59 5.03-1.78l2.7 2.7L21 21.23l-.35-.36zM12 19.59c-1.6 0-3.11-.62-4.24-1.76A5.945 5.945 0 0 1 6 13.59c0-1.32.43-2.57 1.21-3.6L12 14.77v4.82zM12 5.1v4.58l7.25 7.26c1.37-2.96.84-6.57-1.6-9.01L12 2.27l-3.7 3.7 1.41 1.41L12 5.1z"/></symbol><symbol viewBox="0 0 24 24" id="ic_live_help_24px"><path d="M19 2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h4l3 3 3-3h4c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-6 16h-2v-2h2v2zm2.07-7.75l-.9.92C13.45 11.9 13 12.5 13 14h-2v-.5c0-1.1.45-2.1 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41 0-1.1-.9-2-2-2s-2 .9-2 2H8c0-2.21 1.79-4 4-4s4 1.79 4 4c0 .88-.36 1.68-.93 2.25z"/></symbol><symbol viewBox="0 0 24 24" id="ic_location_off_24px"><path d="M12 6.5A2.5 2.5 0 0 1 14.5 9c0 .74-.33 1.39-.83 1.85l3.63 3.63c.98-1.86 1.7-3.8 1.7-5.48 0-3.87-3.13-7-7-7a7 7 0 0 0-5.04 2.15l3.19 3.19c.46-.52 1.11-.84 1.85-.84zm4.37 9.6l-4.63-4.63-.11-.11L3.27 3 2 4.27l3.18 3.18C5.07 7.95 5 8.47 5 9c0 5.25 7 13 7 13s1.67-1.85 3.38-4.35L18.73 21 20 19.73l-3.63-3.63z"/></symbol><symbol viewBox="0 0 24 24" id="ic_location_on_24px"><path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5a2.5 2.5 0 0 1 0-5 2.5 2.5 0 0 1 0 5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_mail_outline_24px"><path d="M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 14H4V8l8 5 8-5v10zm-8-7L4 6h16l-8 5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_message_24px"><path d="M20 2H4c-1.1 0-1.99.9-1.99 2L2 22l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-2 12H6v-2h12v2zm0-3H6V9h12v2zm0-3H6V6h12v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_no_sim_24px"><path d="M18.99 5c0-1.1-.89-2-1.99-2h-7L7.66 5.34 19 16.68 18.99 5zM3.65 3.88L2.38 5.15 5 7.77V19c0 1.1.9 2 2 2h10.01c.35 0 .67-.1.96-.26l1.88 1.88 1.27-1.27L3.65 3.88z"/></symbol><symbol viewBox="0 0 24 24" id="ic_phone_24px"><path d="M6.62 10.79a15.15 15.15 0 0 0 6.59 6.59l2.2-2.2c.27-.27.67-.36 1.02-.24 1.12.37 2.33.57 3.57.57.55 0 1 .45 1 1V20c0 .55-.45 1-1 1-9.39 0-17-7.61-17-17 0-.55.45-1 1-1h3.5c.55 0 1 .45 1 1 0 1.25.2 2.45.57 3.57.11.35.03.74-.25 1.02l-2.2 2.2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_phonelink_erase_24px"><path d="M13 8.2l-1-1-4 4-4-4-1 1 4 4-4 4 1 1 4-4 4 4 1-1-4-4 4-4zM19 1H9c-1.1 0-2 .9-2 2v3h2V4h10v16H9v-2H7v3c0 1.1.9 2 2 2h10c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_phonelink_lock_24px"><path d="M19 1H9c-1.1 0-2 .9-2 2v3h2V4h10v16H9v-2H7v3c0 1.1.9 2 2 2h10c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2zm-8.2 10V9.5C10.8 8.1 9.4 7 8 7S5.2 8.1 5.2 9.5V11c-.6 0-1.2.6-1.2 1.2v3.5c0 .7.6 1.3 1.2 1.3h5.5c.7 0 1.3-.6 1.3-1.2v-3.5c0-.7-.6-1.3-1.2-1.3zm-1.3 0h-3V9.5c0-.8.7-1.3 1.5-1.3s1.5.5 1.5 1.3V11z"/></symbol><symbol viewBox="0 0 24 24" id="ic_phonelink_ring_24px"><path d="M20.1 7.7l-1 1c1.8 1.8 1.8 4.6 0 6.5l1 1c2.5-2.3 2.5-6.1 0-8.5zM18 9.8l-1 1c.5.7.5 1.6 0 2.3l1 1c1.2-1.2 1.2-3 0-4.3zM14 1H4c-1.1 0-2 .9-2 2v18c0 1.1.9 2 2 2h10c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2zm0 19H4V4h10v16z"/></symbol><symbol viewBox="0 0 24 24" id="ic_phonelink_setup_24px"><path d="M11.8 12.5v-1l1.1-.8c.1-.1.1-.2.1-.3l-1-1.7c-.1-.1-.2-.2-.3-.1l-1.3.4c-.3-.2-.6-.4-.9-.5l-.2-1.3c0-.1-.1-.2-.3-.2H7c-.1 0-.2.1-.3.2l-.2 1.3c-.3.1-.6.3-.9.5l-1.3-.5c-.1 0-.2 0-.3.1l-1 1.7c-.1.1 0 .2.1.3l1.1.8v1l-1.1.8c-.1.2-.1.3-.1.4l1 1.7c.1.1.2.2.3.1l1.4-.4c.3.2.6.4.9.5l.2 1.3c-.1.1.1.2.2.2h2c.1 0 .2-.1.3-.2l.2-1.3c.3-.1.6-.3.9-.5l1.3.5c.1 0 .2 0 .3-.1l1-1.7c.1-.1 0-.2-.1-.3l-1.1-.9zM8 14c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zM19 1H9c-1.1 0-2 .9-2 2v3h2V4h10v16H9v-2H7v3c0 1.1.9 2 2 2h10c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_portable_wifi_off_24px"><path d="M17.56 14.24c.28-.69.44-1.45.44-2.24 0-3.31-2.69-6-6-6-.79 0-1.55.16-2.24.44l1.62 1.62c.2-.03.41-.06.62-.06a4 4 0 0 1 3.95 4.63l1.61 1.61zM12 4c4.42 0 8 3.58 8 8 0 1.35-.35 2.62-.95 3.74l1.47 1.47A9.86 9.86 0 0 0 22 12c0-5.52-4.48-10-10-10-1.91 0-3.69.55-5.21 1.47l1.46 1.46A8.04 8.04 0 0 1 12 4zM3.27 2.5L2 3.77l2.1 2.1C2.79 7.57 2 9.69 2 12c0 3.7 2.01 6.92 4.99 8.65l1-1.73C5.61 17.53 4 14.96 4 12c0-1.76.57-3.38 1.53-4.69l1.43 1.44C6.36 9.68 6 10.8 6 12c0 2.22 1.21 4.15 3 5.19l1-1.74c-1.19-.7-2-1.97-2-3.45 0-.65.17-1.25.44-1.79l1.58 1.58L10 12c0 1.1.9 2 2 2l.21-.02.01.01 7.51 7.51L21 20.23 4.27 3.5l-1-1z"/></symbol><symbol viewBox="0 0 24 24" id="ic_present_to_all_24px"><path d="M21 3H3c-1.11 0-2 .89-2 2v14c0 1.11.89 2 2 2h18c1.11 0 2-.89 2-2V5c0-1.11-.89-2-2-2zm0 16.02H3V4.98h18v14.04zM10 12H8l4-4 4 4h-2v4h-4v-4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_ring_volume_24px"><path d="M23.71 16.67A16.97 16.97 0 0 0 12 12C7.46 12 3.34 13.78.29 16.67c-.18.18-.29.43-.29.71 0 .28.11.53.29.71l2.48 2.48c.18.18.43.29.71.29.27 0 .52-.11.7-.28.79-.74 1.69-1.36 2.66-1.85.33-.16.56-.5.56-.9v-3.1c1.45-.48 3-.73 4.6-.73s3.15.25 4.6.72v3.1c0 .39.23.74.56.9.98.49 1.87 1.12 2.66 1.85.18.18.43.28.7.28.28 0 .53-.11.71-.29l2.48-2.48c.18-.18.29-.43.29-.71a.99.99 0 0 0-.29-.7zM21.16 6.26l-1.41-1.41-3.56 3.55 1.41 1.41s3.45-3.52 3.56-3.55zM13 2h-2v5h2V2zM6.4 9.81L7.81 8.4 4.26 4.84 2.84 6.26c.11.03 3.56 3.55 3.56 3.55z"/></symbol><symbol viewBox="0 0 24 24" id="ic_rss_feed_24px"><circle cx="6.18" cy="17.82" r="2.18"/><path d="M4 4.44v2.83c7.03 0 12.73 5.7 12.73 12.73h2.83c0-8.59-6.97-15.56-15.56-15.56zm0 5.66v2.83c3.9 0 7.07 3.17 7.07 7.07h2.83c0-5.47-4.43-9.9-9.9-9.9z"/></symbol><symbol viewBox="0 0 24 24" id="ic_screen_share_24px"><path d="M20 18c1.1 0 1.99-.9 1.99-2L22 6a2 2 0 0 0-2-2H4c-1.11 0-2 .89-2 2v10a2 2 0 0 0 2 2H0v2h24v-2h-4zm-7-3.53v-2.19c-2.78 0-4.61.85-6 2.72.56-2.67 2.11-5.33 6-5.87V7l4 3.73-4 3.74z"/></symbol><symbol viewBox="0 0 24 24" id="ic_speaker_phone_24px"><path d="M7 7.07L8.43 8.5c.91-.91 2.18-1.48 3.57-1.48s2.66.57 3.57 1.48L17 7.07C15.72 5.79 13.95 5 12 5s-3.72.79-5 2.07zM12 1C8.98 1 6.24 2.23 4.25 4.21l1.41 1.41C7.28 4 9.53 3 12 3s4.72 1 6.34 2.62l1.41-1.41A10.963 10.963 0 0 0 12 1zm2.86 9.01L9.14 10C8.51 10 8 10.51 8 11.14v9.71c0 .63.51 1.14 1.14 1.14h5.71c.63 0 1.14-.51 1.14-1.14v-9.71c.01-.63-.5-1.13-1.13-1.13zM15 20H9v-8h6v8z"/></symbol><symbol viewBox="0 0 24 24" id="ic_stay_current_landscape_24px"><path d="M1.01 7L1 17c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2H3c-1.1 0-1.99.9-1.99 2zM19 7v10H5V7h14z"/></symbol><symbol viewBox="0 0 24 24" id="ic_stay_current_portrait_24px"><path d="M17 1.01L7 1c-1.1 0-1.99.9-1.99 2v18c0 1.1.89 2 1.99 2h10c1.1 0 2-.9 2-2V3c0-1.1-.9-1.99-2-1.99zM17 19H7V5h10v14z"/></symbol><symbol viewBox="0 0 24 24" id="ic_stay_primary_landscape_24px"><path d="M1.01 7L1 17c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2H3c-1.1 0-1.99.9-1.99 2zM19 7v10H5V7h14z"/></symbol><symbol viewBox="0 0 24 24" id="ic_stay_primary_portrait_24px"><path d="M17 1.01L7 1c-1.1 0-1.99.9-1.99 2v18c0 1.1.89 2 1.99 2h10c1.1 0 2-.9 2-2V3c0-1.1-.9-1.99-2-1.99zM17 19H7V5h10v14z"/></symbol><symbol viewBox="0 0 24 24" id="ic_stop_screen_share_24px"><path d="M21.22 18.02l2 2H24v-2h-2.78zm.77-2l.01-10a2 2 0 0 0-2-2H7.22l5.23 5.23c.18-.04.36-.07.55-.1V7.02l4 3.73-1.58 1.47 5.54 5.54c.61-.33 1.03-.99 1.03-1.74zM2.39 1.73L1.11 3l1.54 1.54c-.4.36-.65.89-.65 1.48v10a2 2 0 0 0 2 2H0v2h18.13l2.71 2.71 1.27-1.27L2.39 1.73zM7 15.02c.31-1.48.92-2.95 2.07-4.06l1.59 1.59c-1.54.38-2.7 1.18-3.66 2.47z"/></symbol><symbol viewBox="0 0 24 24" id="ic_swap_calls_24px"><path d="M18 4l-4 4h3v7c0 1.1-.9 2-2 2s-2-.9-2-2V8c0-2.21-1.79-4-4-4S5 5.79 5 8v7H2l4 4 4-4H7V8c0-1.1.9-2 2-2s2 .9 2 2v7c0 2.21 1.79 4 4 4s4-1.79 4-4V8h3l-4-4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_textsms_24px"><path d="M20 2H4c-1.1 0-1.99.9-1.99 2L2 22l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zM9 11H7V9h2v2zm4 0h-2V9h2v2zm4 0h-2V9h2v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_voicemail_24px"><path d="M18.5 6C15.46 6 13 8.46 13 11.5c0 1.33.47 2.55 1.26 3.5H9.74A5.45 5.45 0 0 0 11 11.5C11 8.46 8.54 6 5.5 6S0 8.46 0 11.5 2.46 17 5.5 17h13c3.04 0 5.5-2.46 5.5-5.5S21.54 6 18.5 6zm-13 9C3.57 15 2 13.43 2 11.5S3.57 8 5.5 8 9 9.57 9 11.5 7.43 15 5.5 15zm13 0c-1.93 0-3.5-1.57-3.5-3.5S16.57 8 18.5 8 22 9.57 22 11.5 20.43 15 18.5 15z"/></symbol><symbol viewBox="0 0 24 24" id="ic_vpn_key_24px"><path d="M12.65 10A5.99 5.99 0 0 0 7 6c-3.31 0-6 2.69-6 6s2.69 6 6 6a5.99 5.99 0 0 0 5.65-4H17v4h4v-4h2v-4H12.65zM7 14c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2z"/></symbol></svg>
\ 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 0000000..65dd1a9
--- /dev/null
+++ b/skyquake/framework/style/icons/svg-sprite-content-symbol.svg
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><symbol viewBox="0 0 24 24" id="ic_add_24px"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_add_box_24px"><path d="M19 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-2 10h-4v4h-2v-4H7v-2h4V7h2v4h4v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_add_circle_24px"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm5 11h-4v4h-2v-4H7v-2h4V7h2v4h4v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_add_circle_outline_24px"><path d="M13 7h-2v4H7v2h4v4h2v-4h4v-2h-4V7zm-1-5C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"/></symbol><symbol viewBox="0 0 24 24" id="ic_archive_24px"><path d="M20.54 5.23l-1.39-1.68A1.45 1.45 0 0 0 18 3H6c-.47 0-.88.21-1.16.55L3.46 5.23C3.17 5.57 3 6.02 3 6.5V19c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V6.5c0-.48-.17-.93-.46-1.27zM12 17.5L6.5 12H10v-2h4v2h3.5L12 17.5zM5.12 5l.81-1h12l.94 1H5.12z"/></symbol><symbol viewBox="0 0 24 24" id="ic_backspace_24px"><path d="M22 3H7c-.69 0-1.23.35-1.59.88L0 12l5.41 8.11c.36.53.9.89 1.59.89h15c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-3 12.59L17.59 17 14 13.41 10.41 17 9 15.59 12.59 12 9 8.41 10.41 7 14 10.59 17.59 7 19 8.41 15.41 12 19 15.59z"/></symbol><symbol viewBox="0 0 24 24" id="ic_block_24px"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zM4 12c0-4.42 3.58-8 8-8 1.85 0 3.55.63 4.9 1.69L5.69 16.9A7.902 7.902 0 0 1 4 12zm8 8c-1.85 0-3.55-.63-4.9-1.69L18.31 7.1A7.902 7.902 0 0 1 20 12c0 4.42-3.58 8-8 8z"/></symbol><symbol viewBox="0 0 24 24" id="ic_clear_24px"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></symbol><symbol viewBox="0 0 24 24" id="ic_content_copy_24px"><path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/></symbol><symbol viewBox="0 0 24 24" id="ic_content_cut_24px"><path d="M9.64 7.64c.23-.5.36-1.05.36-1.64 0-2.21-1.79-4-4-4S2 3.79 2 6s1.79 4 4 4c.59 0 1.14-.13 1.64-.36L10 12l-2.36 2.36C7.14 14.13 6.59 14 6 14c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4c0-.59-.13-1.14-.36-1.64L12 14l7 7h3v-1L9.64 7.64zM6 8a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm0 12a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm6-7.5c-.28 0-.5-.22-.5-.5s.22-.5.5-.5.5.22.5.5-.22.5-.5.5zM19 3l-6 6 2 2 7-7V3z"/></symbol><symbol viewBox="0 0 24 24" id="ic_content_paste_24px"><path d="M19 2h-4.18C14.4.84 13.3 0 12 0c-1.3 0-2.4.84-2.82 2H5c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-7 0c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zm7 18H5V4h2v3h10V4h2v16z"/></symbol><symbol viewBox="0 0 24 24" id="ic_create_24px"><path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04a.996.996 0 0 0 0-1.41l-2.34-2.34a.996.996 0 0 0-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"/></symbol><symbol viewBox="0 0 24 24" id="ic_delete_sweep_24px"><path d="M15 16h4v2h-4zm0-8h7v2h-7zm0 4h6v2h-6zM3 18c0 1.1.9 2 2 2h6c1.1 0 2-.9 2-2V8H3v10zM14 5h-3l-1-1H6L5 5H2v2h12z"/></symbol><symbol viewBox="0 0 24 24" id="ic_drafts_24px"><path d="M21.99 8c0-.72-.37-1.35-.94-1.7L12 1 2.95 6.3C2.38 6.65 2 7.28 2 8v10c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2l-.01-10zM12 13L3.74 7.84 12 3l8.26 4.84L12 13z"/></symbol><symbol viewBox="0 0 24 24" id="ic_filter_list_24px"><path d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_flag_24px"><path d="M14.4 6L14 4H5v17h2v-7h5.6l.4 2h7V6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_font_download_24px"><path d="M9.93 13.5h4.14L12 7.98zM20 2H4c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-4.05 16.5l-1.14-3H9.17l-1.12 3H5.96l5.11-13h1.86l5.11 13h-2.09z"/></symbol><symbol viewBox="0 0 24 24" id="ic_forward_24px"><path d="M12 8V4l8 8-8 8v-4H4V8z"/></symbol><symbol viewBox="0 0 24 24" id="ic_gesture_24px"><path d="M4.59 6.89c.7-.71 1.4-1.35 1.71-1.22.5.2 0 1.03-.3 1.52-.25.42-2.86 3.89-2.86 6.31 0 1.28.48 2.34 1.34 2.98.75.56 1.74.73 2.64.46 1.07-.31 1.95-1.4 3.06-2.77 1.21-1.49 2.83-3.44 4.08-3.44 1.63 0 1.65 1.01 1.76 1.79-3.78.64-5.38 3.67-5.38 5.37 0 1.7 1.44 3.09 3.21 3.09 1.63 0 4.29-1.33 4.69-6.1H21v-2.5h-2.47c-.15-1.65-1.09-4.2-4.03-4.2-2.25 0-4.18 1.91-4.94 2.84-.58.73-2.06 2.48-2.29 2.72-.25.3-.68.84-1.11.84-.45 0-.72-.83-.36-1.92.35-1.09 1.4-2.86 1.85-3.52.78-1.14 1.3-1.92 1.3-3.28C8.95 3.69 7.31 3 6.44 3 5.12 3 3.97 4 3.72 4.25c-.36.36-.66.66-.88.93l1.75 1.71zm9.29 11.66c-.31 0-.74-.26-.74-.72 0-.6.73-2.2 2.87-2.76-.3 2.69-1.43 3.48-2.13 3.48z"/></symbol><symbol viewBox="0 0 24 24" id="ic_inbox_24px"><path d="M19 3H4.99c-1.11 0-1.98.89-1.98 2L3 19c0 1.1.88 2 1.99 2H19c1.1 0 2-.9 2-2V5a2 2 0 0 0-2-2zm0 12h-4c0 1.66-1.35 3-3 3s-3-1.34-3-3H4.99V5H19v10z"/></symbol><symbol viewBox="0 0 24 24" id="ic_link_24px"><path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76 0 5-2.24 5-5s-2.24-5-5-5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_low_priority_24px"><path d="M14 5h8v2h-8zm0 5.5h8v2h-8zm0 5.5h8v2h-8zM2 11.5C2 15.08 4.92 18 8.5 18H9v2l3-3-3-3v2h-.5C6.02 16 4 13.98 4 11.5S6.02 7 8.5 7H12V5H8.5C4.92 5 2 7.92 2 11.5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_mail_24px"><path d="M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_markunread_24px"><path d="M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_move_to_inbox_24px"><path d="M19 3H4.99c-1.11 0-1.98.9-1.98 2L3 19c0 1.1.88 2 1.99 2H19c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 12h-4c0 1.66-1.35 3-3 3s-3-1.34-3-3H4.99V5H19v10zm-3-5h-2V7h-4v3H8l4 4 4-4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_next_week_24px"><path fill="#010101" d="M20 7h-4V5c0-.55-.22-1.05-.59-1.41C15.05 3.22 14.55 3 14 3h-4c-1.1 0-2 .9-2 2v2H4c-1.1 0-2 .9-2 2v11c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V9c0-1.1-.9-2-2-2zM10 5h4v2h-4V5zm1 13.5l-1-1 3-3-3-3 1-1 4 4-4 4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_redo_24px"><path d="M18.4 10.6C16.55 8.99 14.15 8 11.5 8c-4.65 0-8.58 3.03-9.96 7.22L3.9 16a8.002 8.002 0 0 1 7.6-5.5c1.95 0 3.73.72 5.12 1.88L13 16h9V7l-3.6 3.6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_remove_24px"><path d="M19 13H5v-2h14v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_remove_circle_24px"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm5 11H7v-2h10v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_remove_circle_outline_24px"><path d="M7 11v2h10v-2H7zm5-9C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"/></symbol><symbol viewBox="0 0 24 24" id="ic_reply_24px"><path d="M10 9V5l-7 7 7 7v-4.1c5 0 8.5 1.6 11 5.1-1-5-4-10-11-11z"/></symbol><symbol viewBox="0 0 24 24" id="ic_reply_all_24px"><path d="M7 8V5l-7 7 7 7v-3l-4-4 4-4zm6 1V5l-7 7 7 7v-4.1c5 0 8.5 1.6 11 5.1-1-5-4-10-11-11z"/></symbol><symbol viewBox="0 0 24 24" id="ic_report_24px"><path d="M15.73 3H8.27L3 8.27v7.46L8.27 21h7.46L21 15.73V8.27L15.73 3zM12 17.3c-.72 0-1.3-.58-1.3-1.3 0-.72.58-1.3 1.3-1.3.72 0 1.3.58 1.3 1.3 0 .72-.58 1.3-1.3 1.3zm1-4.3h-2V7h2v6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_save_24px"><path d="M17 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14c1.1 0 2-.9 2-2V7l-4-4zm-5 16c-1.66 0-3-1.34-3-3s1.34-3 3-3 3 1.34 3 3-1.34 3-3 3zm3-10H5V5h10v4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_select_all_24px"><path d="M3 5h2V3c-1.1 0-2 .9-2 2zm0 8h2v-2H3v2zm4 8h2v-2H7v2zM3 9h2V7H3v2zm10-6h-2v2h2V3zm6 0v2h2c0-1.1-.9-2-2-2zM5 21v-2H3c0 1.1.9 2 2 2zm-2-4h2v-2H3v2zM9 3H7v2h2V3zm2 18h2v-2h-2v2zm8-8h2v-2h-2v2zm0 8c1.1 0 2-.9 2-2h-2v2zm0-12h2V7h-2v2zm0 8h2v-2h-2v2zm-4 4h2v-2h-2v2zm0-16h2V3h-2v2zM7 17h10V7H7v10zm2-8h6v6H9V9z"/></symbol><symbol viewBox="0 0 24 24" id="ic_send_24px"><path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_sort_24px"><path d="M3 18h6v-2H3v2zM3 6v2h18V6H3zm0 7h12v-2H3v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_text_format_24px"><path d="M5 17v2h14v-2H5zm4.5-4.2h5l.9 2.2h2.1L12.75 4h-1.5L6.5 15h2.1l.9-2.2zM12 5.98L13.87 11h-3.74L12 5.98z"/></symbol><symbol viewBox="0 0 24 24" id="ic_unarchive_24px"><path d="M20.55 5.22l-1.39-1.68A1.51 1.51 0 0 0 18 3H6c-.47 0-.88.21-1.15.55L3.46 5.22C3.17 5.57 3 6.01 3 6.5V19a2 2 0 0 0 2 2h14c1.1 0 2-.9 2-2V6.5c0-.49-.17-.93-.45-1.28zM12 9.5l5.5 5.5H14v2h-4v-2H6.5L12 9.5zM5.12 5l.82-1h12l.93 1H5.12z"/></symbol><symbol viewBox="0 0 24 24" id="ic_undo_24px"><path d="M12.5 8c-2.65 0-5.05.99-6.9 2.6L2 7v9h9l-3.62-3.62c1.39-1.16 3.16-1.88 5.12-1.88 3.54 0 6.55 2.31 7.6 5.5l2.37-.78C21.08 11.03 17.15 8 12.5 8z"/></symbol><symbol viewBox="0 0 24 24" id="ic_weekend_24px"><path d="M21 10c-1.1 0-2 .9-2 2v3H5v-3c0-1.1-.9-2-2-2s-2 .9-2 2v5c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2v-5c0-1.1-.9-2-2-2zm-3-5H6c-1.1 0-2 .9-2 2v2.15c1.16.41 2 1.51 2 2.82V14h12v-2.03c0-1.3.84-2.4 2-2.82V7c0-1.1-.9-2-2-2z" fill="#010101"/></symbol></svg>
\ 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 0000000..d86f1fc
--- /dev/null
+++ b/skyquake/framework/style/icons/svg-sprite-device.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="26" height="2064" viewBox="0 0 26 2064"><svg width="24" height="24" viewBox="0 0 24 24" id="ic_access_alarm_24px" y="0"><path d="M22 5.72l-4.6-3.86-1.29 1.53 4.6 3.86L22 5.72zM7.88 3.39L6.6 1.86 2 5.71l1.29 1.53 4.59-3.85zM12.5 8H11v6l4.75 2.85.75-1.23-4-2.37V8zM12 4c-4.97 0-9 4.03-9 9s4.02 9 9 9c4.97 0 9-4.03 9-9s-4.03-9-9-9zm0 16c-3.87 0-7-3.13-7-7s3.13-7 7-7 7 3.13 7 7-3.13 7-7 7z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_access_alarms_24px" y="24"><path d="M22 5.7l-4.6-3.9-1.3 1.5 4.6 3.9L22 5.7zM7.9 3.4L6.6 1.9 2 5.7l1.3 1.5 4.6-3.8zM12.5 8H11v6l4.7 2.9.8-1.2-4-2.4V8zM12 4c-5 0-9 4-9 9s4 9 9 9 9-4 9-9-4-9-9-9zm0 16c-3.9 0-7-3.1-7-7s3.1-7 7-7 7 3.1 7 7-3.1 7-7 7z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_access_time_24px" y="48"><path fill-opacity=".9" d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm.5-13H11v6l5.25 3.15.75-1.23-4.5-2.67z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_add_alarm_24px" y="72"><path d="M7.88 3.39L6.6 1.86 2 5.71l1.29 1.53 4.59-3.85zM22 5.72l-4.6-3.86-1.29 1.53 4.6 3.86L22 5.72zM12 4c-4.97 0-9 4.03-9 9s4.02 9 9 9c4.97 0 9-4.03 9-9s-4.03-9-9-9zm0 16c-3.87 0-7-3.13-7-7s3.13-7 7-7 7 3.13 7 7-3.13 7-7 7zm1-11h-2v3H8v2h3v3h2v-3h3v-2h-3V9z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_airplanemode_off_24px" y="96"><path d="M13 9V3.5c0-.83-.67-1.5-1.5-1.5S10 2.67 10 3.5v3.68l7.83 7.83L21 16v-2l-8-5zM3 5.27l4.99 4.99L2 14v2l8-2.5V19l-2 1.5V22l3.5-1 3.5 1v-1.5L13 19v-3.73L18.73 21 20 19.73 4.27 4 3 5.27z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_airplanemode_on_24px" y="120"><path d="M10.18 9"/><path d="M21 16v-2l-8-5V3.5c0-.83-.67-1.5-1.5-1.5S10 2.67 10 3.5V9l-8 5v2l8-2.5V19l-2 1.5V22l3.5-1 3.5 1v-1.5L13 19v-5.5l8 2.5z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_battery_20_24px" y="144"><path d="M7 17v3.67C7 21.4 7.6 22 8.33 22h7.33c.74 0 1.34-.6 1.34-1.33V17H7z"/><path fill-opacity=".3" d="M17 5.33C17 4.6 16.4 4 15.67 4H14V2h-4v2H8.33C7.6 4 7 4.6 7 5.33V17h10V5.33z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_battery_30_24px" y="168"><path fill-opacity=".3" d="M17 5.33C17 4.6 16.4 4 15.67 4H14V2h-4v2H8.33C7.6 4 7 4.6 7 5.33V15h10V5.33z"/><path d="M7 15v5.67C7 21.4 7.6 22 8.33 22h7.33c.74 0 1.34-.6 1.34-1.33V15H7z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_battery_50_24px" y="192"><path fill-opacity=".3" d="M17 5.33C17 4.6 16.4 4 15.67 4H14V2h-4v2H8.33C7.6 4 7 4.6 7 5.33V13h10V5.33z"/><path d="M7 13v7.67C7 21.4 7.6 22 8.33 22h7.33c.74 0 1.34-.6 1.34-1.33V13H7z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_battery_60_24px" y="216"><path fill-opacity=".3" d="M17 5.33C17 4.6 16.4 4 15.67 4H14V2h-4v2H8.33C7.6 4 7 4.6 7 5.33V11h10V5.33z"/><path d="M7 11v9.67C7 21.4 7.6 22 8.33 22h7.33c.74 0 1.34-.6 1.34-1.33V11H7z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_battery_80_24px" y="240"><path fill-opacity=".3" d="M17 5.33C17 4.6 16.4 4 15.67 4H14V2h-4v2H8.33C7.6 4 7 4.6 7 5.33V9h10V5.33z"/><path d="M7 9v11.67C7 21.4 7.6 22 8.33 22h7.33c.74 0 1.34-.6 1.34-1.33V9H7z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_battery_90_24px" y="264"><path fill-opacity=".3" d="M17 5.33C17 4.6 16.4 4 15.67 4H14V2h-4v2H8.33C7.6 4 7 4.6 7 5.33V8h10V5.33z"/><path d="M7 8v12.67C7 21.4 7.6 22 8.33 22h7.33c.74 0 1.34-.6 1.34-1.33V8H7z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_battery_alert_24px" y="288"><path d="M15.67 4H14V2h-4v2H8.33C7.6 4 7 4.6 7 5.33v15.33C7 21.4 7.6 22 8.33 22h7.33c.74 0 1.34-.6 1.34-1.33V5.33C17 4.6 16.4 4 15.67 4zM13 18h-2v-2h2v2zm0-4h-2V9h2v5z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_battery_charging_20_24px" y="312"><path d="M11 20v-3H7v3.67C7 21.4 7.6 22 8.33 22h7.33c.74 0 1.34-.6 1.34-1.33V17h-4.4L11 20z"/><path fill-opacity=".3" d="M15.67 4H14V2h-4v2H8.33C7.6 4 7 4.6 7 5.33V17h4v-2.5H9L13 7v5.5h2L12.6 17H17V5.33C17 4.6 16.4 4 15.67 4z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_battery_charging_30_24px" y="336"><path fill-opacity=".3" d="M15.67 4H14V2h-4v2H8.33C7.6 4 7 4.6 7 5.33v9.17h2L13 7v5.5h2l-1.07 2H17V5.33C17 4.6 16.4 4 15.67 4z"/><path d="M11 20v-5.5H7v6.17C7 21.4 7.6 22 8.33 22h7.33c.74 0 1.34-.6 1.34-1.33V14.5h-3.07L11 20z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_battery_charging_50_24px" y="360"><path d="M14.47 13.5L11 20v-5.5H9l.53-1H7v7.17C7 21.4 7.6 22 8.33 22h7.33c.74 0 1.34-.6 1.34-1.33V13.5h-2.53z"/><path fill-opacity=".3" d="M15.67 4H14V2h-4v2H8.33C7.6 4 7 4.6 7 5.33v8.17h2.53L13 7v5.5h2l-.53 1H17V5.33C17 4.6 16.4 4 15.67 4z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_battery_charging_60_24px" y="384"><path fill-opacity=".3" d="M15.67 4H14V2h-4v2H8.33C7.6 4 7 4.6 7 5.33V11h3.87L13 7v4h4V5.33C17 4.6 16.4 4 15.67 4z"/><path d="M13 12.5h2L11 20v-5.5H9l1.87-3.5H7v9.67C7 21.4 7.6 22 8.33 22h7.33c.74 0 1.34-.6 1.34-1.33V11h-4v1.5z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_battery_charging_80_24px" y="408"><path fill-opacity=".3" d="M15.67 4H14V2h-4v2H8.33C7.6 4 7 4.6 7 5.33V9h4.93L13 7v2h4V5.33C17 4.6 16.4 4 15.67 4z"/><path d="M13 12.5h2L11 20v-5.5H9L11.93 9H7v11.67C7 21.4 7.6 22 8.33 22h7.33c.74 0 1.34-.6 1.34-1.33V9h-4v3.5z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_battery_charging_90_24px" y="432"><path fill-opacity=".3" d="M15.67 4H14V2h-4v2H8.33C7.6 4 7 4.6 7 5.33V8h5.47L13 7v1h4V5.33C17 4.6 16.4 4 15.67 4z"/><path d="M13 12.5h2L11 20v-5.5H9L12.47 8H7v12.67C7 21.4 7.6 22 8.33 22h7.33c.74 0 1.34-.6 1.34-1.33V8h-4v4.5z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_battery_charging_full_24px" y="456"><path d="M15.67 4H14V2h-4v2H8.33C7.6 4 7 4.6 7 5.33v15.33C7 21.4 7.6 22 8.33 22h7.33c.74 0 1.34-.6 1.34-1.33V5.33C17 4.6 16.4 4 15.67 4zM11 20v-5.5H9L13 7v5.5h2L11 20z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_battery_full_24px" y="480"><path d="M15.67 4H14V2h-4v2H8.33C7.6 4 7 4.6 7 5.33v15.33C7 21.4 7.6 22 8.33 22h7.33c.74 0 1.34-.6 1.34-1.33V5.33C17 4.6 16.4 4 15.67 4z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_battery_std_24px" y="504"><path d="M15.67 4H14V2h-4v2H8.33C7.6 4 7 4.6 7 5.33v15.33C7 21.4 7.6 22 8.33 22h7.33c.74 0 1.34-.6 1.34-1.33V5.33C17 4.6 16.4 4 15.67 4z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_battery_unknown_24px" y="528"><path d="M15.67 4H14V2h-4v2H8.33C7.6 4 7 4.6 7 5.33v15.33C7 21.4 7.6 22 8.33 22h7.33c.74 0 1.34-.6 1.34-1.33V5.33C17 4.6 16.4 4 15.67 4zm-2.72 13.95h-1.9v-1.9h1.9v1.9zm1.35-5.26s-.38.42-.67.71c-.48.48-.83 1.15-.83 1.6h-1.6c0-.83.46-1.52.93-2l.93-.94c.27-.27.44-.65.44-1.06 0-.83-.67-1.5-1.5-1.5s-1.5.67-1.5 1.5H9c0-1.66 1.34-3 3-3s3 1.34 3 3c0 .66-.27 1.26-.7 1.69z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_bluetooth_24px" y="552"><path d="M17.71 7.71L12 2h-1v7.59L6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 11 14.41V22h1l5.71-5.71-4.3-4.29 4.3-4.29zM13 5.83l1.88 1.88L13 9.59V5.83zm1.88 10.46L13 18.17v-3.76l1.88 1.88z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_bluetooth_connected_24px" y="576"><path d="M7 12l-2-2-2 2 2 2 2-2zm10.71-4.29L12 2h-1v7.59L6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 11 14.41V22h1l5.71-5.71-4.3-4.29 4.3-4.29zM13 5.83l1.88 1.88L13 9.59V5.83zm1.88 10.46L13 18.17v-3.76l1.88 1.88zM19 10l-2 2 2 2 2-2-2-2z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_bluetooth_disabled_24px" y="600"><path d="M13 5.83l1.88 1.88-1.6 1.6 1.41 1.41 3.02-3.02L12 2h-1v5.03l2 2v-3.2zM5.41 4L4 5.41 10.59 12 5 17.59 6.41 19 11 14.41V22h1l4.29-4.29 2.3 2.29L20 18.59 5.41 4zM13 18.17v-3.76l1.88 1.88L13 18.17z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_bluetooth_searching_24px" y="624"><path d="M14.24 12.01l2.32 2.32c.28-.72.44-1.51.44-2.33 0-.82-.16-1.59-.43-2.31l-2.33 2.32zm5.29-5.3l-1.26 1.26c.63 1.21.98 2.57.98 4.02s-.36 2.82-.98 4.02l1.2 1.2c.97-1.54 1.54-3.36 1.54-5.31-.01-1.89-.55-3.67-1.48-5.19zm-3.82 1L10 2H9v7.59L4.41 5 3 6.41 8.59 12 3 17.59 4.41 19 9 14.41V22h1l5.71-5.71-4.3-4.29 4.3-4.29zM11 5.83l1.88 1.88L11 9.59V5.83zm1.88 10.46L11 18.17v-3.76l1.88 1.88z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_brightness_auto_24px" y="648"><path d="M10.85 12.65h2.3L12 9l-1.15 3.65zM20 8.69V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12 20 8.69zM14.3 16l-.7-2h-3.2l-.7 2H7.8L11 7h2l3.2 9h-1.9z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_brightness_high_24px" y="672"><path d="M20 8.69V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12 20 8.69zM12 18c-3.31 0-6-2.69-6-6s2.69-6 6-6 6 2.69 6 6-2.69 6-6 6zm0-10c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_brightness_low_24px" y="696"><path d="M20 15.31L23.31 12 20 8.69V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69zM12 18c-3.31 0-6-2.69-6-6s2.69-6 6-6 6 2.69 6 6-2.69 6-6 6z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_brightness_medium_24px" y="720"><path d="M20 15.31L23.31 12 20 8.69V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69zM12 18V6c3.31 0 6 2.69 6 6s-2.69 6-6 6z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_data_usage_24px" y="744"><path d="M13 2.05v3.03c3.39.49 6 3.39 6 6.92 0 .9-.18 1.75-.48 2.54l2.6 1.53c.56-1.24.88-2.62.88-4.07 0-5.18-3.95-9.45-9-9.95zM12 19c-3.87 0-7-3.13-7-7 0-3.53 2.61-6.43 6-6.92V2.05c-5.06.5-9 4.76-9 9.95 0 5.52 4.47 10 9.99 10 3.31 0 6.24-1.61 8.06-4.09l-2.6-1.53C16.17 17.98 14.21 19 12 19z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_developer_mode_24px" y="768"><path d="M7 5h10v2h2V3c0-1.1-.9-1.99-2-1.99L7 1c-1.1 0-2 .9-2 2v4h2V5zm8.41 11.59L20 12l-4.59-4.59L14 8.83 17.17 12 14 15.17l1.41 1.42zM10 15.17L6.83 12 10 8.83 8.59 7.41 4 12l4.59 4.59L10 15.17zM17 19H7v-2H5v4c0 1.1.9 2 2 2h10c1.1 0 2-.9 2-2v-4h-2v2z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_devices_24px" y="792"><path d="M4 6h18V4H4c-1.1 0-2 .9-2 2v11H0v3h14v-3H4V6zm19 2h-6c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h6c.55 0 1-.45 1-1V9c0-.55-.45-1-1-1zm-1 9h-4v-7h4v7z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_dvr_24px" y="816"><path d="M21 3H3c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h5v2h8v-2h5c1.1 0 1.99-.9 1.99-2L23 5c0-1.1-.9-2-2-2zm0 14H3V5h18v12zm-2-9H8v2h11V8zm0 4H8v2h11v-2zM7 8H5v2h2V8zm0 4H5v2h2v-2z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_gps_fixed_24px" y="840"><path d="M12 8c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm8.94 3c-.46-4.17-3.77-7.48-7.94-7.94V1h-2v2.06C6.83 3.52 3.52 6.83 3.06 11H1v2h2.06c.46 4.17 3.77 7.48 7.94 7.94V23h2v-2.06c4.17-.46 7.48-3.77 7.94-7.94H23v-2h-2.06zM12 19c-3.87 0-7-3.13-7-7s3.13-7 7-7 7 3.13 7 7-3.13 7-7 7z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_gps_not_fixed_24px" y="864"><path d="M20.94 11c-.46-4.17-3.77-7.48-7.94-7.94V1h-2v2.06C6.83 3.52 3.52 6.83 3.06 11H1v2h2.06c.46 4.17 3.77 7.48 7.94 7.94V23h2v-2.06c4.17-.46 7.48-3.77 7.94-7.94H23v-2h-2.06zM12 19c-3.87 0-7-3.13-7-7s3.13-7 7-7 7 3.13 7 7-3.13 7-7 7z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_gps_off_24px" y="888"><path d="M20.94 11c-.46-4.17-3.77-7.48-7.94-7.94V1h-2v2.06c-1.13.12-2.19.46-3.16.97l1.5 1.5C10.16 5.19 11.06 5 12 5c3.87 0 7 3.13 7 7 0 .94-.19 1.84-.52 2.65l1.5 1.5c.5-.96.84-2.02.97-3.15H23v-2h-2.06zM3 4.27l2.04 2.04C3.97 7.62 3.25 9.23 3.06 11H1v2h2.06c.46 4.17 3.77 7.48 7.94 7.94V23h2v-2.06c1.77-.2 3.38-.91 4.69-1.98L19.73 21 21 19.73 4.27 3 3 4.27zm13.27 13.27C15.09 18.45 13.61 19 12 19c-3.87 0-7-3.13-7-7 0-1.61.55-3.09 1.46-4.27l9.81 9.81z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_location_disabled_24px" y="912"><path d="M20.94 11c-.46-4.17-3.77-7.48-7.94-7.94V1h-2v2.06c-1.13.12-2.19.46-3.16.97l1.5 1.5C10.16 5.19 11.06 5 12 5c3.87 0 7 3.13 7 7 0 .94-.19 1.84-.52 2.65l1.5 1.5c.5-.96.84-2.02.97-3.15H23v-2h-2.06zM3 4.27l2.04 2.04C3.97 7.62 3.25 9.23 3.06 11H1v2h2.06c.46 4.17 3.77 7.48 7.94 7.94V23h2v-2.06c1.77-.2 3.38-.91 4.69-1.98L19.73 21 21 19.73 4.27 3 3 4.27zm13.27 13.27C15.09 18.45 13.61 19 12 19c-3.87 0-7-3.13-7-7 0-1.61.55-3.09 1.46-4.27l9.81 9.81z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_location_searching_24px" y="936"><path d="M20.94 11c-.46-4.17-3.77-7.48-7.94-7.94V1h-2v2.06C6.83 3.52 3.52 6.83 3.06 11H1v2h2.06c.46 4.17 3.77 7.48 7.94 7.94V23h2v-2.06c4.17-.46 7.48-3.77 7.94-7.94H23v-2h-2.06zM12 19c-3.87 0-7-3.13-7-7s3.13-7 7-7 7 3.13 7 7-3.13 7-7 7z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_multitrack_audio_24px" y="960"><path d="M7 18h2V6H7v12zm4 4h2V2h-2v20zm-8-8h2v-4H3v4zm12 4h2V6h-2v12zm4-8v4h2v-4h-2z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_network_cell_24px" y="984"><path fill-opacity=".3" d="M2 22h20V2z"/><path d="M17 7L2 22h15z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_network_wifi_24px" y="1008"><path fill-opacity=".3" d="M12.01 21.49L23.64 7c-.45-.34-4.93-4-11.64-4C5.28 3 .81 6.66.36 7l11.63 14.49.01.01.01-.01z"/><path d="M3.53 10.95l8.46 10.54.01.01.01-.01 8.46-10.54C20.04 10.62 16.81 8 12 8c-4.81 0-8.04 2.62-8.47 2.95z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_nfc_24px" y="1032"><path d="M20 2H4c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 18H4V4h16v16zM18 6h-5c-1.1 0-2 .9-2 2v2.28c-.6.35-1 .98-1 1.72 0 1.1.9 2 2 2s2-.9 2-2c0-.74-.4-1.38-1-1.72V8h3v8H8V8h2V6H6v12h12V6z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_now_wallpaper_24px" y="1056"><path d="M4 4h7V2H4c-1.1 0-2 .9-2 2v7h2V4zm6 9l-4 5h12l-3-4-2.03 2.71L10 13zm7-4.5c0-.83-.67-1.5-1.5-1.5S14 7.67 14 8.5s.67 1.5 1.5 1.5S17 9.33 17 8.5zM20 2h-7v2h7v7h2V4c0-1.1-.9-2-2-2zm0 18h-7v2h7c1.1 0 2-.9 2-2v-7h-2v7zM4 13H2v7c0 1.1.9 2 2 2h7v-2H4v-7z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_now_widgets_24px" y="1080"><path d="M13 13v8h8v-8h-8zM3 21h8v-8H3v8zM3 3v8h8V3H3zm13.66-1.31L11 7.34 16.66 13l5.66-5.66-5.66-5.65z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_screen_lock_landscape_24px" y="1104"><path d="M21 5H3c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm-2 12H5V7h14v10zm-9-1h4c.55 0 1-.45 1-1v-3c0-.55-.45-1-1-1v-1c0-1.11-.9-2-2-2-1.11 0-2 .9-2 2v1c-.55 0-1 .45-1 1v3c0 .55.45 1 1 1zm.8-6c0-.66.54-1.2 1.2-1.2.66 0 1.2.54 1.2 1.2v1h-2.4v-1z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_screen_lock_portrait_24px" y="1128"><path d="M10 16h4c.55 0 1-.45 1-1v-3c0-.55-.45-1-1-1v-1c0-1.11-.9-2-2-2-1.11 0-2 .9-2 2v1c-.55 0-1 .45-1 1v3c0 .55.45 1 1 1zm.8-6c0-.66.54-1.2 1.2-1.2.66 0 1.2.54 1.2 1.2v1h-2.4v-1zM17 1H7c-1.1 0-2 .9-2 2v18c0 1.1.9 2 2 2h10c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2zm0 18H7V5h10v14z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_screen_lock_rotation_24px" y="1152"><path d="M23.25 12.77l-2.57-2.57-1.41 1.41 2.22 2.22-5.66 5.66L4.51 8.17l5.66-5.66 2.1 2.1 1.41-1.41L11.23.75c-.59-.59-1.54-.59-2.12 0L2.75 7.11c-.59.59-.59 1.54 0 2.12l12.02 12.02c.59.59 1.54.59 2.12 0l6.36-6.36c.59-.59.59-1.54 0-2.12zM8.47 20.48C5.2 18.94 2.86 15.76 2.5 12H1c.51 6.16 5.66 11 11.95 11l.66-.03-3.81-3.82-1.33 1.33zM16 9h5c.55 0 1-.45 1-1V4c0-.55-.45-1-1-1v-.5C21 1.12 19.88 0 18.5 0S16 1.12 16 2.5V3c-.55 0-1 .45-1 1v4c0 .55.45 1 1 1zm.8-6.5c0-.94.76-1.7 1.7-1.7s1.7.76 1.7 1.7V3h-3.4v-.5z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_screen_rotation_24px" y="1176"><path d="M16.48 2.52c3.27 1.55 5.61 4.72 5.97 8.48h1.5C23.44 4.84 18.29 0 12 0l-.66.03 3.81 3.81 1.33-1.32zm-6.25-.77c-.59-.59-1.54-.59-2.12 0L1.75 8.11c-.59.59-.59 1.54 0 2.12l12.02 12.02c.59.59 1.54.59 2.12 0l6.36-6.36c.59-.59.59-1.54 0-2.12L10.23 1.75zm4.6 19.44L2.81 9.17l6.36-6.36 12.02 12.02-6.36 6.36zm-7.31.29C4.25 19.94 1.91 16.76 1.55 13H.05C.56 19.16 5.71 24 12 24l.66-.03-3.81-3.81-1.33 1.32z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_sd_storage_24px" y="1200"><path d="M18 2h-8L4.02 8 4 20c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-6 6h-2V4h2v4zm3 0h-2V4h2v4zm3 0h-2V4h2v4z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_settings_system_daydream_24px" y="1224"><path d="M9 16h6.5c1.38 0 2.5-1.12 2.5-2.5S16.88 11 15.5 11h-.05c-.24-1.69-1.69-3-3.45-3-1.4 0-2.6.83-3.16 2.02h-.16C7.17 10.18 6 11.45 6 13c0 1.66 1.34 3 3 3zM21 3H3c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16.01H3V4.99h18v14.02z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_signal_cellular_0_bar_24px" y="1248"><path fill-opacity=".3" d="M2 22h20V2z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_signal_cellular_1_bar_24px" y="1272"><path fill-opacity=".3" d="M2 22h20V2z"/><path d="M12 12L2 22h10z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_signal_cellular_2_bar_24px" y="1296"><path fill-opacity=".3" d="M2 22h20V2z"/><path d="M14 10L2 22h12z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_signal_cellular_3_bar_24px" y="1320"><path fill-opacity=".3" d="M2 22h20V2z"/><path d="M17 7L2 22h15z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_signal_cellular_4_bar_24px" y="1344"><path d="M2 22h20V2z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_signal_cellular_connected_no_internet_0_bar_24px" y="1368"><path fill-opacity=".3" d="M22 8V2L2 22h16V8z"/><path d="M20 22h2v-2h-2v2zm0-12v8h2v-8h-2z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_signal_cellular_connected_no_internet_1_bar_24px" y="1392"><path fill-opacity=".3" d="M22 8V2L2 22h16V8z"/><path d="M20 10v8h2v-8h-2zm-8 12V12L2 22h10zm8 0h2v-2h-2v2z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_signal_cellular_connected_no_internet_2_bar_24px" y="1416"><path fill-opacity=".3" d="M22 8V2L2 22h16V8z"/><path d="M14 22V10L2 22h12zm6-12v8h2v-8h-2zm0 12h2v-2h-2v2z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_signal_cellular_connected_no_internet_3_bar_24px" y="1440"><path fill-opacity=".3" d="M22 8V2L2 22h16V8z"/><path d="M17 22V7L2 22h15zm3-12v8h2v-8h-2zm0 12h2v-2h-2v2z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_signal_cellular_connected_no_internet_4_bar_24px" y="1464"><path d="M20 18h2v-8h-2v8zm0 4h2v-2h-2v2zM2 22h16V8h4V2L2 22z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_signal_cellular_no_sim_24px" y="1488"><path d="M18.99 5c0-1.1-.89-2-1.99-2h-7L7.66 5.34 19 16.68 18.99 5zM3.65 3.88L2.38 5.15 5 7.77V19c0 1.1.9 2 2 2h10.01c.35 0 .67-.1.96-.26l1.88 1.88 1.27-1.27L3.65 3.88z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_signal_cellular_null_24px" y="1512"><path d="M20 6.83V20H6.83L20 6.83M22 2L2 22h20V2z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_signal_cellular_off_24px" y="1536"><path d="M21 1l-8.59 8.59L21 18.18V1zM4.77 4.5L3.5 5.77l6.36 6.36L1 21h17.73l2 2L22 21.73 4.77 4.5z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_signal_wifi_0_bar_24px" y="1560"><path fill-opacity=".3" d="M12.01 21.49L23.64 7c-.45-.34-4.93-4-11.64-4C5.28 3 .81 6.66.36 7l11.63 14.49.01.01.01-.01z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_signal_wifi_1_bar_24px" y="1584"><path fill-opacity=".3" d="M12.01 21.49L23.64 7c-.45-.34-4.93-4-11.64-4C5.28 3 .81 6.66.36 7l11.63 14.49.01.01.01-.01z"/><path d="M6.67 14.86L12 21.49v.01l.01-.01 5.33-6.63C17.06 14.65 15.03 13 12 13s-5.06 1.65-5.33 1.86z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_signal_wifi_2_bar_24px" y="1608"><path fill-opacity=".3" d="M12.01 21.49L23.64 7c-.45-.34-4.93-4-11.64-4C5.28 3 .81 6.66.36 7l11.63 14.49.01.01.01-.01z"/><path d="M4.79 12.52l7.2 8.98H12l.01-.01 7.2-8.98C18.85 12.24 16.1 10 12 10s-6.85 2.24-7.21 2.52z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_signal_wifi_3_bar_24px" y="1632"><path fill-opacity=".3" d="M12.01 21.49L23.64 7c-.45-.34-4.93-4-11.64-4C5.28 3 .81 6.66.36 7l11.63 14.49.01.01.01-.01z"/><path d="M3.53 10.95l8.46 10.54.01.01.01-.01 8.46-10.54C20.04 10.62 16.81 8 12 8c-4.81 0-8.04 2.62-8.47 2.95z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_signal_wifi_4_bar_24px" y="1656"><path d="M12.01 21.49L23.64 7c-.45-.34-4.93-4-11.64-4C5.28 3 .81 6.66.36 7l11.63 14.49.01.01.01-.01z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_signal_wifi_off_24px" y="1680"><path d="M23.64 7c-.45-.34-4.93-4-11.64-4-1.5 0-2.89.19-4.15.48L18.18 13.8 23.64 7zm-6.6 8.22L3.27 1.44 2 2.72l2.05 2.06C1.91 5.76.59 6.82.36 7l11.63 14.49.01.01.01-.01 3.9-4.86 3.32 3.32 1.27-1.27-3.46-3.46z"/></svg><svg width="26" height="24" viewBox="0 0 26 24" id="ic_signal_wifi_statusbar_1_bar_26x24px" y="1704"><path d="M13 21.99l5.66-7.05C18.44 14.78 16.27 13 13 13s-5.44 1.78-5.66 1.95L13 21.99z"/><path fill-opacity=".3" d="M13.01 21.99L25.58 6.32C25.1 5.96 20.26 2 13 2S.9 5.96.42 6.32l12.57 15.66.01.02.01-.01z"/></svg><svg width="26" height="24" viewBox="0 0 26 24" id="ic_signal_wifi_statusbar_2_bar_26x24px" y="1728"><path fill-opacity=".3" d="M13.01 21.99L25.58 6.32C25.1 5.96 20.26 2 13 2S.9 5.96.42 6.32l12.57 15.66.01.02.01-.01z"/><path d="M13.01 21.99l7.54-9.4C20.26 12.38 17.36 10 13 10c-4.36 0-7.26 2.38-7.55 2.59l7.54 9.4h.02z"/></svg><svg width="26" height="24" viewBox="0 0 26 24" id="ic_signal_wifi_statusbar_3_bar_26x24px" y="1752"><path d="M13.01 21.99l9.43-11.75C22.07 9.97 18.44 7 13 7c-5.44 0-9.07 2.97-9.44 3.24l9.43 11.75h.02z"/><path fill-opacity=".3" d="M13.01 21.99L25.58 6.32C25.1 5.96 20.26 2 13 2S.9 5.96.42 6.32l12.57 15.66.01.02.01-.01z"/></svg><svg width="26" height="24" viewBox="0 0 26 24" id="ic_signal_wifi_statusbar_4_bar_26x24px" y="1776"><path d="M13.01 21.99L25.58 6.32C25.1 5.96 20.26 2 13 2S.9 5.96.42 6.32l12.57 15.66.01.02.01-.01z"/></svg><svg width="26" height="24" viewBox="0 0 26 24" id="ic_signal_wifi_statusbar_connected_no_internet_1_26x24px" y="1800"><path fill-opacity=".3" d="M24.24 8l1.35-1.68C25.1 5.96 20.26 2 13 2S.9 5.96.42 6.32l12.57 15.66.01.02.01-.01L20 13.28V8h4.24z"/><path d="M7.34 14.95L13 21.99V22v-.01l5.66-7.05C18.44 14.78 16.27 13 13 13s-5.44 1.78-5.66 1.95zM22 22h2v-2h-2v2zm0-12v8h2v-8h-2z"/></svg><svg width="26" height="24" viewBox="0 0 26 24" id="ic_signal_wifi_statusbar_connected_no_internet_26x24px" y="1824"><path fill-opacity=".3" d="M24.24 8l1.35-1.68C25.1 5.96 20.26 2 13 2S.9 5.96.42 6.32l12.57 15.66.01.02.01-.01L20 13.28V8h4.24z"/><path d="M22 22h2v-2h-2v2zm0-12v8h2v-8h-2z"/></svg><svg width="26" height="24" viewBox="0 0 26 24" id="ic_signal_wifi_statusbar_connected_no_internet_2_26x24px" y="1848"><path fill-opacity=".3" d="M24.24 8l1.35-1.68C25.1 5.96 20.26 2 13 2S.9 5.96.42 6.32l12.57 15.66.01.02.01-.01L20 13.28V8h4.24z"/><path d="M5.45 12.59l7.54 9.4.01.01.01-.01L20 13.28v-1.09c-1.07-.73-3.59-2.19-7-2.19-4.36 0-7.26 2.38-7.55 2.59zM22 10v8h2v-8h-2zm0 12h2v-2h-2v2z"/></svg><svg width="26" height="24" viewBox="0 0 26 24" id="ic_signal_wifi_statusbar_connected_no_internet_3_26x24px" y="1872"><path fill-opacity=".3" d="M24.24 8l1.35-1.68C25.1 5.96 20.26 2 13 2S.9 5.96.42 6.32l12.57 15.66.01.02.01-.01L20 13.28V8h4.24z"/><path d="M20 13.28V8.71C18.35 7.87 15.94 7 13 7c-5.44 0-9.07 2.97-9.44 3.24l9.43 11.75.01.01.01-.01L20 13.28zM22 22h2v-2h-2v2zm0-12v8h2v-8h-2z"/></svg><svg width="26" height="24" viewBox="0 0 26 24" id="ic_signal_wifi_statusbar_connected_no_internet_4_26x24px" y="1896"><path d="M22 22h2v-2h-2v2zM13 2C5.74 2 .9 5.96.42 6.32l12.57 15.66.01.02.01-.01L20 13.28V8h4.24l1.35-1.68C25.1 5.96 20.26 2 13 2zm9 16h2v-8h-2v8z"/></svg><svg width="26" height="24" viewBox="0 0 26 24" id="ic_signal_wifi_statusbar_not_connected_26x24px" y="1920"><path fill-opacity=".3" d="M21 8.5c.85 0 1.64.23 2.34.62l2.24-2.79C25.1 5.96 20.26 2 13 2S.9 5.96.42 6.32l12.57 15.66.01.02.01-.01 4.21-5.24c-.76-.87-1.22-2-1.22-3.25 0-2.76 2.24-5 5-5z"/><path d="M21 10c-1.93 0-3.5 1.57-3.5 3.5h1.75c0-.97.78-1.75 1.75-1.75s1.75.78 1.75 1.75c0 .48-.2.92-.51 1.24l-1.09 1.1c-.63.63-1.02 1.51-1.02 2.47v.44h1.75c0-1.31.39-1.84 1.03-2.47l.78-.8c.5-.5.82-1.2.82-1.97C24.5 11.57 22.93 10 21 10zm-.95 11.95h1.9v-1.9h-1.9v1.9z"/></svg><svg width="26" height="24" viewBox="0 0 26 24" id="ic_signal_wifi_statusbar_null_26x24px" y="1944"><path d="M13 4c4.25 0 7.62 1.51 9.68 2.75L13 18.8 3.33 6.75C5.38 5.51 8.75 4 13 4m0-2C5.74 2 .9 5.96.42 6.32l12.57 15.66.01.02.01-.01L25.58 6.32C25.1 5.96 20.26 2 13 2z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_storage_24px" y="1968"><path d="M2 20h20v-4H2v4zm2-3h2v2H4v-2zM2 4v4h20V4H2zm4 3H4V5h2v2zm-4 7h20v-4H2v4zm2-3h2v2H4v-2z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_usb_24px" y="1992"><path d="M15 7v4h1v2h-3V5h2l-3-4-3 4h2v8H8v-2.07c.7-.37 1.2-1.08 1.2-1.93 0-1.21-.99-2.2-2.2-2.2-1.21 0-2.2.99-2.2 2.2 0 .85.5 1.56 1.2 1.93V13c0 1.11.89 2 2 2h3v3.05c-.71.37-1.2 1.1-1.2 1.95 0 1.22.99 2.2 2.2 2.2 1.21 0 2.2-.98 2.2-2.2 0-.85-.49-1.58-1.2-1.95V15h3c1.11 0 2-.89 2-2v-2h1V7h-4z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_wifi_lock_24px" y="2016"><path d="M20.5 9.5c.28 0 .55.04.81.08L24 6c-3.34-2.51-7.5-4-12-4S3.34 3.49 0 6l12 16 3.5-4.67V14.5c0-2.76 2.24-5 5-5zM23 16v-1.5c0-1.38-1.12-2.5-2.5-2.5S18 13.12 18 14.5V16c-.55 0-1 .45-1 1v4c0 .55.45 1 1 1h5c.55 0 1-.45 1-1v-4c0-.55-.45-1-1-1zm-1 0h-3v-1.5c0-.83.67-1.5 1.5-1.5s1.5.67 1.5 1.5V16z"/></svg><svg width="24" height="24" viewBox="0 0 24 24" id="ic_wifi_tethering_24px" y="2040"><path d="M12 11c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm6 2c0-3.31-2.69-6-6-6s-6 2.69-6 6c0 2.22 1.21 4.15 3 5.19l1-1.74c-1.19-.7-2-1.97-2-3.45 0-2.21 1.79-4 4-4s4 1.79 4 4c0 1.48-.81 2.75-2 3.45l1 1.74c1.79-1.04 3-2.97 3-5.19zM12 3C6.48 3 2 7.48 2 13c0 3.7 2.01 6.92 4.99 8.65l1-1.73C5.61 18.53 4 15.96 4 13c0-4.42 3.58-8 8-8s8 3.58 8 8c0 2.96-1.61 5.53-4 6.92l1 1.73c2.99-1.73 5-4.95 5-8.65 0-5.52-4.48-10-10-10z"/></svg></svg>
\ 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 0000000..5a0a575
--- /dev/null
+++ b/skyquake/framework/style/icons/svg-sprite-editor-symbol.svg
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><symbol viewBox="0 0 24 24" id="ic_attach_file_24px"><path d="M16.5 6v11.5c0 2.21-1.79 4-4 4s-4-1.79-4-4V5a2.5 2.5 0 0 1 5 0v10.5c0 .55-.45 1-1 1s-1-.45-1-1V6H10v9.5a2.5 2.5 0 0 0 5 0V5c0-2.21-1.79-4-4-4S7 2.79 7 5v12.5c0 3.04 2.46 5.5 5.5 5.5s5.5-2.46 5.5-5.5V6h-1.5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_attach_money_24px"><path d="M11.8 10.9c-2.27-.59-3-1.2-3-2.15 0-1.09 1.01-1.85 2.7-1.85 1.78 0 2.44.85 2.5 2.1h2.21c-.07-1.72-1.12-3.3-3.21-3.81V3h-3v2.16c-1.94.42-3.5 1.68-3.5 3.61 0 2.31 1.91 3.46 4.7 4.13 2.5.6 3 1.48 3 2.41 0 .69-.49 1.79-2.7 1.79-2.06 0-2.87-.92-2.98-2.1h-2.2c.12 2.19 1.76 3.42 3.68 3.83V21h3v-2.15c1.95-.37 3.5-1.5 3.5-3.55 0-2.84-2.43-3.81-4.7-4.4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_border_all_24px"><path d="M3 3v18h18V3H3zm8 16H5v-6h6v6zm0-8H5V5h6v6zm8 8h-6v-6h6v6zm0-8h-6V5h6v6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_border_bottom_24px"><path d="M9 11H7v2h2v-2zm4 4h-2v2h2v-2zM9 3H7v2h2V3zm4 8h-2v2h2v-2zM5 3H3v2h2V3zm8 4h-2v2h2V7zm4 4h-2v2h2v-2zm-4-8h-2v2h2V3zm4 0h-2v2h2V3zm2 10h2v-2h-2v2zm0 4h2v-2h-2v2zM5 7H3v2h2V7zm14-4v2h2V3h-2zm0 6h2V7h-2v2zM5 11H3v2h2v-2zM3 21h18v-2H3v2zm2-6H3v2h2v-2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_border_clear_24px"><path d="M7 5h2V3H7v2zm0 8h2v-2H7v2zm0 8h2v-2H7v2zm4-4h2v-2h-2v2zm0 4h2v-2h-2v2zm-8 0h2v-2H3v2zm0-4h2v-2H3v2zm0-4h2v-2H3v2zm0-4h2V7H3v2zm0-4h2V3H3v2zm8 8h2v-2h-2v2zm8 4h2v-2h-2v2zm0-4h2v-2h-2v2zm0 8h2v-2h-2v2zm0-12h2V7h-2v2zm-8 0h2V7h-2v2zm8-6v2h2V3h-2zm-8 2h2V3h-2v2zm4 16h2v-2h-2v2zm0-8h2v-2h-2v2zm0-8h2V3h-2v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_border_color_24px"><path d="M17.75 7L14 3.25l-10 10V17h3.75l10-10zm2.96-2.96a.996.996 0 0 0 0-1.41L18.37.29a.996.996 0 0 0-1.41 0L15 2.25 18.75 6l1.96-1.96z"/><path fill-opacity=".36" d="M0 20h24v4H0z"/></symbol><symbol viewBox="0 0 24 24" id="ic_border_horizontal_24px"><path d="M3 21h2v-2H3v2zM5 7H3v2h2V7zM3 17h2v-2H3v2zm4 4h2v-2H7v2zM5 3H3v2h2V3zm4 0H7v2h2V3zm8 0h-2v2h2V3zm-4 4h-2v2h2V7zm0-4h-2v2h2V3zm6 14h2v-2h-2v2zm-8 4h2v-2h-2v2zm-8-8h18v-2H3v2zM19 3v2h2V3h-2zm0 6h2V7h-2v2zm-8 8h2v-2h-2v2zm4 4h2v-2h-2v2zm4 0h2v-2h-2v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_border_inner_24px"><path d="M3 21h2v-2H3v2zm4 0h2v-2H7v2zM5 7H3v2h2V7zM3 17h2v-2H3v2zM9 3H7v2h2V3zM5 3H3v2h2V3zm12 0h-2v2h2V3zm2 6h2V7h-2v2zm0-6v2h2V3h-2zm-4 18h2v-2h-2v2zM13 3h-2v8H3v2h8v8h2v-8h8v-2h-8V3zm6 18h2v-2h-2v2zm0-4h2v-2h-2v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_border_left_24px"><path d="M11 21h2v-2h-2v2zm0-4h2v-2h-2v2zm0-12h2V3h-2v2zm0 4h2V7h-2v2zm0 4h2v-2h-2v2zm-4 8h2v-2H7v2zM7 5h2V3H7v2zm0 8h2v-2H7v2zm-4 8h2V3H3v18zM19 9h2V7h-2v2zm-4 12h2v-2h-2v2zm4-4h2v-2h-2v2zm0-14v2h2V3h-2zm0 10h2v-2h-2v2zm0 8h2v-2h-2v2zm-4-8h2v-2h-2v2zm0-8h2V3h-2v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_border_outer_24px"><path d="M13 7h-2v2h2V7zm0 4h-2v2h2v-2zm4 0h-2v2h2v-2zM3 3v18h18V3H3zm16 16H5V5h14v14zm-6-4h-2v2h2v-2zm-4-4H7v2h2v-2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_border_right_24px"><path d="M7 21h2v-2H7v2zM3 5h2V3H3v2zm4 0h2V3H7v2zm0 8h2v-2H7v2zm-4 8h2v-2H3v2zm8 0h2v-2h-2v2zm-8-8h2v-2H3v2zm0 4h2v-2H3v2zm0-8h2V7H3v2zm8 8h2v-2h-2v2zm4-4h2v-2h-2v2zm4-10v18h2V3h-2zm-4 18h2v-2h-2v2zm0-16h2V3h-2v2zm-4 8h2v-2h-2v2zm0-8h2V3h-2v2zm0 4h2V7h-2v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_border_style_24px"><path d="M15 21h2v-2h-2v2zm4 0h2v-2h-2v2zM7 21h2v-2H7v2zm4 0h2v-2h-2v2zm8-4h2v-2h-2v2zm0-4h2v-2h-2v2zM3 3v18h2V5h16V3H3zm16 6h2V7h-2v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_border_top_24px"><path d="M7 21h2v-2H7v2zm0-8h2v-2H7v2zm4 0h2v-2h-2v2zm0 8h2v-2h-2v2zm-8-4h2v-2H3v2zm0 4h2v-2H3v2zm0-8h2v-2H3v2zm0-4h2V7H3v2zm8 8h2v-2h-2v2zm8-8h2V7h-2v2zm0 4h2v-2h-2v2zM3 3v2h18V3H3zm16 14h2v-2h-2v2zm-4 4h2v-2h-2v2zM11 9h2V7h-2v2zm8 12h2v-2h-2v2zm-4-8h2v-2h-2v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_border_vertical_24px"><path d="M3 9h2V7H3v2zm0-4h2V3H3v2zm4 16h2v-2H7v2zm0-8h2v-2H7v2zm-4 0h2v-2H3v2zm0 8h2v-2H3v2zm0-4h2v-2H3v2zM7 5h2V3H7v2zm12 12h2v-2h-2v2zm-8 4h2V3h-2v18zm8 0h2v-2h-2v2zm0-8h2v-2h-2v2zm0-10v2h2V3h-2zm0 6h2V7h-2v2zm-4-4h2V3h-2v2zm0 16h2v-2h-2v2zm0-8h2v-2h-2v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_bubble_chart_24px"><circle cx="7.2" cy="14.4" r="3.2"/><circle cx="14.8" cy="18" r="2"/><circle cx="15.2" cy="8.8" r="4.8"/></symbol><symbol viewBox="0 0 24 24" id="ic_drag_handle_24px"><path d="M20 9H4v2h16V9zM4 15h16v-2H4v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_format_align_center_24px"><path d="M7 15v2h10v-2H7zm-4 6h18v-2H3v2zm0-8h18v-2H3v2zm4-6v2h10V7H7zM3 3v2h18V3H3z"/></symbol><symbol viewBox="0 0 24 24" id="ic_format_align_justify_24px"><path d="M3 21h18v-2H3v2zm0-4h18v-2H3v2zm0-4h18v-2H3v2zm0-4h18V7H3v2zm0-6v2h18V3H3z"/></symbol><symbol viewBox="0 0 24 24" id="ic_format_align_left_24px"><path d="M15 15H3v2h12v-2zm0-8H3v2h12V7zM3 13h18v-2H3v2zm0 8h18v-2H3v2zM3 3v2h18V3H3z"/></symbol><symbol viewBox="0 0 24 24" id="ic_format_align_right_24px"><path d="M3 21h18v-2H3v2zm6-4h12v-2H9v2zm-6-4h18v-2H3v2zm6-4h12V7H9v2zM3 3v2h18V3H3z"/></symbol><symbol viewBox="0 0 24 24" id="ic_format_bold_24px"><path d="M15.6 10.79c.97-.67 1.65-1.77 1.65-2.79 0-2.26-1.75-4-4-4H7v14h7.04c2.09 0 3.71-1.7 3.71-3.79 0-1.52-.86-2.82-2.15-3.42zM10 6.5h3c.83 0 1.5.67 1.5 1.5s-.67 1.5-1.5 1.5h-3v-3zm3.5 9H10v-3h3.5c.83 0 1.5.67 1.5 1.5s-.67 1.5-1.5 1.5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_format_clear_24px"><path d="M3.27 5L2 6.27l6.97 6.97L6.5 19h3l1.57-3.66L16.73 21 18 19.73 3.55 5.27 3.27 5zM6 5v.18L8.82 8h2.4l-.72 1.68 2.1 2.1L14.21 8H20V5H6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_format_color_fill_24px"><path d="M16.56 8.94L7.62 0 6.21 1.41l2.38 2.38-5.15 5.15a1.49 1.49 0 0 0 0 2.12l5.5 5.5c.29.29.68.44 1.06.44s.77-.15 1.06-.44l5.5-5.5c.59-.58.59-1.53 0-2.12zM5.21 10L10 5.21 14.79 10H5.21zM19 11.5s-2 2.17-2 3.5c0 1.1.9 2 2 2s2-.9 2-2c0-1.33-2-3.5-2-3.5z"/><path fill-opacity=".36" d="M0 20h24v4H0z"/></symbol><symbol viewBox="0 0 24 24" id="ic_format_color_reset_24px"><path d="M18 14c0-4-6-10.8-6-10.8s-1.33 1.51-2.73 3.52l8.59 8.59c.09-.42.14-.86.14-1.31zm-.88 3.12L12.5 12.5 5.27 5.27 4 6.55l3.32 3.32C6.55 11.32 6 12.79 6 14c0 3.31 2.69 6 6 6 1.52 0 2.9-.57 3.96-1.5l2.63 2.63 1.27-1.27-2.74-2.74z"/></symbol><symbol viewBox="0 0 24 24" id="ic_format_color_text_24px"><path fill-opacity=".36" d="M0 20h24v4H0z"/><path d="M11 3L5.5 17h2.25l1.12-3h6.25l1.12 3h2.25L13 3h-2zm-1.38 9L12 5.67 14.38 12H9.62z"/></symbol><symbol viewBox="0 0 24 24" id="ic_format_indent_decrease_24px"><path d="M11 17h10v-2H11v2zm-8-5l4 4V8l-4 4zm0 9h18v-2H3v2zM3 3v2h18V3H3zm8 6h10V7H11v2zm0 4h10v-2H11v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_format_indent_increase_24px"><path d="M3 21h18v-2H3v2zM3 8v8l4-4-4-4zm8 9h10v-2H11v2zM3 3v2h18V3H3zm8 6h10V7H11v2zm0 4h10v-2H11v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_format_italic_24px"><path d="M10 4v3h2.21l-3.42 8H6v3h8v-3h-2.21l3.42-8H18V4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_format_line_spacing_24px"><path d="M6 7h2.5L5 3.5 1.5 7H4v10H1.5L5 20.5 8.5 17H6V7zm4-2v2h12V5H10zm0 14h12v-2H10v2zm0-6h12v-2H10v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_format_list_bulleted_24px"><path d="M4 10.5c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5zm0-6c-.83 0-1.5.67-1.5 1.5S3.17 7.5 4 7.5 5.5 6.83 5.5 6 4.83 4.5 4 4.5zm0 12c-.83 0-1.5.68-1.5 1.5s.68 1.5 1.5 1.5 1.5-.68 1.5-1.5-.67-1.5-1.5-1.5zM7 19h14v-2H7v2zm0-6h14v-2H7v2zm0-8v2h14V5H7z"/></symbol><symbol viewBox="0 0 24 24" id="ic_format_list_numbered_24px"><path d="M2 17h2v.5H3v1h1v.5H2v1h3v-4H2v1zm1-9h1V4H2v1h1v3zm-1 3h1.8L2 13.1v.9h3v-1H3.2L5 10.9V10H2v1zm5-6v2h14V5H7zm0 14h14v-2H7v2zm0-6h14v-2H7v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_format_paint_24px"><path d="M18 4V3c0-.55-.45-1-1-1H5c-.55 0-1 .45-1 1v4c0 .55.45 1 1 1h12c.55 0 1-.45 1-1V6h1v4H9v11c0 .55.45 1 1 1h2c.55 0 1-.45 1-1v-9h8V4h-3z"/></symbol><symbol viewBox="0 0 24 24" id="ic_format_quote_24px"><path d="M6 17h3l2-4V7H5v6h3zm8 0h3l2-4V7h-6v6h3z"/></symbol><symbol viewBox="0 0 24 24" id="ic_format_shapes_24px"><path d="M23 7V1h-6v2H7V1H1v6h2v10H1v6h6v-2h10v2h6v-6h-2V7h2zM3 3h2v2H3V3zm2 18H3v-2h2v2zm12-2H7v-2H5V7h2V5h10v2h2v10h-2v2zm4 2h-2v-2h2v2zM19 5V3h2v2h-2zm-5.27 9h-3.49l-.73 2H7.89l3.4-9h1.4l3.41 9h-1.63l-.74-2zm-3.04-1.26h2.61L12 8.91l-1.31 3.83z"/></symbol><symbol viewBox="0 0 24 24" id="ic_format_size_24px"><path d="M9 4v3h5v12h3V7h5V4H9zm-6 8h3v7h3v-7h3V9H3v3z"/></symbol><symbol viewBox="0 0 24 24" id="ic_format_strikethrough_24px"><path d="M10 19h4v-3h-4v3zM5 4v3h5v3h4V7h5V4H5zM3 14h18v-2H3v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_format_textdirection_l_to_r_24px"><path d="M9 10v5h2V4h2v11h2V4h2V2H9C6.79 2 5 3.79 5 6s1.79 4 4 4zm12 8l-4-4v3H5v2h12v3l4-4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_format_textdirection_r_to_l_24px"><path d="M10 10v5h2V4h2v11h2V4h2V2h-8C7.79 2 6 3.79 6 6s1.79 4 4 4zm-2 7v-3l-4 4 4 4v-3h12v-2H8z"/></symbol><symbol viewBox="0 0 24 24" id="ic_format_underlined_24px"><path d="M12 17c3.31 0 6-2.69 6-6V3h-2.5v8c0 1.93-1.57 3.5-3.5 3.5S8.5 12.93 8.5 11V3H6v8c0 3.31 2.69 6 6 6zm-7 2v2h14v-2H5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_functions_24px"><path d="M18 4H6v2l6.5 6L6 18v2h12v-3h-7l5-5-5-5h7z"/></symbol><symbol viewBox="0 0 24 24" id="ic_highlight_24px"><path d="M6 14l3 3v5h6v-5l3-3V9H6zm5-12h2v3h-2zM3.5 5.875L4.914 4.46l2.12 2.122L5.62 7.997zm13.46.71l2.123-2.12 1.414 1.414L18.375 8z"/></symbol><symbol viewBox="0 0 24 24" id="ic_insert_chart_24px"><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM9 17H7v-7h2v7zm4 0h-2V7h2v10zm4 0h-2v-4h2v4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_insert_comment_24px"><path d="M20 2H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h14l4 4V4c0-1.1-.9-2-2-2zm-2 12H6v-2h12v2zm0-3H6V9h12v2zm0-3H6V6h12v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_insert_drive_file_24px"><path d="M6 2c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6H6zm7 7V3.5L18.5 9H13z"/></symbol><symbol viewBox="0 0 24 24" id="ic_insert_emoticon_24px"><path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm3.5-9c.83 0 1.5-.67 1.5-1.5S16.33 8 15.5 8 14 8.67 14 9.5s.67 1.5 1.5 1.5zm-7 0c.83 0 1.5-.67 1.5-1.5S9.33 8 8.5 8 7 8.67 7 9.5 7.67 11 8.5 11zm3.5 6.5c2.33 0 4.31-1.46 5.11-3.5H6.89c.8 2.04 2.78 3.5 5.11 3.5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_insert_invitation_24px"><path d="M17 12h-5v5h5v-5zM16 1v2H8V1H6v2H5c-1.11 0-1.99.9-1.99 2L3 19a2 2 0 0 0 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2h-1V1h-2zm3 18H5V8h14v11z"/></symbol><symbol viewBox="0 0 24 24" id="ic_insert_link_24px"><path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76 0 5-2.24 5-5s-2.24-5-5-5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_insert_photo_24px"><path d="M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_linear_scale_24px"><path d="M19.5 9.5c-1.03 0-1.9.62-2.29 1.5h-2.92c-.39-.88-1.26-1.5-2.29-1.5s-1.9.62-2.29 1.5H6.79c-.39-.88-1.26-1.5-2.29-1.5a2.5 2.5 0 0 0 0 5c1.03 0 1.9-.62 2.29-1.5h2.92c.39.88 1.26 1.5 2.29 1.5s1.9-.62 2.29-1.5h2.92c.39.88 1.26 1.5 2.29 1.5a2.5 2.5 0 0 0 0-5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_merge_type_24px"><path d="M17 20.41L18.41 19 15 15.59 13.59 17 17 20.41zM7.5 8H11v5.59L5.59 19 7 20.41l6-6V8h3.5L12 3.5 7.5 8z"/></symbol><symbol viewBox="0 0 24 24" id="ic_mode_comment_24px"><path d="M21.99 4c0-1.1-.89-2-1.99-2H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h14l4 4-.01-18z"/></symbol><symbol viewBox="0 0 24 24" id="ic_mode_edit_24px"><path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04a.996.996 0 0 0 0-1.41l-2.34-2.34a.996.996 0 0 0-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"/></symbol><symbol viewBox="0 0 24 24" id="ic_monetization_on_24px"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1.41 16.09V20h-2.67v-1.93c-1.71-.36-3.16-1.46-3.27-3.4h1.96c.1 1.05.82 1.87 2.65 1.87 1.96 0 2.4-.98 2.4-1.59 0-.83-.44-1.61-2.67-2.14-2.48-.6-4.18-1.62-4.18-3.67 0-1.72 1.39-2.84 3.11-3.21V4h2.67v1.95c1.86.45 2.79 1.86 2.85 3.39H14.3c-.05-1.11-.64-1.87-2.22-1.87-1.5 0-2.4.68-2.4 1.64 0 .84.65 1.39 2.67 1.91s4.18 1.39 4.18 3.91c-.01 1.83-1.38 2.83-3.12 3.16z"/></symbol><symbol viewBox="0 0 24 24" id="ic_money_off_24px"><path d="M12.5 6.9c1.78 0 2.44.85 2.5 2.1h2.21c-.07-1.72-1.12-3.3-3.21-3.81V3h-3v2.16c-.53.12-1.03.3-1.48.54l1.47 1.47c.41-.17.91-.27 1.51-.27zM5.33 4.06L4.06 5.33 7.5 8.77c0 2.08 1.56 3.21 3.91 3.91l3.51 3.51c-.34.48-1.05.91-2.42.91-2.06 0-2.87-.92-2.98-2.1h-2.2c.12 2.19 1.76 3.42 3.68 3.83V21h3v-2.15c.96-.18 1.82-.55 2.45-1.12l2.22 2.22 1.27-1.27L5.33 4.06z"/></symbol><symbol viewBox="0 0 24 24" id="ic_multiline_chart_24px"><path d="M22 6.92l-1.41-1.41-2.85 3.21C15.68 6.4 12.83 5 9.61 5 6.72 5 4.07 6.16 2 8l1.42 1.42C5.12 7.93 7.27 7 9.61 7c2.74 0 5.09 1.26 6.77 3.24l-2.88 3.24-4-4L2 16.99l1.5 1.5 6-6.01 4 4 4.05-4.55c.75 1.35 1.25 2.9 1.44 4.55H21c-.22-2.3-.95-4.39-2.04-6.14L22 6.92z"/></symbol><symbol viewBox="0 0 24 24" id="ic_pie_chart_24px"><path d="M11 2v20c-5.07-.5-9-4.79-9-10s3.93-9.5 9-10zm2.03 0v8.99H22c-.47-4.74-4.24-8.52-8.97-8.99zm0 11.01V22c4.74-.47 8.5-4.25 8.97-8.99h-8.97z"/></symbol><symbol viewBox="0 0 24 24" id="ic_pie_chart_outlined_24px"><path d="M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10 10-4.5 10-10S17.5 2 12 2zm1 2.07c3.61.45 6.48 3.33 6.93 6.93H13V4.07zM4 12c0-4.06 3.07-7.44 7-7.93v15.87c-3.93-.5-7-3.88-7-7.94zm9 7.93V13h6.93A8.002 8.002 0 0 1 13 19.93z"/></symbol><symbol viewBox="0 0 24 24" id="ic_publish_24px"><path d="M5 4v2h14V4H5zm0 10h4v6h6v-6h4l-7-7-7 7z"/></symbol><symbol viewBox="0 0 24 24" id="ic_short_text_24px"><path d="M4 9h16v2H4zm0 4h10v2H4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_show_chart_24px"><path d="M3.5 18.49l6-6.01 4 4L22 6.92l-1.41-1.41-7.09 7.97-4-4L2 16.99z"/></symbol><symbol viewBox="0 0 24 24" id="ic_space_bar_24px"><path d="M18 9v4H6V9H4v6h16V9z"/></symbol><symbol viewBox="0 0 24 24" id="ic_strikethrough_s_24px"><path fill="#010101" d="M7.24 8.75c-.26-.48-.39-1.03-.39-1.67 0-.61.13-1.16.4-1.67.26-.5.63-.93 1.11-1.29a5.73 5.73 0 0 1 1.7-.83c.66-.19 1.39-.29 2.18-.29.81 0 1.54.11 2.21.34.66.22 1.23.54 1.69.94.47.4.83.88 1.08 1.43.25.55.38 1.15.38 1.81h-3.01c0-.31-.05-.59-.15-.85-.09-.27-.24-.49-.44-.68-.2-.19-.45-.33-.75-.44-.3-.1-.66-.16-1.06-.16-.39 0-.74.04-1.03.13-.29.09-.53.21-.72.36-.19.16-.34.34-.44.55-.1.21-.15.43-.15.66 0 .48.25.88.74 1.21.38.25.77.48 1.41.7H7.39c-.05-.08-.11-.17-.15-.25zM21 12v-2H3v2h9.62c.18.07.4.14.55.2.37.17.66.34.87.51.21.17.35.36.43.57.07.2.11.43.11.69 0 .23-.05.45-.14.66-.09.2-.23.38-.42.53-.19.15-.42.26-.71.35-.29.08-.63.13-1.01.13-.43 0-.83-.04-1.18-.13s-.66-.23-.91-.42a1.92 1.92 0 0 1-.59-.75c-.14-.31-.25-.76-.25-1.21H6.4c0 .55.08 1.13.24 1.58.16.45.37.85.65 1.21.28.35.6.66.98.92.37.26.78.48 1.22.65.44.17.9.3 1.38.39.48.08.96.13 1.44.13.8 0 1.53-.09 2.18-.28s1.21-.45 1.67-.79c.46-.34.82-.77 1.07-1.27s.38-1.07.38-1.71c0-.6-.1-1.14-.31-1.61-.05-.11-.11-.23-.17-.33H21z"/></symbol><symbol viewBox="0 0 24 24" id="ic_text_fields_24px"><path d="M2.5 4v3h5v12h3V7h5V4h-13zm19 5h-9v3h3v7h3v-7h3V9z"/></symbol><symbol viewBox="0 0 24 24" id="ic_title_24px"><path d="M5 4v3h5.5v12h3V7H19V4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_vertical_align_bottom_24px"><path d="M16 13h-3V3h-2v10H8l4 4 4-4zM4 19v2h16v-2H4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_vertical_align_center_24px"><path d="M8 19h3v4h2v-4h3l-4-4-4 4zm8-14h-3V1h-2v4H8l4 4 4-4zM4 11v2h16v-2H4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_vertical_align_top_24px"><path d="M8 11h3v10h2V11h3l-4-4-4 4zM4 3v2h16V3H4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_wrap_text_24px"><path d="M4 19h6v-2H4v2zM20 5H4v2h16V5zm-3 6H4v2h13.25c1.1 0 2 .9 2 2s-.9 2-2 2H15v-2l-3 3 3 3v-2h2c2.21 0 4-1.79 4-4s-1.79-4-4-4z"/></symbol></svg>
\ 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 0000000..01d7d4d
--- /dev/null
+++ b/skyquake/framework/style/icons/svg-sprite-file-symbol.svg
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><symbol viewBox="0 0 24 24" id="ic_attachment_24px"><path d="M2 12.5C2 9.46 4.46 7 7.5 7H18c2.21 0 4 1.79 4 4s-1.79 4-4 4H9.5a2.5 2.5 0 0 1 0-5H17v2H9.41c-.55 0-.55 1 0 1H18c1.1 0 2-.9 2-2s-.9-2-2-2H7.5C5.57 9 4 10.57 4 12.5S5.57 16 7.5 16H17v2H7.5C4.46 18 2 15.54 2 12.5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_cloud_24px"><path d="M19.35 10.04A7.49 7.49 0 0 0 12 4C9.11 4 6.6 5.64 5.35 8.04A5.994 5.994 0 0 0 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96z"/></symbol><symbol viewBox="0 0 24 24" id="ic_cloud_circle_24px"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm4.5 14H8c-1.66 0-3-1.34-3-3s1.34-3 3-3l.14.01A3.98 3.98 0 0 1 12 7a4 4 0 0 1 4 4h.5a2.5 2.5 0 0 1 0 5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_cloud_done_24px"><path d="M19.35 10.04A7.49 7.49 0 0 0 12 4C9.11 4 6.6 5.64 5.35 8.04A5.994 5.994 0 0 0 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96zM10 17l-3.5-3.5 1.41-1.41L10 14.17 15.18 9l1.41 1.41L10 17z"/></symbol><symbol viewBox="0 0 24 24" id="ic_cloud_download_24px"><path d="M19.35 10.04A7.49 7.49 0 0 0 12 4C9.11 4 6.6 5.64 5.35 8.04A5.994 5.994 0 0 0 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96zM17 13l-5 5-5-5h3V9h4v4h3z"/></symbol><symbol viewBox="0 0 24 24" id="ic_cloud_off_24px"><path d="M19.35 10.04A7.49 7.49 0 0 0 12 4c-1.48 0-2.85.43-4.01 1.17l1.46 1.46a5.497 5.497 0 0 1 8.05 4.87v.5H19c1.66 0 3 1.34 3 3 0 1.13-.64 2.11-1.56 2.62l1.45 1.45C23.16 18.16 24 16.68 24 15c0-2.64-2.05-4.78-4.65-4.96zM3 5.27l2.75 2.74C2.56 8.15 0 10.77 0 14c0 3.31 2.69 6 6 6h11.73l2 2L21 20.73 4.27 4 3 5.27zM7.73 10l8 8H6c-2.21 0-4-1.79-4-4s1.79-4 4-4h1.73z"/></symbol><symbol viewBox="0 0 24 24" id="ic_cloud_queue_24px"><path d="M19.35 10.04A7.49 7.49 0 0 0 12 4C9.11 4 6.6 5.64 5.35 8.04A5.994 5.994 0 0 0 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96zM19 18H6c-2.21 0-4-1.79-4-4s1.79-4 4-4h.71A5.5 5.5 0 0 1 12 6c3.04 0 5.5 2.46 5.5 5.5v.5H19c1.66 0 3 1.34 3 3s-1.34 3-3 3z"/></symbol><symbol viewBox="0 0 24 24" id="ic_cloud_upload_24px"><path d="M19.35 10.04A7.49 7.49 0 0 0 12 4C9.11 4 6.6 5.64 5.35 8.04A5.994 5.994 0 0 0 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96zM14 13v4h-4v-4H7l5-5 5 5h-3z"/></symbol><symbol viewBox="0 0 24 24" id="ic_create_new_folder_24px"><path d="M20 6h-8l-2-2H4c-1.11 0-1.99.89-1.99 2L2 18c0 1.11.89 2 2 2h16c1.11 0 2-.89 2-2V8c0-1.11-.89-2-2-2zm-1 8h-3v3h-2v-3h-3v-2h3V9h2v3h3v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_file_download_24px"><path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_file_upload_24px"><path d="M9 16h6v-6h4l-7-7-7 7h4zm-4 2h14v2H5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_folder_24px"><path d="M10 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_folder_open_24px"><path d="M20 6h-8l-2-2H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2zm0 12H4V8h16v10z"/></symbol><symbol viewBox="0 0 24 24" id="ic_folder_shared_24px"><path d="M20 6h-8l-2-2H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2zm-5 3c1.1 0 2 .9 2 2s-.9 2-2 2-2-.9-2-2 .9-2 2-2zm4 8h-8v-1c0-1.33 2.67-2 4-2s4 .67 4 2v1z"/></symbol></svg>
\ 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 0000000..7f5d14c
--- /dev/null
+++ b/skyquake/framework/style/icons/svg-sprite-hardware-symbol.svg
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><symbol viewBox="0 0 24 24" id="ic_cast_24px"><path d="M21 3H3c-1.1 0-2 .9-2 2v3h2V5h18v14h-7v2h7c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM1 18v3h3c0-1.66-1.34-3-3-3zm0-4v2c2.76 0 5 2.24 5 5h2c0-3.87-3.13-7-7-7zm0-4v2a9 9 0 0 1 9 9h2c0-6.08-4.93-11-11-11z"/></symbol><symbol viewBox="0 0 24 24" id="ic_cast_connected_24px"><path d="M1 18v3h3c0-1.66-1.34-3-3-3zm0-4v2c2.76 0 5 2.24 5 5h2c0-3.87-3.13-7-7-7zm18-7H5v1.63c3.96 1.28 7.09 4.41 8.37 8.37H19V7zM1 10v2a9 9 0 0 1 9 9h2c0-6.08-4.93-11-11-11zm20-7H3c-1.1 0-2 .9-2 2v3h2V5h18v14h-7v2h7c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_computer_24px"><path d="M20 18c1.1 0 1.99-.9 1.99-2L22 6c0-1.1-.9-2-2-2H4c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2H0v2h24v-2h-4zM4 6h16v10H4V6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_desktop_mac_24px"><path d="M21 2H3c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h7l-2 3v1h8v-1l-2-3h7c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 12H3V4h18v10z"/></symbol><symbol viewBox="0 0 24 24" id="ic_desktop_windows_24px"><path d="M21 2H3c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h7v2H8v2h8v-2h-2v-2h7c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H3V4h18v12z"/></symbol><symbol viewBox="0 0 24 24" id="ic_developer_board_24px"><path d="M22 9V7h-2V5c0-1.1-.9-2-2-2H4c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2v-2h2v-2h-2v-2h2v-2h-2V9h2zm-4 10H4V5h14v14zM6 13h5v4H6zm6-6h4v3h-4zM6 7h5v5H6zm6 4h4v6h-4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_device_hub_24px"><path d="M17 16l-4-4V8.82C14.16 8.4 15 7.3 15 6c0-1.66-1.34-3-3-3S9 4.34 9 6c0 1.3.84 2.4 2 2.82V12l-4 4H3v5h5v-3.05l4-4.2 4 4.2V21h5v-5h-4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_devices_other_24px"><path d="M3 6h18V4H3c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h4v-2H3V6zm10 6H9v1.78c-.61.55-1 1.33-1 2.22s.39 1.67 1 2.22V20h4v-1.78c.61-.55 1-1.34 1-2.22s-.39-1.67-1-2.22V12zm-2 5.5c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zM22 8h-6c-.5 0-1 .5-1 1v10c0 .5.5 1 1 1h6c.5 0 1-.5 1-1V9c0-.5-.5-1-1-1zm-1 10h-4v-8h4v8z"/></symbol><symbol viewBox="0 0 24 24" id="ic_dock_24px"><path d="M8 23h8v-2H8v2zm8-21.99L8 1c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V3c0-1.1-.9-1.99-2-1.99zM16 15H8V5h8v10z"/></symbol><symbol viewBox="0 0 24 24" id="ic_gamepad_24px"><path d="M15 7.5V2H9v5.5l3 3 3-3zM7.5 9H2v6h5.5l3-3-3-3zM9 16.5V22h6v-5.5l-3-3-3 3zM16.5 9l-3 3 3 3H22V9h-5.5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_headset_24px"><path d="M12 1a9 9 0 0 0-9 9v7c0 1.66 1.34 3 3 3h3v-8H5v-2c0-3.87 3.13-7 7-7s7 3.13 7 7v2h-4v8h3c1.66 0 3-1.34 3-3v-7a9 9 0 0 0-9-9z"/></symbol><symbol viewBox="0 0 24 24" id="ic_headset_mic_24px"><path d="M12 1a9 9 0 0 0-9 9v7c0 1.66 1.34 3 3 3h3v-8H5v-2c0-3.87 3.13-7 7-7s7 3.13 7 7v2h-4v8h4v1h-7v2h6c1.66 0 3-1.34 3-3V10a9 9 0 0 0-9-9z"/></symbol><symbol viewBox="0 0 24 24" id="ic_keyboard_24px"><path d="M20 5H4c-1.1 0-1.99.9-1.99 2L2 17c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm-9 3h2v2h-2V8zm0 3h2v2h-2v-2zM8 8h2v2H8V8zm0 3h2v2H8v-2zm-1 2H5v-2h2v2zm0-3H5V8h2v2zm9 7H8v-2h8v2zm0-4h-2v-2h2v2zm0-3h-2V8h2v2zm3 3h-2v-2h2v2zm0-3h-2V8h2v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_keyboard_arrow_down_24px"><path d="M7.41 7.84L12 12.42l4.59-4.58L18 9.25l-6 6-6-6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_keyboard_arrow_left_24px"><path d="M15.41 16.09l-4.58-4.59 4.58-4.59L14 5.5l-6 6 6 6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_keyboard_arrow_right_24px"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_keyboard_arrow_up_24px"><path d="M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_keyboard_backspace_24px"><path d="M21 11H6.83l3.58-3.59L9 6l-6 6 6 6 1.41-1.41L6.83 13H21z"/></symbol><symbol viewBox="0 0 24 24" id="ic_keyboard_capslock_24px"><path d="M12 8.41L16.59 13 18 11.59l-6-6-6 6L7.41 13 12 8.41zM6 18h12v-2H6v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_keyboard_hide_24px"><path d="M20 3H4c-1.1 0-1.99.9-1.99 2L2 15c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-9 3h2v2h-2V6zm0 3h2v2h-2V9zM8 6h2v2H8V6zm0 3h2v2H8V9zm-1 2H5V9h2v2zm0-3H5V6h2v2zm9 7H8v-2h8v2zm0-4h-2V9h2v2zm0-3h-2V6h2v2zm3 3h-2V9h2v2zm0-3h-2V6h2v2zm-7 15l4-4H8l4 4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_keyboard_return_24px"><path d="M19 7v4H5.83l3.58-3.59L8 6l-6 6 6 6 1.41-1.41L5.83 13H21V7z"/></symbol><symbol viewBox="0 0 24 24" id="ic_keyboard_tab_24px"><path d="M11.59 7.41L15.17 11H1v2h14.17l-3.59 3.59L13 18l6-6-6-6-1.41 1.41zM20 6v12h2V6h-2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_keyboard_voice_24px"><path d="M12 15c1.66 0 2.99-1.34 2.99-3L15 6c0-1.66-1.34-3-3-3S9 4.34 9 6v6c0 1.66 1.34 3 3 3zm5.3-3c0 3-2.54 5.1-5.3 5.1S6.7 15 6.7 12H5c0 3.42 2.72 6.23 6 6.72V22h2v-3.28c3.28-.48 6-3.3 6-6.72h-1.7z"/></symbol><symbol viewBox="0 0 24 24" id="ic_laptop_24px"><path d="M20 18c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2H4c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2H0v2h24v-2h-4zM4 6h16v10H4V6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_laptop_chromebook_24px"><path d="M22 18V3H2v15H0v2h24v-2h-2zm-8 0h-4v-1h4v1zm6-3H4V5h16v10z"/></symbol><symbol viewBox="0 0 24 24" id="ic_laptop_mac_24px"><path d="M20 18c1.1 0 1.99-.9 1.99-2L22 5c0-1.1-.9-2-2-2H4c-1.1 0-2 .9-2 2v11c0 1.1.9 2 2 2H0c0 1.1.9 2 2 2h20c1.1 0 2-.9 2-2h-4zM4 5h16v11H4V5zm8 14c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1z"/></symbol><symbol viewBox="0 0 24 24" id="ic_laptop_windows_24px"><path d="M20 18v-1c1.1 0 1.99-.9 1.99-2L22 5c0-1.1-.9-2-2-2H4c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2v1H0v2h24v-2h-4zM4 5h16v10H4V5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_memory_24px"><path d="M15 9H9v6h6V9zm-2 4h-2v-2h2v2zm8-2V9h-2V7c0-1.1-.9-2-2-2h-2V3h-2v2h-2V3H9v2H7c-1.1 0-2 .9-2 2v2H3v2h2v2H3v2h2v2c0 1.1.9 2 2 2h2v2h2v-2h2v2h2v-2h2c1.1 0 2-.9 2-2v-2h2v-2h-2v-2h2zm-4 6H7V7h10v10z"/></symbol><symbol viewBox="0 0 24 24" id="ic_mouse_24px"><path d="M13 1.07V9h7c0-4.08-3.05-7.44-7-7.93zM4 15c0 4.42 3.58 8 8 8s8-3.58 8-8v-4H4v4zm7-13.93C7.05 1.56 4 4.92 4 9h7V1.07z"/></symbol><symbol viewBox="0 0 24 24" id="ic_phone_android_24px"><path d="M16 1H8C6.34 1 5 2.34 5 4v16c0 1.66 1.34 3 3 3h8c1.66 0 3-1.34 3-3V4c0-1.66-1.34-3-3-3zm-2 20h-4v-1h4v1zm3.25-3H6.75V4h10.5v14z"/></symbol><symbol viewBox="0 0 24 24" id="ic_phone_iphone_24px"><path d="M15.5 1h-8A2.5 2.5 0 0 0 5 3.5v17A2.5 2.5 0 0 0 7.5 23h8a2.5 2.5 0 0 0 2.5-2.5v-17A2.5 2.5 0 0 0 15.5 1zm-4 21c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zm4.5-4H7V4h9v14z"/></symbol><symbol viewBox="0 0 24 24" id="ic_phonelink_24px"><path d="M4 6h18V4H4c-1.1 0-2 .9-2 2v11H0v3h14v-3H4V6zm19 2h-6c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h6c.55 0 1-.45 1-1V9c0-.55-.45-1-1-1zm-1 9h-4v-7h4v7z"/></symbol><symbol viewBox="0 0 24 24" id="ic_phonelink_off_24px"><path d="M22 6V4H6.82l2 2H22zM1.92 1.65L.65 2.92l1.82 1.82C2.18 5.08 2 5.52 2 6v11H0v3h17.73l2.35 2.35 1.27-1.27L3.89 3.62 1.92 1.65zM4 6.27L14.73 17H4V6.27zM23 8h-6c-.55 0-1 .45-1 1v4.18l2 2V10h4v7h-2.18l3 3H23c.55 0 1-.45 1-1V9c0-.55-.45-1-1-1z"/></symbol><symbol viewBox="0 0 24 24" id="ic_power_input_24px"><path d="M2 9v2h19V9H2zm0 6h5v-2H2v2zm7 0h5v-2H9v2zm7 0h5v-2h-5v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_router_24px"><path d="M20.2 5.9l.8-.8C19.6 3.7 17.8 3 16 3s-3.6.7-5 2.1l.8.8C13 4.8 14.5 4.2 16 4.2s3 .6 4.2 1.7zm-.9.8c-.9-.9-2.1-1.4-3.3-1.4s-2.4.5-3.3 1.4l.8.8c.7-.7 1.6-1 2.5-1 .9 0 1.8.3 2.5 1l.8-.8zM19 13h-2V9h-2v4H5c-1.1 0-2 .9-2 2v4c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2v-4c0-1.1-.9-2-2-2zM8 18H6v-2h2v2zm3.5 0h-2v-2h2v2zm3.5 0h-2v-2h2v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_scanner_24px"><path d="M19.8 10.7L4.2 5l-.7 1.9L17.6 12H5c-1.1 0-2 .9-2 2v4c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2v-5.5c0-.8-.5-1.6-1.2-1.8zM7 17H5v-2h2v2zm12 0H9v-2h10v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_security_24px"><path d="M12 1L3 5v6c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V5l-9-4zm0 10.99h7c-.53 4.12-3.28 7.79-7 8.94V12H5V6.3l7-3.11v8.8z"/></symbol><symbol viewBox="0 0 24 24" id="ic_sim_card_24px"><path d="M19.99 4c0-1.1-.89-2-1.99-2h-8L4 8v12c0 1.1.9 2 2 2h12.01c1.1 0 1.99-.9 1.99-2l-.01-16zM9 19H7v-2h2v2zm8 0h-2v-2h2v2zm-8-4H7v-4h2v4zm4 4h-2v-4h2v4zm0-6h-2v-2h2v2zm4 2h-2v-4h2v4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_smartphone_24px"><path d="M17 1.01L7 1c-1.1 0-2 .9-2 2v18c0 1.1.9 2 2 2h10c1.1 0 2-.9 2-2V3c0-1.1-.9-1.99-2-1.99zM17 19H7V5h10v14z"/></symbol><symbol viewBox="0 0 24 24" id="ic_speaker_24px"><path d="M17 2H7c-1.1 0-2 .9-2 2v16c0 1.1.9 1.99 2 1.99L17 22c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-5 2c1.1 0 2 .9 2 2s-.9 2-2 2a2 2 0 0 1 0-4zm0 16c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z"/></symbol><symbol viewBox="0 0 24 24" id="ic_speaker_group_24px"><path d="M18.2 1H9.8C8.81 1 8 1.81 8 2.8v14.4c0 .99.81 1.79 1.8 1.79l8.4.01c.99 0 1.8-.81 1.8-1.8V2.8c0-.99-.81-1.8-1.8-1.8zM14 3a2 2 0 1 1 0 4 2 2 0 0 1 0-4zm0 13.5c-2.21 0-4-1.79-4-4s1.79-4 4-4 4 1.79 4 4-1.79 4-4 4z"/><circle cx="14" cy="12.5" r="2.5"/><path d="M6 5H4v16a2 2 0 0 0 2 2h10v-2H6V5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_tablet_24px"><path d="M21 4H3c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h18c1.1 0 1.99-.9 1.99-2L23 6c0-1.1-.9-2-2-2zm-2 14H5V6h14v12z"/></symbol><symbol viewBox="0 0 24 24" id="ic_tablet_android_24px"><path d="M18 0H6C4.34 0 3 1.34 3 3v18c0 1.66 1.34 3 3 3h12c1.66 0 3-1.34 3-3V3c0-1.66-1.34-3-3-3zm-4 22h-4v-1h4v1zm5.25-3H4.75V3h14.5v16z"/></symbol><symbol viewBox="0 0 24 24" id="ic_tablet_mac_24px"><path d="M18.5 0h-14A2.5 2.5 0 0 0 2 2.5v19A2.5 2.5 0 0 0 4.5 24h14a2.5 2.5 0 0 0 2.5-2.5v-19A2.5 2.5 0 0 0 18.5 0zm-7 23c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zm7.5-4H4V3h15v16z"/></symbol><symbol viewBox="0 0 24 24" id="ic_toys_24px"><path d="M12 12c0-3 2.5-5.5 5.5-5.5S23 9 23 12H12zm0 0c0 3-2.5 5.5-5.5 5.5S1 15 1 12h11zm0 0c-3 0-5.5-2.5-5.5-5.5S9 1 12 1v11zm0 0c3 0 5.5 2.5 5.5 5.5S15 23 12 23V12z"/></symbol><symbol viewBox="0 0 24 24" id="ic_tv_24px"><path d="M21 3H3c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h5v2h8v-2h5c1.1 0 1.99-.9 1.99-2L23 5c0-1.1-.9-2-2-2zm0 14H3V5h18v12z"/></symbol><symbol viewBox="0 0 24 24" id="ic_videogame_asset_24px"><path d="M21 6H3c-1.1 0-2 .9-2 2v8c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2zm-10 7H8v3H6v-3H3v-2h3V8h2v3h3v2zm4.5 2c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zm4-3c-.83 0-1.5-.67-1.5-1.5S18.67 9 19.5 9s1.5.67 1.5 1.5-.67 1.5-1.5 1.5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_watch_24px"><path d="M20 12c0-2.54-1.19-4.81-3.04-6.27L16 0H8l-.95 5.73C5.19 7.19 4 9.45 4 12s1.19 4.81 3.05 6.27L8 24h8l.96-5.73A7.976 7.976 0 0 0 20 12zM6 12c0-3.31 2.69-6 6-6s6 2.69 6 6-2.69 6-6 6-6-2.69-6-6z"/></symbol></svg>
\ 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 0000000..5431358
--- /dev/null
+++ b/skyquake/framework/style/icons/svg-sprite-image-symbol.svg
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><symbol viewBox="0 0 24 24" id="ic_add_a_photo_24px"><path d="M3 4V1h2v3h3v2H5v3H3V6H0V4h3zm3 6V7h3V4h7l1.83 2H21c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H5c-1.1 0-2-.9-2-2V10h3zm7 9c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5zm-3.2-5c0 1.77 1.43 3.2 3.2 3.2s3.2-1.43 3.2-3.2-1.43-3.2-3.2-3.2-3.2 1.43-3.2 3.2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_add_to_photos_24px"><path d="M4 6H2v14c0 1.1.9 2 2 2h14v-2H4V6zm16-4H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-1 9h-4v4h-2v-4H9V9h4V5h2v4h4v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_adjust_24px"><path d="M12 2C6.49 2 2 6.49 2 12s4.49 10 10 10 10-4.49 10-10S17.51 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm3-8c0 1.66-1.34 3-3 3s-3-1.34-3-3 1.34-3 3-3 3 1.34 3 3z"/></symbol><symbol viewBox="0 0 24 24" id="ic_assistant_24px"><path d="M19 2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h4l3 3 3-3h4c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-5.12 10.88L12 17l-1.88-4.12L6 11l4.12-1.88L12 5l1.88 4.12L18 11l-4.12 1.88z"/></symbol><symbol viewBox="0 0 24 24" id="ic_assistant_photo_24px"><path d="M14.4 6L14 4H5v17h2v-7h5.6l.4 2h7V6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_audiotrack_24px"><path d="M12 3v9.28a4.39 4.39 0 0 0-1.5-.28C8.01 12 6 14.01 6 16.5S8.01 21 10.5 21c2.31 0 4.2-1.75 4.45-4H15V6h4V3h-7z"/></symbol><symbol viewBox="0 0 24 24" id="ic_blur_circular_24px"><path d="M10 9c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm0 4c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zM7 9.5c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zm3 7c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zm-3-3c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zm3-6c.28 0 .5-.22.5-.5s-.22-.5-.5-.5-.5.22-.5.5.22.5.5.5zM14 9c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm0-1.5c.28 0 .5-.22.5-.5s-.22-.5-.5-.5-.5.22-.5.5.22.5.5.5zm3 6c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zm0-4c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zM12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm2-3.5c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zm0-3.5c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1z"/></symbol><symbol viewBox="0 0 24 24" id="ic_blur_linear_24px"><path d="M5 17.5c.83 0 1.5-.67 1.5-1.5s-.67-1.5-1.5-1.5-1.5.67-1.5 1.5.67 1.5 1.5 1.5zM9 13c.55 0 1-.45 1-1s-.45-1-1-1-1 .45-1 1 .45 1 1 1zm0-4c.55 0 1-.45 1-1s-.45-1-1-1-1 .45-1 1 .45 1 1 1zM3 21h18v-2H3v2zM5 9.5c.83 0 1.5-.67 1.5-1.5S5.83 6.5 5 6.5 3.5 7.17 3.5 8 4.17 9.5 5 9.5zm0 4c.83 0 1.5-.67 1.5-1.5s-.67-1.5-1.5-1.5-1.5.67-1.5 1.5.67 1.5 1.5 1.5zM9 17c.55 0 1-.45 1-1s-.45-1-1-1-1 .45-1 1 .45 1 1 1zm8-.5c.28 0 .5-.22.5-.5s-.22-.5-.5-.5-.5.22-.5.5.22.5.5.5zM3 3v2h18V3H3zm14 5.5c.28 0 .5-.22.5-.5s-.22-.5-.5-.5-.5.22-.5.5.22.5.5.5zm0 4c.28 0 .5-.22.5-.5s-.22-.5-.5-.5-.5.22-.5.5.22.5.5.5zM13 9c.55 0 1-.45 1-1s-.45-1-1-1-1 .45-1 1 .45 1 1 1zm0 4c.55 0 1-.45 1-1s-.45-1-1-1-1 .45-1 1 .45 1 1 1zm0 4c.55 0 1-.45 1-1s-.45-1-1-1-1 .45-1 1 .45 1 1 1z"/></symbol><symbol viewBox="0 0 24 24" id="ic_blur_off_24px"><path d="M14 7c.55 0 1-.45 1-1s-.45-1-1-1-1 .45-1 1 .45 1 1 1zm-.2 4.48l.2.02c.83 0 1.5-.67 1.5-1.5s-.67-1.5-1.5-1.5-1.5.67-1.5 1.5l.02.2c.09.67.61 1.19 1.28 1.28zM14 3.5c.28 0 .5-.22.5-.5s-.22-.5-.5-.5-.5.22-.5.5.22.5.5.5zm-4 0c.28 0 .5-.22.5-.5s-.22-.5-.5-.5-.5.22-.5.5.22.5.5.5zm11 7c.28 0 .5-.22.5-.5s-.22-.5-.5-.5-.5.22-.5.5.22.5.5.5zM10 7c.55 0 1-.45 1-1s-.45-1-1-1-1 .45-1 1 .45 1 1 1zm8 8c.55 0 1-.45 1-1s-.45-1-1-1-1 .45-1 1 .45 1 1 1zm0-4c.55 0 1-.45 1-1s-.45-1-1-1-1 .45-1 1 .45 1 1 1zm0-4c.55 0 1-.45 1-1s-.45-1-1-1-1 .45-1 1 .45 1 1 1zm-4 13.5c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zM2.5 5.27l3.78 3.78L6 9c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1c0-.1-.03-.19-.06-.28l2.81 2.81c-.71.11-1.25.73-1.25 1.47 0 .83.67 1.5 1.5 1.5.74 0 1.36-.54 1.47-1.25l2.81 2.81A.875.875 0 0 0 14 17c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1c0-.1-.03-.19-.06-.28l3.78 3.78L20 20.23 3.77 4 2.5 5.27zM10 17c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm11-3.5c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zM6 13c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zM3 9.5c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zm7 11c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zM6 17c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm-3-3.5c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_blur_on_24px"><path d="M6 13c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm0 4c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm0-8c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm-3 .5c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zM6 5c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm15 5.5c.28 0 .5-.22.5-.5s-.22-.5-.5-.5-.5.22-.5.5.22.5.5.5zM14 7c.55 0 1-.45 1-1s-.45-1-1-1-1 .45-1 1 .45 1 1 1zm0-3.5c.28 0 .5-.22.5-.5s-.22-.5-.5-.5-.5.22-.5.5.22.5.5.5zm-11 10c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zm7 7c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zm0-17c.28 0 .5-.22.5-.5s-.22-.5-.5-.5-.5.22-.5.5.22.5.5.5zM10 7c.55 0 1-.45 1-1s-.45-1-1-1-1 .45-1 1 .45 1 1 1zm0 5.5c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5zm8 .5c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm0 4c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm0-8c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm0-4c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm3 8.5c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zM14 17c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm0 3.5c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zm-4-12c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5zm0 8.5c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm4-4.5c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5zm0-4c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_brightness_1_24px"><circle cx="12" cy="12" r="10"/></symbol><symbol viewBox="0 0 24 24" id="ic_brightness_2_24px"><path d="M10 2c-1.82 0-3.53.5-5 1.35C7.99 5.08 10 8.3 10 12s-2.01 6.92-5 8.65C6.47 21.5 8.18 22 10 22c5.52 0 10-4.48 10-10S15.52 2 10 2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_brightness_3_24px"><path d="M9 2c-1.05 0-2.05.16-3 .46 4.06 1.27 7 5.06 7 9.54 0 4.48-2.94 8.27-7 9.54.95.3 1.95.46 3 .46 5.52 0 10-4.48 10-10S14.52 2 9 2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_brightness_4_24px"><path d="M20 8.69V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12 20 8.69zM12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6c3.31 0 6 2.69 6 6s-2.69 6-6 6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_brightness_5_24px"><path d="M20 15.31L23.31 12 20 8.69V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69zM12 18c-3.31 0-6-2.69-6-6s2.69-6 6-6 6 2.69 6 6-2.69 6-6 6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_brightness_6_24px"><path d="M20 15.31L23.31 12 20 8.69V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69zM12 18V6c3.31 0 6 2.69 6 6s-2.69 6-6 6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_brightness_7_24px"><path d="M20 8.69V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12 20 8.69zM12 18c-3.31 0-6-2.69-6-6s2.69-6 6-6 6 2.69 6 6-2.69 6-6 6zm0-10c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_broken_image_24px"><path d="M21 5v6.59l-3-3.01-4 4.01-4-4-4 4-3-3.01V5c0-1.1.9-2 2-2h14c1.1 0 2 .9 2 2zm-3 6.42l3 3.01V19c0 1.1-.9 2-2 2H5c-1.1 0-2-.9-2-2v-6.58l3 2.99 4-4 4 4 4-3.99z"/></symbol><symbol viewBox="0 0 24 24" id="ic_brush_24px"><path d="M7 14c-1.66 0-3 1.34-3 3 0 1.31-1.16 2-2 2 .92 1.22 2.49 2 4 2a4 4 0 0 0 4-4c0-1.66-1.34-3-3-3zm13.71-9.37l-1.34-1.34a.996.996 0 0 0-1.41 0L9 12.25 11.75 15l8.96-8.96a.996.996 0 0 0 0-1.41z"/></symbol><symbol viewBox="0 0 24 24" id="ic_burst_mode_24px"><path d="M1 5h2v14H1zm4 0h2v14H5zm17 0H10c-.55 0-1 .45-1 1v12c0 .55.45 1 1 1h12c.55 0 1-.45 1-1V6c0-.55-.45-1-1-1zM11 17l2.5-3.15L15.29 16l2.5-3.22L21 17H11z"/></symbol><symbol viewBox="0 0 24 24" id="ic_camera_24px"><path d="M9.4 10.5l4.77-8.26a9.984 9.984 0 0 0-8.49 2.01l3.66 6.35.06-.1zM21.54 9c-.92-2.92-3.15-5.26-6-6.34L11.88 9h9.66zm.26 1h-7.49l.29.5 4.76 8.25A9.91 9.91 0 0 0 22 12c0-.69-.07-1.35-.2-2zM8.54 12l-3.9-6.75A9.958 9.958 0 0 0 2.2 14h7.49l-1.15-2zm-6.08 3c.92 2.92 3.15 5.26 6 6.34L12.12 15H2.46zm11.27 0l-3.9 6.76a9.984 9.984 0 0 0 8.49-2.01l-3.66-6.35-.93 1.6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_camera_alt_24px"><circle cx="12" cy="12" r="3.2"/><path d="M9 2L7.17 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2h-3.17L15 2H9zm3 15c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_camera_front_24px"><path d="M10 20H5v2h5v2l3-3-3-3v2zm4 0v2h5v-2h-5zM12 8c1.1 0 2-.9 2-2s-.9-2-2-2-1.99.9-1.99 2S10.9 8 12 8zm5-8H7C5.9 0 5 .9 5 2v14c0 1.1.9 2 2 2h10c1.1 0 2-.9 2-2V2c0-1.1-.9-2-2-2zM7 2h10v10.5c0-1.67-3.33-2.5-5-2.5s-5 .83-5 2.5V2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_camera_rear_24px"><path d="M10 20H5v2h5v2l3-3-3-3v2zm4 0v2h5v-2h-5zm3-20H7C5.9 0 5 .9 5 2v14c0 1.1.9 2 2 2h10c1.1 0 2-.9 2-2V2c0-1.1-.9-2-2-2zm-5 6c-1.11 0-2-.9-2-2s.89-2 1.99-2 2 .9 2 2C14 5.1 13.1 6 12 6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_camera_roll_24px"><path d="M14 5c0-1.1-.9-2-2-2h-1V2c0-.55-.45-1-1-1H6c-.55 0-1 .45-1 1v1H4c-1.1 0-2 .9-2 2v15c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2h8V5h-8zm-2 13h-2v-2h2v2zm0-9h-2V7h2v2zm4 9h-2v-2h2v2zm0-9h-2V7h2v2zm4 9h-2v-2h2v2zm0-9h-2V7h2v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_center_focus_strong_24px"><path d="M12 8c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm-7 7H3v4c0 1.1.9 2 2 2h4v-2H5v-4zM5 5h4V3H5c-1.1 0-2 .9-2 2v4h2V5zm14-2h-4v2h4v4h2V5c0-1.1-.9-2-2-2zm0 16h-4v2h4c1.1 0 2-.9 2-2v-4h-2v4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_center_focus_weak_24px"><path d="M5 15H3v4c0 1.1.9 2 2 2h4v-2H5v-4zM5 5h4V3H5c-1.1 0-2 .9-2 2v4h2V5zm14-2h-4v2h4v4h2V5c0-1.1-.9-2-2-2zm0 16h-4v2h4c1.1 0 2-.9 2-2v-4h-2v4zM12 8c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm0 6c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_collections_24px"><path d="M22 16V4c0-1.1-.9-2-2-2H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2zm-11-4l2.03 2.71L16 11l4 5H8l3-4zM2 6v14c0 1.1.9 2 2 2h14v-2H4V6H2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_collections_bookmark_24px"><path d="M4 6H2v14c0 1.1.9 2 2 2h14v-2H4V6zm16-4H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 10l-2.5-1.5L15 12V4h5v8z"/></symbol><symbol viewBox="0 0 24 24" id="ic_color_lens_24px"><path d="M12 3a9 9 0 0 0 0 18c.83 0 1.5-.67 1.5-1.5 0-.39-.15-.74-.39-1.01-.23-.26-.38-.61-.38-.99 0-.83.67-1.5 1.5-1.5H16c2.76 0 5-2.24 5-5 0-4.42-4.03-8-9-8zm-5.5 9c-.83 0-1.5-.67-1.5-1.5S5.67 9 6.5 9 8 9.67 8 10.5 7.33 12 6.5 12zm3-4C8.67 8 8 7.33 8 6.5S8.67 5 9.5 5s1.5.67 1.5 1.5S10.33 8 9.5 8zm5 0c-.83 0-1.5-.67-1.5-1.5S13.67 5 14.5 5s1.5.67 1.5 1.5S15.33 8 14.5 8zm3 4c-.83 0-1.5-.67-1.5-1.5S16.67 9 17.5 9s1.5.67 1.5 1.5-.67 1.5-1.5 1.5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_colorize_24px"><path d="M20.71 5.63l-2.34-2.34a.996.996 0 0 0-1.41 0l-3.12 3.12-1.93-1.91-1.41 1.41 1.42 1.42L3 16.25V21h4.75l8.92-8.92 1.42 1.42 1.41-1.41-1.92-1.92 3.12-3.12a1 1 0 0 0 .01-1.42zM6.92 19L5 17.08l8.06-8.06 1.92 1.92L6.92 19z"/></symbol><symbol viewBox="0 0 24 24" id="ic_compare_24px"><path d="M10 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h5v2h2V1h-2v2zm0 15H5l5-6v6zm9-15h-5v2h5v13l-5-6v9h5c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_control_point_24px"><path d="M13 7h-2v4H7v2h4v4h2v-4h4v-2h-4V7zm-1-5C6.49 2 2 6.49 2 12s4.49 10 10 10 10-4.49 10-10S17.51 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"/></symbol><symbol viewBox="0 0 24 24" id="ic_control_point_duplicate_24px"><path d="M16 8h-2v3h-3v2h3v3h2v-3h3v-2h-3zM2 12c0-2.79 1.64-5.2 4.01-6.32V3.52C2.52 4.76 0 8.09 0 12s2.52 7.24 6.01 8.48v-2.16A6.99 6.99 0 0 1 2 12zm13-9c-4.96 0-9 4.04-9 9s4.04 9 9 9 9-4.04 9-9-4.04-9-9-9zm0 16c-3.86 0-7-3.14-7-7s3.14-7 7-7 7 3.14 7 7-3.14 7-7 7z"/></symbol><symbol viewBox="0 0 24 24" id="ic_crop_16_9_24px"><path d="M19 6H5c-1.1 0-2 .9-2 2v8c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2zm0 10H5V8h14v8z"/></symbol><symbol viewBox="0 0 24 24" id="ic_crop_24px"><path d="M17 15h2V7c0-1.1-.9-2-2-2H9v2h8v8zM7 17V1H5v4H1v2h4v10c0 1.1.9 2 2 2h10v4h2v-4h4v-2H7z"/></symbol><symbol viewBox="0 0 24 24" id="ic_crop_3_2_24px"><path d="M19 4H5c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 14H5V6h14v12z"/></symbol><symbol viewBox="0 0 24 24" id="ic_crop_5_4_24px"><path d="M19 5H5c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 12H5V7h14v10z"/></symbol><symbol viewBox="0 0 24 24" id="ic_crop_7_5_24px"><path d="M19 7H5c-1.1 0-2 .9-2 2v6c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V9c0-1.1-.9-2-2-2zm0 8H5V9h14v6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_crop_din_24px"><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V5h14v14z"/></symbol><symbol viewBox="0 0 24 24" id="ic_crop_free_24px"><path d="M3 5v4h2V5h4V3H5c-1.1 0-2 .9-2 2zm2 10H3v4c0 1.1.9 2 2 2h4v-2H5v-4zm14 4h-4v2h4c1.1 0 2-.9 2-2v-4h-2v4zm0-16h-4v2h4v4h2V5c0-1.1-.9-2-2-2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_crop_landscape_24px"><path d="M19 5H5c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 12H5V7h14v10z"/></symbol><symbol viewBox="0 0 24 24" id="ic_crop_original_24px"><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V5h14v14zm-5.04-6.71l-2.75 3.54-1.96-2.36L6.5 17h11l-3.54-4.71z"/></symbol><symbol viewBox="0 0 24 24" id="ic_crop_portrait_24px"><path d="M17 3H7c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h10c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H7V5h10v14z"/></symbol><symbol viewBox="0 0 24 24" id="ic_crop_rotate_24px"><path d="M7.47 21.49C4.2 19.93 1.86 16.76 1.5 13H0c.51 6.16 5.66 11 11.95 11 .23 0 .44-.02.66-.03L8.8 20.15l-1.33 1.34zM12.05 0c-.23 0-.44.02-.66.04l3.81 3.81 1.33-1.33C19.8 4.07 22.14 7.24 22.5 11H24c-.51-6.16-5.66-11-11.95-11zM16 14h2V8a2 2 0 0 0-2-2h-6v2h6v6zm-8 2V4H6v2H4v2h2v8a2 2 0 0 0 2 2h8v2h2v-2h2v-2H8z"/></symbol><symbol viewBox="0 0 24 24" id="ic_crop_square_24px"><path d="M18 4H6c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 14H6V6h12v12z"/></symbol><symbol viewBox="0 0 24 24" id="ic_dehaze_24px"><path d="M2 15.5v2h20v-2H2zm0-5v2h20v-2H2zm0-5v2h20v-2H2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_details_24px"><path d="M3 4l9 16 9-16H3zm3.38 2h11.25L12 16 6.38 6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_edit_24px"><path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04a.996.996 0 0 0 0-1.41l-2.34-2.34a.996.996 0 0 0-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"/></symbol><symbol viewBox="0 0 24 24" id="ic_exposure_24px"><path d="M15 17v2h2v-2h2v-2h-2v-2h-2v2h-2v2h2zm5-15H4c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zM5 5h6v2H5V5zm15 15H4L20 4v16z"/></symbol><symbol viewBox="0 0 24 24" id="ic_exposure_neg_1_24px"><path d="M4 11v2h8v-2H4zm15 7h-2V7.38L14 8.4V6.7L18.7 5h.3v13z"/></symbol><symbol viewBox="0 0 24 24" id="ic_exposure_neg_2_24px"><path d="M15.05 16.29l2.86-3.07c.38-.39.72-.79 1.04-1.18.32-.39.59-.78.82-1.17.23-.39.41-.78.54-1.17s.19-.79.19-1.18c0-.53-.09-1.02-.27-1.46a2.94 2.94 0 0 0-.78-1.11c-.34-.31-.77-.54-1.26-.71A5.72 5.72 0 0 0 16.47 5c-.69 0-1.31.11-1.85.32-.54.21-1 .51-1.36.88-.37.37-.65.8-.84 1.3-.18.47-.27.97-.28 1.5h2.14c.01-.31.05-.6.13-.87.09-.29.23-.54.4-.75.18-.21.41-.37.68-.49.27-.12.6-.18.96-.18.31 0 .58.05.81.15.23.1.43.25.59.43.16.18.28.4.37.65.08.25.13.52.13.81 0 .22-.03.43-.08.65-.06.22-.15.45-.29.7-.14.25-.32.53-.56.83-.23.3-.52.65-.88 1.03l-4.17 4.55V18H21v-1.71h-5.95zM2 11v2h8v-2H2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_exposure_plus_1_24px"><path d="M10 7H8v4H4v2h4v4h2v-4h4v-2h-4V7zm10 11h-2V7.38L15 8.4V6.7L19.7 5h.3v13z"/></symbol><symbol viewBox="0 0 24 24" id="ic_exposure_plus_2_24px"><path d="M16.05 16.29l2.86-3.07c.38-.39.72-.79 1.04-1.18.32-.39.59-.78.82-1.17.23-.39.41-.78.54-1.17.13-.39.19-.79.19-1.18 0-.53-.09-1.02-.27-1.46a2.94 2.94 0 0 0-.78-1.11c-.34-.31-.77-.54-1.26-.71A5.72 5.72 0 0 0 17.47 5c-.69 0-1.31.11-1.85.32-.54.21-1 .51-1.36.88-.37.37-.65.8-.84 1.3-.18.47-.27.97-.28 1.5h2.14c.01-.31.05-.6.13-.87.09-.29.23-.54.4-.75.18-.21.41-.37.68-.49.27-.12.6-.18.96-.18.31 0 .58.05.81.15.23.1.43.25.59.43.16.18.28.4.37.65.08.25.13.52.13.81 0 .22-.03.43-.08.65-.06.22-.15.45-.29.7-.14.25-.32.53-.56.83-.23.3-.52.65-.88 1.03l-4.17 4.55V18H22v-1.71h-5.95zM8 7H6v4H2v2h4v4h2v-4h4v-2H8V7z"/></symbol><symbol viewBox="0 0 24 24" id="ic_exposure_zero_24px"><path d="M16.14 12.5c0 1-.1 1.85-.3 2.55-.2.7-.48 1.27-.83 1.7-.36.44-.79.75-1.3.95-.51.2-1.07.3-1.7.3-.62 0-1.18-.1-1.69-.3-.51-.2-.95-.51-1.31-.95-.36-.44-.65-1.01-.85-1.7-.2-.7-.3-1.55-.3-2.55v-2.04c0-1 .1-1.85.3-2.55.2-.7.48-1.26.84-1.69.36-.43.8-.74 1.31-.93C10.81 5.1 11.38 5 12 5c.63 0 1.19.1 1.7.29.51.19.95.5 1.31.93.36.43.64.99.84 1.69.2.7.3 1.54.3 2.55v2.04zm-2.11-2.36c0-.64-.05-1.18-.13-1.62-.09-.44-.22-.79-.4-1.06-.17-.27-.39-.46-.64-.58-.25-.13-.54-.19-.86-.19-.32 0-.61.06-.86.18s-.47.31-.64.58c-.17.27-.31.62-.4 1.06s-.13.98-.13 1.62v2.67c0 .64.05 1.18.14 1.62.09.45.23.81.4 1.09s.39.48.64.61.54.19.87.19c.33 0 .62-.06.87-.19s.46-.33.63-.61c.17-.28.3-.64.39-1.09.09-.45.13-.99.13-1.62v-2.66z"/></symbol><symbol viewBox="0 0 24 24" id="ic_filter_1_24px"><path d="M3 5H1v16c0 1.1.9 2 2 2h16v-2H3V5zm11 10h2V5h-4v2h2v8zm7-14H7c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2zm0 16H7V3h14v14z"/></symbol><symbol viewBox="0 0 24 24" id="ic_filter_24px"><path d="M15.96 10.29l-2.75 3.54-1.96-2.36L8.5 15h11l-3.54-4.71zM3 5H1v16c0 1.1.9 2 2 2h16v-2H3V5zm18-4H7c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2zm0 16H7V3h14v14z"/></symbol><symbol viewBox="0 0 24 24" id="ic_filter_2_24px"><path d="M3 5H1v16c0 1.1.9 2 2 2h16v-2H3V5zm18-4H7c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2zm0 16H7V3h14v14zm-4-4h-4v-2h2a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-4v2h4v2h-2a2 2 0 0 0-2 2v4h6v-2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_filter_3_24px"><path d="M21 1H7c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2zm0 16H7V3h14v14zM3 5H1v16c0 1.1.9 2 2 2h16v-2H3V5zm14 8v-1.5c0-.83-.67-1.5-1.5-1.5.83 0 1.5-.67 1.5-1.5V7a2 2 0 0 0-2-2h-4v2h4v2h-2v2h2v2h-4v2h4a2 2 0 0 0 2-2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_filter_4_24px"><path d="M3 5H1v16c0 1.1.9 2 2 2h16v-2H3V5zm12 10h2V5h-2v4h-2V5h-2v6h4v4zm6-14H7c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2zm0 16H7V3h14v14z"/></symbol><symbol viewBox="0 0 24 24" id="ic_filter_5_24px"><path d="M21 1H7c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2zm0 16H7V3h14v14zM3 5H1v16c0 1.1.9 2 2 2h16v-2H3V5zm14 8v-2a2 2 0 0 0-2-2h-2V7h4V5h-6v6h4v2h-4v2h4a2 2 0 0 0 2-2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_filter_6_24px"><path d="M3 5H1v16c0 1.1.9 2 2 2h16v-2H3V5zm18-4H7c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2zm0 16H7V3h14v14zm-8-2h2a2 2 0 0 0 2-2v-2a2 2 0 0 0-2-2h-2V7h4V5h-4a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2zm0-4h2v2h-2v-2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_filter_7_24px"><path d="M3 5H1v16c0 1.1.9 2 2 2h16v-2H3V5zm18-4H7c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2zm0 16H7V3h14v14zm-8-2l4-8V5h-6v2h4l-4 8h2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_filter_8_24px"><path d="M3 5H1v16c0 1.1.9 2 2 2h16v-2H3V5zm18-4H7c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2zm0 16H7V3h14v14zm-8-2h2a2 2 0 0 0 2-2v-1.5c0-.83-.67-1.5-1.5-1.5.83 0 1.5-.67 1.5-1.5V7a2 2 0 0 0-2-2h-2a2 2 0 0 0-2 2v1.5c0 .83.67 1.5 1.5 1.5-.83 0-1.5.67-1.5 1.5V13a2 2 0 0 0 2 2zm0-8h2v2h-2V7zm0 4h2v2h-2v-2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_filter_9_24px"><path d="M3 5H1v16c0 1.1.9 2 2 2h16v-2H3V5zm18-4H7c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2zm0 16H7V3h14v14zM15 5h-2a2 2 0 0 0-2 2v2a2 2 0 0 0 2 2h2v2h-4v2h4a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2zm0 4h-2V7h2v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_filter_9_plus_24px"><path d="M3 5H1v16c0 1.1.9 2 2 2h16v-2H3V5zm11 7V8a2 2 0 0 0-2-2h-1a2 2 0 0 0-2 2v1a2 2 0 0 0 2 2h1v1H9v2h3a2 2 0 0 0 2-2zm-3-3V8h1v1h-1zm10-8H7c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2zm0 8h-2V7h-2v2h-2v2h2v2h2v-2h2v6H7V3h14v6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_filter_b_and_w_24px"><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16l-7-8v8H5l7-8V5h7v14z"/></symbol><symbol viewBox="0 0 24 24" id="ic_filter_center_focus_24px"><path d="M5 15H3v4c0 1.1.9 2 2 2h4v-2H5v-4zM5 5h4V3H5c-1.1 0-2 .9-2 2v4h2V5zm14-2h-4v2h4v4h2V5c0-1.1-.9-2-2-2zm0 16h-4v2h4c1.1 0 2-.9 2-2v-4h-2v4zM12 9c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z"/></symbol><symbol viewBox="0 0 24 24" id="ic_filter_drama_24px"><path d="M19.35 10.04A7.49 7.49 0 0 0 12 4a7.48 7.48 0 0 0-6.64 4.04A5.996 5.996 0 0 0 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96zM19 18H6c-2.21 0-4-1.79-4-4s1.79-4 4-4 4 1.79 4 4h2c0-2.76-1.86-5.08-4.4-5.78C8.61 6.88 10.2 6 12 6c3.03 0 5.5 2.47 5.5 5.5v.5H19c1.65 0 3 1.35 3 3s-1.35 3-3 3z"/></symbol><symbol viewBox="0 0 24 24" id="ic_filter_frames_24px"><path d="M20 4h-4l-4-4-4 4H4c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 16H4V6h4.52l3.52-3.5L15.52 6H20v14zM18 8H6v10h12"/></symbol><symbol viewBox="0 0 24 24" id="ic_filter_hdr_24px"><path d="M14 6l-3.75 5 2.85 3.8-1.6 1.2C9.81 13.75 7 10 7 10l-6 8h22L14 6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_filter_none_24px"><path d="M3 5H1v16c0 1.1.9 2 2 2h16v-2H3V5zm18-4H7c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2zm0 16H7V3h14v14z"/></symbol><symbol viewBox="0 0 24 24" id="ic_filter_tilt_shift_24px"><path d="M11 4.07V2.05c-2.01.2-3.84 1-5.32 2.21L7.1 5.69A7.94 7.94 0 0 1 11 4.07zm7.32.19A9.95 9.95 0 0 0 13 2.05v2.02c1.46.18 2.79.76 3.9 1.62l1.42-1.43zM19.93 11h2.02c-.2-2.01-1-3.84-2.21-5.32L18.31 7.1a7.94 7.94 0 0 1 1.62 3.9zM5.69 7.1L4.26 5.68A9.95 9.95 0 0 0 2.05 11h2.02a7.94 7.94 0 0 1 1.62-3.9zM4.07 13H2.05c.2 2.01 1 3.84 2.21 5.32l1.43-1.43A7.868 7.868 0 0 1 4.07 13zM15 12c0-1.66-1.34-3-3-3s-3 1.34-3 3 1.34 3 3 3 3-1.34 3-3zm3.31 4.9l1.43 1.43a9.98 9.98 0 0 0 2.21-5.32h-2.02a7.945 7.945 0 0 1-1.62 3.89zM13 19.93v2.02c2.01-.2 3.84-1 5.32-2.21l-1.43-1.43c-1.1.86-2.43 1.44-3.89 1.62zm-7.32-.19A9.98 9.98 0 0 0 11 21.95v-2.02a7.94 7.94 0 0 1-3.9-1.62l-1.42 1.43z"/></symbol><symbol viewBox="0 0 24 24" id="ic_filter_vintage_24px"><path d="M18.7 12.4a6.06 6.06 0 0 0-.86-.4c.29-.11.58-.24.86-.4a6.012 6.012 0 0 0 3-5.19 6.007 6.007 0 0 0-6 0c-.28.16-.54.35-.78.54.05-.31.08-.63.08-.95 0-2.22-1.21-4.15-3-5.19C10.21 1.85 9 3.78 9 6c0 .32.03.64.08.95-.24-.2-.5-.39-.78-.55a6.008 6.008 0 0 0-6 0 5.97 5.97 0 0 0 3 5.19c.28.16.57.29.86.4-.29.11-.58.24-.86.4a6.012 6.012 0 0 0-3 5.19 6.007 6.007 0 0 0 6 0c.28-.16.54-.35.78-.54-.05.32-.08.64-.08.96 0 2.22 1.21 4.15 3 5.19 1.79-1.04 3-2.97 3-5.19 0-.32-.03-.64-.08-.95.24.2.5.38.78.54a6.008 6.008 0 0 0 6 0 6.012 6.012 0 0 0-3-5.19zM12 16c-2.21 0-4-1.79-4-4s1.79-4 4-4 4 1.79 4 4-1.79 4-4 4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_flare_24px"><path d="M7 11H1v2h6v-2zm2.17-3.24L7.05 5.64 5.64 7.05l2.12 2.12 1.41-1.41zM13 1h-2v6h2V1zm5.36 6.05l-1.41-1.41-2.12 2.12 1.41 1.41 2.12-2.12zM17 11v2h6v-2h-6zm-5-2c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3zm2.83 7.24l2.12 2.12 1.41-1.41-2.12-2.12-1.41 1.41zm-9.19.71l1.41 1.41 2.12-2.12-1.41-1.41-2.12 2.12zM11 23h2v-6h-2v6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_flash_auto_24px"><path d="M3 2v12h3v9l7-12H9l4-9H3zm16 0h-2l-3.2 9h1.9l.7-2h3.2l.7 2h1.9L19 2zm-2.15 5.65L18 4l1.15 3.65h-2.3z"/></symbol><symbol viewBox="0 0 24 24" id="ic_flash_off_24px"><path d="M3.27 3L2 4.27l5 5V13h3v9l3.58-6.14L17.73 20 19 18.73 3.27 3zM17 10h-4l4-8H7v2.18l8.46 8.46L17 10z"/></symbol><symbol viewBox="0 0 24 24" id="ic_flash_on_24px"><path d="M7 2v11h3v9l7-12h-4l4-8z"/></symbol><symbol viewBox="0 0 24 24" id="ic_flip_24px"><path d="M15 21h2v-2h-2v2zm4-12h2V7h-2v2zM3 5v14c0 1.1.9 2 2 2h4v-2H5V5h4V3H5c-1.1 0-2 .9-2 2zm16-2v2h2c0-1.1-.9-2-2-2zm-8 20h2V1h-2v22zm8-6h2v-2h-2v2zM15 5h2V3h-2v2zm4 8h2v-2h-2v2zm0 8c1.1 0 2-.9 2-2h-2v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_gradient_24px"><path d="M11 9h2v2h-2zm-2 2h2v2H9zm4 0h2v2h-2zm2-2h2v2h-2zM7 9h2v2H7zm12-6H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM9 18H7v-2h2v2zm4 0h-2v-2h2v2zm4 0h-2v-2h2v2zm2-7h-2v2h2v2h-2v-2h-2v2h-2v-2h-2v2H9v-2H7v2H5v-2h2v-2H5V5h14v6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_grain_24px"><path d="M10 12c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zM6 8c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 8c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm12-8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm-4 8c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm4-4c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm-4-4c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm-4-4c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_grid_off_24px"><path d="M8 4v1.45l2 2V4h4v4h-3.45l2 2H14v1.45l2 2V10h4v4h-3.45l2 2H20v1.45l2 2V4c0-1.1-.9-2-2-2H4.55l2 2H8zm8 0h4v4h-4V4zM1.27 1.27L0 2.55l2 2V20c0 1.1.9 2 2 2h15.46l2 2 1.27-1.27L1.27 1.27zM10 12.55L11.45 14H10v-1.45zm-6-6L5.45 8H4V6.55zM8 20H4v-4h4v4zm0-6H4v-4h3.45l.55.55V14zm6 6h-4v-4h3.45l.55.54V20zm2 0v-1.46L17.46 20H16z"/></symbol><symbol viewBox="0 0 24 24" id="ic_grid_on_24px"><path d="M20 2H4c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zM8 20H4v-4h4v4zm0-6H4v-4h4v4zm0-6H4V4h4v4zm6 12h-4v-4h4v4zm0-6h-4v-4h4v4zm0-6h-4V4h4v4zm6 12h-4v-4h4v4zm0-6h-4v-4h4v4zm0-6h-4V4h4v4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_hdr_off_24px"><path d="M17.5 15v-2h1.1l.9 2H21l-.9-2.1c.5-.2.9-.8.9-1.4v-1c0-.8-.7-1.5-1.5-1.5H16v4.9l1.1 1.1h.4zm0-4.5h2v1h-2v-1zm-4.5 0v.4l1.5 1.5v-1.9c0-.8-.7-1.5-1.5-1.5h-1.9l1.5 1.5h.4zm-3.5-1l-7-7-1.1 1L6.9 9h-.4v2h-2V9H3v6h1.5v-2.5h2V15H8v-4.9l1.5 1.5V15h3.4l7.6 7.6 1.1-1.1-12.1-12z"/></symbol><symbol viewBox="0 0 24 24" id="ic_hdr_on_24px"><path d="M21 11.5v-1c0-.8-.7-1.5-1.5-1.5H16v6h1.5v-2h1.1l.9 2H21l-.9-2.1c.5-.3.9-.8.9-1.4zm-1.5 0h-2v-1h2v1zm-13-.5h-2V9H3v6h1.5v-2.5h2V15H8V9H6.5v2zM13 9H9.5v6H13c.8 0 1.5-.7 1.5-1.5v-3c0-.8-.7-1.5-1.5-1.5zm0 4.5h-2v-3h2v3z"/></symbol><symbol viewBox="0 0 24 24" id="ic_hdr_strong_24px"><path d="M17 6c-3.31 0-6 2.69-6 6s2.69 6 6 6 6-2.69 6-6-2.69-6-6-6zM5 8c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm0 6c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_hdr_weak_24px"><path d="M5 8c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm12-2c-3.31 0-6 2.69-6 6s2.69 6 6 6 6-2.69 6-6-2.69-6-6-6zm0 10c-2.21 0-4-1.79-4-4s1.79-4 4-4 4 1.79 4 4-1.79 4-4 4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_healing_24px"><path d="M17.73 12.02l3.98-3.98a.996.996 0 0 0 0-1.41l-4.34-4.34a.996.996 0 0 0-1.41 0l-3.98 3.98L8 2.29a1 1 0 0 0-1.41 0L2.25 6.63a.996.996 0 0 0 0 1.41l3.98 3.98L2.25 16a.996.996 0 0 0 0 1.41l4.34 4.34c.39.39 1.02.39 1.41 0l3.98-3.98 3.98 3.98c.2.2.45.29.71.29.26 0 .51-.1.71-.29l4.34-4.34a.996.996 0 0 0 0-1.41l-3.99-3.98zM12 9c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zm-4.71 1.96L3.66 7.34l3.63-3.63 3.62 3.62-3.62 3.63zM10 13c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1zm2 2c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1zm2-4c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zm2.66 9.34l-3.63-3.62 3.63-3.63 3.62 3.62-3.62 3.63z"/></symbol><symbol viewBox="0 0 24 24" id="ic_image_24px"><path d="M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_image_aspect_ratio_24px"><path d="M16 10h-2v2h2v-2zm0 4h-2v2h2v-2zm-8-4H6v2h2v-2zm4 0h-2v2h2v-2zm8-6H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 14H4V6h16v12z"/></symbol><symbol viewBox="0 0 24 24" id="ic_iso_24px"><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM5.5 7.5h2v-2H9v2h2V9H9v2H7.5V9h-2V7.5zM19 19H5L19 5v14zm-2-2v-1.5h-5V17h5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_landscape_24px"><path d="M14 6l-3.75 5 2.85 3.8-1.6 1.2C9.81 13.75 7 10 7 10l-6 8h22L14 6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_leak_add_24px"><path d="M6 3H3v3c1.66 0 3-1.34 3-3zm8 0h-2a9 9 0 0 1-9 9v2c6.08 0 11-4.93 11-11zm-4 0H8c0 2.76-2.24 5-5 5v2c3.87 0 7-3.13 7-7zm0 18h2a9 9 0 0 1 9-9v-2c-6.07 0-11 4.93-11 11zm8 0h3v-3c-1.66 0-3 1.34-3 3zm-4 0h2c0-2.76 2.24-5 5-5v-2c-3.87 0-7 3.13-7 7z"/></symbol><symbol viewBox="0 0 24 24" id="ic_leak_remove_24px"><path d="M10 3H8c0 .37-.04.72-.12 1.06l1.59 1.59C9.81 4.84 10 3.94 10 3zM3 4.27l2.84 2.84C5.03 7.67 4.06 8 3 8v2c1.61 0 3.09-.55 4.27-1.46L8.7 9.97A8.99 8.99 0 0 1 3 12v2c2.71 0 5.19-.99 7.11-2.62l2.5 2.5A11.044 11.044 0 0 0 10 21h2c0-2.16.76-4.14 2.03-5.69l1.43 1.43A6.922 6.922 0 0 0 14 21h2c0-1.06.33-2.03.89-2.84L19.73 21 21 19.73 4.27 3 3 4.27zM14 3h-2c0 1.5-.37 2.91-1.02 4.16l1.46 1.46C13.42 6.98 14 5.06 14 3zm5.94 13.12c.34-.08.69-.12 1.06-.12v-2c-.94 0-1.84.19-2.66.52l1.6 1.6zm-4.56-4.56l1.46 1.46A8.98 8.98 0 0 1 21 12v-2c-2.06 0-3.98.58-5.62 1.56z"/></symbol><symbol viewBox="0 0 24 24" id="ic_lens_24px"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_linked_camera_24px"><circle cx="12" cy="14" r="3.2"/><path d="M16 3.33A4.67 4.67 0 0 1 20.67 8H22c0-3.31-2.69-6-6-6v1.33M16 6c1.11 0 2 .89 2 2h1.33A3.33 3.33 0 0 0 16 4.67V6"/><path d="M17 9c0-1.11-.89-2-2-2V4H9L7.17 6H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V9h-5zm-5 10c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_looks_24px"><path d="M12 10c-3.86 0-7 3.14-7 7h2c0-2.76 2.24-5 5-5s5 2.24 5 5h2c0-3.86-3.14-7-7-7zm0-4C5.93 6 1 10.93 1 17h2c0-4.96 4.04-9 9-9s9 4.04 9 9h2c0-6.07-4.93-11-11-11z"/></symbol><symbol viewBox="0 0 24 24" id="ic_looks_3_24px"><path d="M19.01 3h-14c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-4 7.5c0 .83-.67 1.5-1.5 1.5.83 0 1.5.67 1.5 1.5V15a2 2 0 0 1-2 2h-4v-2h4v-2h-2v-2h2V9h-4V7h4a2 2 0 0 1 2 2v1.5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_looks_4_24px"><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-4 14h-2v-4H9V7h2v4h2V7h2v10z"/></symbol><symbol viewBox="0 0 24 24" id="ic_looks_5_24px"><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-4 6h-4v2h2a2 2 0 0 1 2 2v2a2 2 0 0 1-2 2H9v-2h4v-2H9V7h6v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_looks_6_24px"><path d="M11 15h2v-2h-2v2zm8-12H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-4 6h-4v2h2a2 2 0 0 1 2 2v2a2 2 0 0 1-2 2h-2a2 2 0 0 1-2-2V9a2 2 0 0 1 2-2h4v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_looks_one_24px"><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-5 14h-2V9h-2V7h4v10z"/></symbol><symbol viewBox="0 0 24 24" id="ic_looks_two_24px"><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-4 8a2 2 0 0 1-2 2h-2v2h4v2H9v-4a2 2 0 0 1 2-2h2V9H9V7h4a2 2 0 0 1 2 2v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_loupe_24px"><path d="M13 7h-2v4H7v2h4v4h2v-4h4v-2h-4V7zm-1-5C6.49 2 2 6.49 2 12s4.49 10 10 10h8c1.1 0 2-.9 2-2v-8c0-5.51-4.49-10-10-10zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"/></symbol><symbol viewBox="0 0 24 24" id="ic_monochrome_photos_24px"><path d="M20 5h-3.2L15 3H9L7.2 5H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 14h-8v-1c-2.8 0-5-2.2-5-5s2.2-5 5-5V7h8v12zm-3-6c0-2.8-2.2-5-5-5v1.8c1.8 0 3.2 1.4 3.2 3.2s-1.4 3.2-3.2 3.2V18c2.8 0 5-2.2 5-5zm-8.2 0c0 1.8 1.4 3.2 3.2 3.2V9.8c-1.8 0-3.2 1.4-3.2 3.2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_movie_creation_24px"><path d="M18 4l2 4h-3l-2-4h-2l2 4h-3l-2-4H8l2 4H7L5 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V4h-4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_movie_filter_24px"><path d="M18 4l2 3h-3l-2-3h-2l2 3h-3l-2-3H8l2 3H7L5 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V4h-4zm-6.75 11.25L10 18l-1.25-2.75L6 14l2.75-1.25L10 10l1.25 2.75L14 14l-2.75 1.25zm5.69-3.31L16 14l-.94-2.06L13 11l2.06-.94L16 8l.94 2.06L19 11l-2.06.94z"/></symbol><symbol viewBox="0 0 24 24" id="ic_music_note_24px"><path d="M12 3v10.55c-.59-.34-1.27-.55-2-.55-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4V7h4V3h-6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_nature_24px"><path d="M13 16.12a7 7 0 0 0 6.17-6.95c0-3.87-3.13-7-7-7s-7 3.13-7 7A6.98 6.98 0 0 0 11 16.06V20H5v2h14v-2h-6v-3.88z"/></symbol><symbol viewBox="0 0 24 24" id="ic_nature_people_24px"><path d="M22.17 9.17c0-3.87-3.13-7-7-7s-7 3.13-7 7A6.98 6.98 0 0 0 14 16.06V20H6v-3h1v-4c0-.55-.45-1-1-1H3c-.55 0-1 .45-1 1v4h1v5h16v-2h-3v-3.88a7 7 0 0 0 6.17-6.95zM4.5 11c.83 0 1.5-.67 1.5-1.5S5.33 8 4.5 8 3 8.67 3 9.5 3.67 11 4.5 11z"/></symbol><symbol viewBox="0 0 24 24" id="ic_navigate_before_24px"><path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"/></symbol><symbol viewBox="0 0 24 24" id="ic_navigate_next_24px"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_palette_24px"><path d="M12 3a9 9 0 0 0 0 18c.83 0 1.5-.67 1.5-1.5 0-.39-.15-.74-.39-1.01-.23-.26-.38-.61-.38-.99 0-.83.67-1.5 1.5-1.5H16c2.76 0 5-2.24 5-5 0-4.42-4.03-8-9-8zm-5.5 9c-.83 0-1.5-.67-1.5-1.5S5.67 9 6.5 9 8 9.67 8 10.5 7.33 12 6.5 12zm3-4C8.67 8 8 7.33 8 6.5S8.67 5 9.5 5s1.5.67 1.5 1.5S10.33 8 9.5 8zm5 0c-.83 0-1.5-.67-1.5-1.5S13.67 5 14.5 5s1.5.67 1.5 1.5S15.33 8 14.5 8zm3 4c-.83 0-1.5-.67-1.5-1.5S16.67 9 17.5 9s1.5.67 1.5 1.5-.67 1.5-1.5 1.5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_panorama_24px"><path d="M23 18V6c0-1.1-.9-2-2-2H3c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2zM8.5 12.5l2.5 3.01L14.5 11l4.5 6H5l3.5-4.5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_panorama_fish_eye_24px"><path d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"/></symbol><symbol viewBox="0 0 24 24" id="ic_panorama_horizontal_24px"><path d="M20 6.54v10.91c-2.6-.77-5.28-1.16-8-1.16-2.72 0-5.4.39-8 1.16V6.54c2.6.77 5.28 1.16 8 1.16 2.72.01 5.4-.38 8-1.16M21.43 4c-.1 0-.2.02-.31.06C18.18 5.16 15.09 5.7 12 5.7c-3.09 0-6.18-.55-9.12-1.64A.94.94 0 0 0 2.57 4c-.34 0-.57.23-.57.63v14.75c0 .39.23.62.57.62.1 0 .2-.02.31-.06 2.94-1.1 6.03-1.64 9.12-1.64 3.09 0 6.18.55 9.12 1.64.11.04.21.06.31.06.33 0 .57-.23.57-.63V4.63c0-.4-.24-.63-.57-.63z"/></symbol><symbol viewBox="0 0 24 24" id="ic_panorama_vertical_24px"><path d="M19.94 21.12c-1.1-2.94-1.64-6.03-1.64-9.12 0-3.09.55-6.18 1.64-9.12a.94.94 0 0 0 .06-.31c0-.34-.23-.57-.63-.57H4.63c-.4 0-.63.23-.63.57 0 .1.02.2.06.31C5.16 5.82 5.71 8.91 5.71 12c0 3.09-.55 6.18-1.64 9.12-.05.11-.07.22-.07.31 0 .33.23.57.63.57h14.75c.39 0 .63-.24.63-.57-.01-.1-.03-.2-.07-.31zM6.54 20c.77-2.6 1.16-5.28 1.16-8 0-2.72-.39-5.4-1.16-8h10.91c-.77 2.6-1.16 5.28-1.16 8 0 2.72.39 5.4 1.16 8H6.54z"/></symbol><symbol viewBox="0 0 24 24" id="ic_panorama_wide_angle_24px"><path d="M12 6c2.45 0 4.71.2 7.29.64A21 21 0 0 1 20 12a21 21 0 0 1-.71 5.36c-2.58.44-4.84.64-7.29.64s-4.71-.2-7.29-.64A21 21 0 0 1 4 12a21 21 0 0 1 .71-5.36C7.29 6.2 9.55 6 12 6m0-2c-2.73 0-5.22.24-7.95.72l-.93.16-.25.9C2.29 7.85 2 9.93 2 12s.29 4.15.87 6.22l.25.89.93.16c2.73.49 5.22.73 7.95.73s5.22-.24 7.95-.72l.93-.16.25-.89c.58-2.08.87-4.16.87-6.23s-.29-4.15-.87-6.22l-.25-.89-.93-.16C17.22 4.24 14.73 4 12 4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_photo_24px"><path d="M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_photo_album_24px"><path d="M18 2H6c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zM6 4h5v8l-2.5-1.5L6 12V4zm0 15l3-3.86 2.14 2.58 3-3.86L18 19H6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_photo_camera_24px"><circle cx="12" cy="12" r="3.2"/><path d="M9 2L7.17 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2h-3.17L15 2H9zm3 15c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_photo_filter_24px"><path d="M19.02 10v9H5V5h9V3H5.02c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2v-9h-2zM17 10l.94-2.06L20 7l-2.06-.94L17 4l-.94 2.06L14 7l2.06.94zm-3.75.75L12 8l-1.25 2.75L8 12l2.75 1.25L12 16l1.25-2.75L16 12z"/></symbol><symbol viewBox="0 0 24 24" id="ic_photo_library_24px"><path d="M22 16V4c0-1.1-.9-2-2-2H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2zm-11-4l2.03 2.71L16 11l4 5H8l3-4zM2 6v14c0 1.1.9 2 2 2h14v-2H4V6H2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_photo_size_select_actual_24px"><path d="M21 3H3C2 3 1 4 1 5v14c0 1.1.9 2 2 2h18c1 0 2-1 2-2V5c0-1-1-2-2-2zM5 17l3.5-4.5 2.5 3.01L14.5 11l4.5 6H5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_photo_size_select_large_24px"><path d="M21 15h2v2h-2v-2zm0-4h2v2h-2v-2zm2 8h-2v2c1 0 2-1 2-2zM13 3h2v2h-2V3zm8 4h2v2h-2V7zm0-4v2h2c0-1-1-2-2-2zM1 7h2v2H1V7zm16-4h2v2h-2V3zm0 16h2v2h-2v-2zM3 3C2 3 1 4 1 5h2V3zm6 0h2v2H9V3zM5 3h2v2H5V3zm-4 8v8c0 1.1.9 2 2 2h12V11H1zm2 8l2.5-3.21 1.79 2.15 2.5-3.22L13 19H3z"/></symbol><symbol viewBox="0 0 24 24" id="ic_photo_size_select_small_24px"><path d="M23 15h-2v2h2v-2zm0-4h-2v2h2v-2zm0 8h-2v2c1 0 2-1 2-2zM15 3h-2v2h2V3zm8 4h-2v2h2V7zm-2-4v2h2c0-1-1-2-2-2zM3 21h8v-6H1v4c0 1.1.9 2 2 2zM3 7H1v2h2V7zm12 12h-2v2h2v-2zm4-16h-2v2h2V3zm0 16h-2v2h2v-2zM3 3C2 3 1 4 1 5h2V3zm0 8H1v2h2v-2zm8-8H9v2h2V3zM7 3H5v2h2V3z"/></symbol><symbol viewBox="0 0 24 24" id="ic_picture_as_pdf_24px"><path d="M20 2H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-8.5 7.5c0 .83-.67 1.5-1.5 1.5H9v2H7.5V7H10c.83 0 1.5.67 1.5 1.5v1zm5 2c0 .83-.67 1.5-1.5 1.5h-2.5V7H15c.83 0 1.5.67 1.5 1.5v3zm4-3H19v1h1.5V11H19v2h-1.5V7h3v1.5zM9 9.5h1v-1H9v1zM4 6H2v14c0 1.1.9 2 2 2h14v-2H4V6zm10 5.5h1v-3h-1v3z"/></symbol><symbol viewBox="0 0 24 24" id="ic_portrait_24px"><path d="M12 12.25c1.24 0 2.25-1.01 2.25-2.25S13.24 7.75 12 7.75 9.75 8.76 9.75 10s1.01 2.25 2.25 2.25zm4.5 4c0-1.5-3-2.25-4.5-2.25s-4.5.75-4.5 2.25V17h9v-.75zM19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V5h14v14z"/></symbol><symbol viewBox="0 0 24 24" id="ic_remove_red_eye_24px"><path d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z"/></symbol><symbol viewBox="0 0 24 24" id="ic_rotate_90_degrees_ccw_24px"><path d="M7.34 6.41L.86 12.9l6.49 6.48 6.49-6.48-6.5-6.49zM3.69 12.9l3.66-3.66L11 12.9l-3.66 3.66-3.65-3.66zm15.67-6.26A8.95 8.95 0 0 0 13 4V.76L8.76 5 13 9.24V6c1.79 0 3.58.68 4.95 2.05a7.007 7.007 0 0 1 0 9.9 6.973 6.973 0 0 1-7.79 1.44l-1.49 1.49C10.02 21.62 11.51 22 13 22c2.3 0 4.61-.88 6.36-2.64a8.98 8.98 0 0 0 0-12.72z"/></symbol><symbol viewBox="0 0 24 24" id="ic_rotate_left_24px"><path d="M7.11 8.53L5.7 7.11C4.8 8.27 4.24 9.61 4.07 11h2.02c.14-.87.49-1.72 1.02-2.47zM6.09 13H4.07c.17 1.39.72 2.73 1.62 3.89l1.41-1.42c-.52-.75-.87-1.59-1.01-2.47zm1.01 5.32c1.16.9 2.51 1.44 3.9 1.61V17.9c-.87-.15-1.71-.49-2.46-1.03L7.1 18.32zM13 4.07V1L8.45 5.55 13 10V6.09c2.84.48 5 2.94 5 5.91s-2.16 5.43-5 5.91v2.02c3.95-.49 7-3.85 7-7.93s-3.05-7.44-7-7.93z"/></symbol><symbol viewBox="0 0 24 24" id="ic_rotate_right_24px"><path d="M15.55 5.55L11 1v3.07C7.06 4.56 4 7.92 4 12s3.05 7.44 7 7.93v-2.02c-2.84-.48-5-2.94-5-5.91s2.16-5.43 5-5.91V10l4.55-4.45zM19.93 11a7.906 7.906 0 0 0-1.62-3.89l-1.42 1.42c.54.75.88 1.6 1.02 2.47h2.02zM13 17.9v2.02c1.39-.17 2.74-.71 3.9-1.61l-1.44-1.44c-.75.54-1.59.89-2.46 1.03zm3.89-2.42l1.42 1.41c.9-1.16 1.45-2.5 1.62-3.89h-2.02c-.14.87-.48 1.72-1.02 2.48z"/></symbol><symbol viewBox="0 0 24 24" id="ic_slideshow_24px"><path d="M10 8v8l5-4-5-4zm9-5H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V5h14v14z"/></symbol><symbol viewBox="0 0 24 24" id="ic_straighten_24px"><path d="M21 6H3c-1.1 0-2 .9-2 2v8c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2zm0 10H3V8h2v4h2V8h2v4h2V8h2v4h2V8h2v4h2V8h2v8z"/></symbol><symbol viewBox="0 0 24 24" id="ic_style_24px"><path d="M2.53 19.65l1.34.56v-9.03l-2.43 5.86a2.02 2.02 0 0 0 1.09 2.61zm19.5-3.7L17.07 3.98a2.013 2.013 0 0 0-1.81-1.23c-.26 0-.53.04-.79.15L7.1 5.95a2 2 0 0 0-1.08 2.6l4.96 11.97a1.998 1.998 0 0 0 2.6 1.08l7.36-3.05a1.994 1.994 0 0 0 1.09-2.6zM7.88 8.75c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1zm-2 11c0 1.1.9 2 2 2h1.45l-3.45-8.34v6.34z"/></symbol><symbol viewBox="0 0 24 24" id="ic_switch_camera_24px"><path d="M20 4h-3.17L15 2H9L7.17 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm-5 11.5V13H9v2.5L5.5 12 9 8.5V11h6V8.5l3.5 3.5-3.5 3.5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_switch_video_24px"><path d="M18 9.5V6c0-.55-.45-1-1-1H3c-.55 0-1 .45-1 1v12c0 .55.45 1 1 1h14c.55 0 1-.45 1-1v-3.5l4 4v-13l-4 4zm-5 6V13H7v2.5L3.5 12 7 8.5V11h6V8.5l3.5 3.5-3.5 3.5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_tag_faces_24px"><path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm3.5-9c.83 0 1.5-.67 1.5-1.5S16.33 8 15.5 8 14 8.67 14 9.5s.67 1.5 1.5 1.5zm-7 0c.83 0 1.5-.67 1.5-1.5S9.33 8 8.5 8 7 8.67 7 9.5 7.67 11 8.5 11zm3.5 6.5c2.33 0 4.31-1.46 5.11-3.5H6.89c.8 2.04 2.78 3.5 5.11 3.5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_texture_24px"><path d="M19.51 3.08L3.08 19.51c.09.34.27.65.51.9.25.24.56.42.9.51L20.93 4.49c-.19-.69-.73-1.23-1.42-1.41zM11.88 3L3 11.88v2.83L14.71 3h-2.83zM5 3c-1.1 0-2 .9-2 2v2l4-4H5zm14 18c.55 0 1.05-.22 1.41-.59.37-.36.59-.86.59-1.41v-2l-4 4h2zm-9.71 0h2.83L21 12.12V9.29L9.29 21z"/></symbol><symbol viewBox="0 0 24 24" id="ic_timelapse_24px"><path d="M16.24 7.76A5.974 5.974 0 0 0 12 6v6l-4.24 4.24c2.34 2.34 6.14 2.34 8.49 0a5.99 5.99 0 0 0-.01-8.48zM12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z"/></symbol><symbol viewBox="0 0 24 24" id="ic_timer_10_24px"><path d="M0 7.72V9.4l3-1V18h2V6h-.25L0 7.72zm23.78 6.65c-.14-.28-.35-.53-.63-.74-.28-.21-.61-.39-1.01-.53s-.85-.27-1.35-.38a6.64 6.64 0 0 1-.87-.23 2.61 2.61 0 0 1-.55-.25.717.717 0 0 1-.28-.3.978.978 0 0 1 .01-.8c.06-.13.15-.25.27-.34.12-.1.27-.18.45-.24s.4-.09.64-.09c.25 0 .47.04.66.11.19.07.35.17.48.29.13.12.22.26.29.42.06.16.1.32.1.49h1.95a2.517 2.517 0 0 0-.93-1.97c-.3-.25-.66-.44-1.09-.59C21.49 9.07 21 9 20.46 9c-.51 0-.98.07-1.39.21-.41.14-.77.33-1.06.57-.29.24-.51.52-.67.84-.16.32-.23.65-.23 1.01s.08.69.23.96c.15.28.36.52.64.73.27.21.6.38.98.53.38.14.81.26 1.27.36.39.08.71.17.95.26s.43.19.57.29c.13.1.22.22.27.34.05.12.07.25.07.39 0 .32-.13.57-.4.77-.27.2-.66.29-1.17.29-.22 0-.43-.02-.64-.08-.21-.05-.4-.13-.56-.24a1.333 1.333 0 0 1-.59-1.11h-1.89c0 .36.08.71.24 1.05.16.34.39.65.7.93.31.27.69.49 1.15.66.46.17.98.25 1.58.25.53 0 1.01-.06 1.44-.19.43-.13.8-.31 1.11-.54.31-.23.54-.51.71-.83.17-.32.25-.67.25-1.06-.02-.4-.09-.74-.24-1.02zm-9.96-7.32c-.34-.4-.75-.7-1.23-.88-.47-.18-1.01-.27-1.59-.27-.58 0-1.11.09-1.59.27-.48.18-.89.47-1.23.88-.34.41-.6.93-.79 1.59-.18.65-.28 1.45-.28 2.39v1.92c0 .94.09 1.74.28 2.39.19.66.45 1.19.8 1.6.34.41.75.71 1.23.89.48.18 1.01.28 1.59.28.59 0 1.12-.09 1.59-.28.48-.18.88-.48 1.22-.89.34-.41.6-.94.78-1.6.18-.65.28-1.45.28-2.39v-1.92c0-.94-.09-1.74-.28-2.39-.18-.66-.44-1.19-.78-1.59zm-.92 6.17c0 .6-.04 1.11-.12 1.53-.08.42-.2.76-.36 1.02-.16.26-.36.45-.59.57-.23.12-.51.18-.82.18-.3 0-.58-.06-.82-.18s-.44-.31-.6-.57c-.16-.26-.29-.6-.38-1.02-.09-.42-.13-.93-.13-1.53v-2.5c0-.6.04-1.11.13-1.52.09-.41.21-.74.38-1 .16-.25.36-.43.6-.55.24-.11.51-.17.81-.17.31 0 .58.06.81.17.24.11.44.29.6.55.16.25.29.58.37.99.08.41.13.92.13 1.52v2.51z"/></symbol><symbol viewBox="0 0 24 24" id="ic_timer_24px"><path d="M15 1H9v2h6V1zm-4 13h2V8h-2v6zm8.03-6.61l1.42-1.42c-.43-.51-.9-.99-1.41-1.41l-1.42 1.42A8.962 8.962 0 0 0 12 4c-4.97 0-9 4.03-9 9s4.02 9 9 9a8.994 8.994 0 0 0 7.03-14.61zM12 20c-3.87 0-7-3.13-7-7s3.13-7 7-7 7 3.13 7 7-3.13 7-7 7z"/></symbol><symbol viewBox="0 0 24 24" id="ic_timer_3_24px"><path d="M11.61 12.97c-.16-.24-.36-.46-.62-.65a3.38 3.38 0 0 0-.93-.48c.3-.14.57-.3.8-.5.23-.2.42-.41.57-.64.15-.23.27-.46.34-.71.08-.24.11-.49.11-.73 0-.55-.09-1.04-.28-1.46-.18-.42-.44-.77-.78-1.06-.33-.28-.73-.5-1.2-.64-.45-.13-.97-.2-1.53-.2-.55 0-1.06.08-1.52.24-.47.17-.87.4-1.2.69-.33.29-.6.63-.78 1.03-.2.39-.29.83-.29 1.29h1.98c0-.26.05-.49.14-.69.09-.2.22-.38.38-.52.17-.14.36-.25.58-.33.22-.08.46-.12.73-.12.61 0 1.06.16 1.36.47.3.31.44.75.44 1.32 0 .27-.04.52-.12.74-.08.22-.21.41-.38.57-.17.16-.38.28-.63.37-.25.09-.55.13-.89.13H6.72v1.57H7.9c.34 0 .64.04.91.11.27.08.5.19.69.35.19.16.34.36.44.61.1.24.16.54.16.87 0 .62-.18 1.09-.53 1.42-.35.33-.84.49-1.45.49-.29 0-.56-.04-.8-.13-.24-.08-.44-.2-.61-.36-.17-.16-.3-.34-.39-.56-.09-.22-.14-.46-.14-.72H4.19c0 .55.11 1.03.32 1.45.21.42.5.77.86 1.05s.77.49 1.24.63.96.21 1.48.21c.57 0 1.09-.08 1.58-.23.49-.15.91-.38 1.26-.68.36-.3.64-.66.84-1.1.2-.43.3-.93.3-1.48 0-.29-.04-.58-.11-.86-.08-.25-.19-.51-.35-.76zm9.26 1.4c-.14-.28-.35-.53-.63-.74-.28-.21-.61-.39-1.01-.53s-.85-.27-1.35-.38a6.64 6.64 0 0 1-.87-.23 2.61 2.61 0 0 1-.55-.25.717.717 0 0 1-.28-.3c-.05-.11-.08-.24-.08-.39a.946.946 0 0 1 .36-.75c.12-.1.27-.18.45-.24s.4-.09.64-.09c.25 0 .47.04.66.11.19.07.35.17.48.29.13.12.22.26.29.42.06.16.1.32.1.49h1.95a2.517 2.517 0 0 0-.93-1.97c-.3-.25-.66-.44-1.09-.59-.43-.15-.92-.22-1.46-.22-.51 0-.98.07-1.39.21-.41.14-.77.33-1.06.57-.29.24-.51.52-.67.84-.16.32-.23.65-.23 1.01s.08.68.23.96c.15.28.37.52.64.73.27.21.6.38.98.53.38.14.81.26 1.27.36.39.08.71.17.95.26s.43.19.57.29c.13.1.22.22.27.34.05.12.07.25.07.39 0 .32-.13.57-.4.77-.27.2-.66.29-1.17.29-.22 0-.43-.02-.64-.08-.21-.05-.4-.13-.56-.24a1.333 1.333 0 0 1-.59-1.11h-1.89c0 .36.08.71.24 1.05.16.34.39.65.7.93.31.27.69.49 1.15.66.46.17.98.25 1.58.25.53 0 1.01-.06 1.44-.19.43-.13.8-.31 1.11-.54.31-.23.54-.51.71-.83.17-.32.25-.67.25-1.06-.02-.4-.09-.74-.24-1.02z"/></symbol><symbol viewBox="0 0 24 24" id="ic_timer_off_24px"><path d="M19.04 4.55l-1.42 1.42a9.012 9.012 0 0 0-10.57-.49l1.46 1.46C9.53 6.35 10.73 6 12 6c3.87 0 7 3.13 7 7 0 1.27-.35 2.47-.94 3.49l1.45 1.45A8.878 8.878 0 0 0 21 13c0-2.12-.74-4.07-1.97-5.61l1.42-1.42-1.41-1.42zM15 1H9v2h6V1zm-4 8.44l2 2V8h-2v1.44zM3.02 4L1.75 5.27 4.5 8.03A8.905 8.905 0 0 0 3 13c0 4.97 4.02 9 9 9 1.84 0 3.55-.55 4.98-1.5l2.5 2.5 1.27-1.27-7.71-7.71L3.02 4zM12 20c-3.87 0-7-3.13-7-7 0-1.28.35-2.48.95-3.52l9.56 9.56c-1.03.61-2.23.96-3.51.96z"/></symbol><symbol viewBox="0 0 24 24" id="ic_tonality_24px"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.94-.49-7-3.85-7-7.93s3.05-7.44 7-7.93v15.86zm2-15.86c1.03.13 2 .45 2.87.93H13v-.93zM13 7h5.24c.25.31.48.65.68 1H13V7zm0 3h6.74c.08.33.15.66.19 1H13v-1zm0 9.93V19h2.87c-.87.48-1.84.8-2.87.93zM18.24 17H13v-1h5.92c-.2.35-.43.69-.68 1zm1.5-3H13v-1h6.93c-.04.34-.11.67-.19 1z"/></symbol><symbol viewBox="0 0 24 24" id="ic_transform_24px"><path d="M22 18v-2H8V4h2L7 1 4 4h2v2H2v2h4v8c0 1.1.9 2 2 2h8v2h-2l3 3 3-3h-2v-2h4zM10 8h6v6h2V8c0-1.1-.9-2-2-2h-6v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_tune_24px"><path d="M3 17v2h6v-2H3zM3 5v2h10V5H3zm10 16v-2h8v-2h-8v-2h-2v6h2zM7 9v2H3v2h4v2h2V9H7zm14 4v-2H11v2h10zm-6-4h2V7h4V5h-4V3h-2v6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_view_comfy_24px"><path d="M3 9h4V5H3v4zm0 5h4v-4H3v4zm5 0h4v-4H8v4zm5 0h4v-4h-4v4zM8 9h4V5H8v4zm5-4v4h4V5h-4zm5 9h4v-4h-4v4zM3 19h4v-4H3v4zm5 0h4v-4H8v4zm5 0h4v-4h-4v4zm5 0h4v-4h-4v4zm0-14v4h4V5h-4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_view_compact_24px"><path d="M3 19h6v-7H3v7zm7 0h12v-7H10v7zM3 5v6h19V5H3z"/></symbol><symbol viewBox="0 0 24 24" id="ic_vignette_24px"><path d="M21 3H3c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-9 15c-4.42 0-8-2.69-8-6s3.58-6 8-6 8 2.69 8 6-3.58 6-8 6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_wb_auto_24px"><path d="M6.85 12.65h2.3L8 9l-1.15 3.65zM22 7l-1.2 6.29L19.3 7h-1.6l-1.49 6.29L15 7h-.76A7.97 7.97 0 0 0 8 4c-4.42 0-8 3.58-8 8s3.58 8 8 8c3.13 0 5.84-1.81 7.15-4.43l.1.43H17l1.5-6.1L20 16h1.75l2.05-9H22zm-11.7 9l-.7-2H6.4l-.7 2H3.8L7 7h2l3.2 9h-1.9z"/></symbol><symbol viewBox="0 0 24 24" id="ic_wb_cloudy_24px"><path d="M19.36 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04A5.994 5.994 0 0 0 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.64-4.96z"/></symbol><symbol viewBox="0 0 24 24" id="ic_wb_incandescent_24px"><path d="M3.55 18.54l1.41 1.41 1.79-1.8-1.41-1.41-1.79 1.8zM11 22.45h2V19.5h-2v2.95zM4 10.5H1v2h3v-2zm11-4.19V1.5H9v4.81C7.21 7.35 6 9.28 6 11.5c0 3.31 2.69 6 6 6s6-2.69 6-6c0-2.22-1.21-4.15-3-5.19zm5 4.19v2h3v-2h-3zm-2.76 7.66l1.79 1.8 1.41-1.41-1.8-1.79-1.4 1.4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_wb_iridescent_24px"><path d="M5 14.5h14v-6H5v6zM11 .55V3.5h2V.55h-2zm8.04 2.5l-1.79 1.79 1.41 1.41 1.8-1.79-1.42-1.41zM13 22.45V19.5h-2v2.95h2zm7.45-3.91l-1.8-1.79-1.41 1.41 1.79 1.8 1.42-1.42zM3.55 4.46l1.79 1.79 1.41-1.41-1.79-1.79-1.41 1.41zm1.41 15.49l1.79-1.8-1.41-1.41-1.79 1.79 1.41 1.42z"/></symbol><symbol viewBox="0 0 24 24" id="ic_wb_sunny_24px"><path d="M6.76 4.84l-1.8-1.79-1.41 1.41 1.79 1.79 1.42-1.41zM4 10.5H1v2h3v-2zm9-9.95h-2V3.5h2V.55zm7.45 3.91l-1.41-1.41-1.79 1.79 1.41 1.41 1.79-1.79zm-3.21 13.7l1.79 1.8 1.41-1.41-1.8-1.79-1.4 1.4zM20 10.5v2h3v-2h-3zm-8-5c-3.31 0-6 2.69-6 6s2.69 6 6 6 6-2.69 6-6-2.69-6-6-6zm-1 16.95h2V19.5h-2v2.95zm-7.45-3.91l1.41 1.41 1.79-1.8-1.41-1.41-1.79 1.8z"/></symbol></svg>
\ 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 0000000..31a70d1
--- /dev/null
+++ b/skyquake/framework/style/icons/svg-sprite-maps-symbol.svg
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><symbol viewBox="0 0 24 24" id="ic_add_location_24px"><path d="M12 2C8.14 2 5 5.14 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.86-3.14-7-7-7zm4 8h-3v3h-2v-3H8V8h3V5h2v3h3v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_beenhere_24px"><path d="M19 1H5c-1.1 0-1.99.9-1.99 2L3 15.93c0 .69.35 1.3.88 1.66L12 23l8.11-5.41c.53-.36.88-.97.88-1.66L21 3c0-1.1-.9-2-2-2zm-9 15l-5-5 1.41-1.41L10 13.17l7.59-7.59L19 7l-9 9z"/></symbol><symbol viewBox="0 0 24 24" id="ic_directions_24px"><path d="M21.71 11.29l-9-9a.996.996 0 0 0-1.41 0l-9 9a.996.996 0 0 0 0 1.41l9 9c.39.39 1.02.39 1.41 0l9-9a.996.996 0 0 0 0-1.41zM14 14.5V12h-4v3H8v-4c0-.55.45-1 1-1h5V7.5l3.5 3.5-3.5 3.5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_directions_bike_24px"><path d="M15.5 5.5c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zM5 12c-2.8 0-5 2.2-5 5s2.2 5 5 5 5-2.2 5-5-2.2-5-5-5zm0 8.5c-1.9 0-3.5-1.6-3.5-3.5s1.6-3.5 3.5-3.5 3.5 1.6 3.5 3.5-1.6 3.5-3.5 3.5zm5.8-10l2.4-2.4.8.8c1.3 1.3 3 2.1 5.1 2.1V9c-1.5 0-2.7-.6-3.6-1.5l-1.9-1.9c-.5-.4-1-.6-1.6-.6s-1.1.2-1.4.6L7.8 8.4c-.4.4-.6.9-.6 1.4 0 .6.2 1.1.6 1.4L11 14v5h2v-6.2l-2.2-2.3zM19 12c-2.8 0-5 2.2-5 5s2.2 5 5 5 5-2.2 5-5-2.2-5-5-5zm0 8.5c-1.9 0-3.5-1.6-3.5-3.5s1.6-3.5 3.5-3.5 3.5 1.6 3.5 3.5-1.6 3.5-3.5 3.5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_directions_boat_24px"><path d="M20 21c-1.39 0-2.78-.47-4-1.32-2.44 1.71-5.56 1.71-8 0C6.78 20.53 5.39 21 4 21H2v2h2c1.38 0 2.74-.35 4-.99a8.752 8.752 0 0 0 8 0c1.26.65 2.62.99 4 .99h2v-2h-2zM3.95 19H4c1.6 0 3.02-.88 4-2 .98 1.12 2.4 2 4 2s3.02-.88 4-2c.98 1.12 2.4 2 4 2h.05l1.89-6.68c.08-.26.06-.54-.06-.78s-.34-.42-.6-.5L20 10.62V6c0-1.1-.9-2-2-2h-3V1H9v3H6c-1.1 0-2 .9-2 2v4.62l-1.29.42a1.007 1.007 0 0 0-.66 1.28L3.95 19zM6 6h12v3.97L12 8 6 9.97V6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_directions_bus_24px"><path d="M4 16c0 .88.39 1.67 1 2.22V20c0 .55.45 1 1 1h1c.55 0 1-.45 1-1v-1h8v1c0 .55.45 1 1 1h1c.55 0 1-.45 1-1v-1.78c.61-.55 1-1.34 1-2.22V6c0-3.5-3.58-4-8-4s-8 .5-8 4v10zm3.5 1c-.83 0-1.5-.67-1.5-1.5S6.67 14 7.5 14s1.5.67 1.5 1.5S8.33 17 7.5 17zm9 0c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zm1.5-6H6V6h12v5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_directions_car_24px"><path d="M18.92 6.01C18.72 5.42 18.16 5 17.5 5h-11c-.66 0-1.21.42-1.42 1.01L3 12v8c0 .55.45 1 1 1h1c.55 0 1-.45 1-1v-1h12v1c0 .55.45 1 1 1h1c.55 0 1-.45 1-1v-8l-2.08-5.99zM6.5 16c-.83 0-1.5-.67-1.5-1.5S5.67 13 6.5 13s1.5.67 1.5 1.5S7.33 16 6.5 16zm11 0c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zM5 11l1.5-4.5h11L19 11H5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_directions_railway_24px"><path d="M4 15.5C4 17.43 5.57 19 7.5 19L6 20.5v.5h12v-.5L16.5 19c1.93 0 3.5-1.57 3.5-3.5V5c0-3.5-3.58-4-8-4s-8 .5-8 4v10.5zm8 1.5c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zm6-7H6V5h12v5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_directions_run_24px"><path d="M13.49 5.48c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm-3.6 13.9l1-4.4 2.1 2v6h2v-7.5l-2.1-2 .6-3c1.3 1.5 3.3 2.5 5.5 2.5v-2c-1.9 0-3.5-1-4.3-2.4l-1-1.6c-.4-.6-1-1-1.7-1-.3 0-.5.1-.8.1l-5.2 2.2v4.7h2v-3.4l1.8-.7-1.6 8.1-4.9-1-.4 2 7 1.4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_directions_subway_24px"><path d="M12 2c-4.42 0-8 .5-8 4v9.5C4 17.43 5.57 19 7.5 19L6 20.5v.5h12v-.5L16.5 19c1.93 0 3.5-1.57 3.5-3.5V6c0-3.5-3.58-4-8-4zM7.5 17c-.83 0-1.5-.67-1.5-1.5S6.67 14 7.5 14s1.5.67 1.5 1.5S8.33 17 7.5 17zm3.5-6H6V6h5v5zm5.5 6c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zm1.5-6h-5V6h5v5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_directions_transit_24px"><path d="M12 2c-4.42 0-8 .5-8 4v9.5C4 17.43 5.57 19 7.5 19L6 20.5v.5h12v-.5L16.5 19c1.93 0 3.5-1.57 3.5-3.5V6c0-3.5-3.58-4-8-4zM7.5 17c-.83 0-1.5-.67-1.5-1.5S6.67 14 7.5 14s1.5.67 1.5 1.5S8.33 17 7.5 17zm3.5-6H6V6h5v5zm5.5 6c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zm1.5-6h-5V6h5v5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_directions_walk_24px"><path d="M13.5 5.5c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zM9.8 8.9L7 23h2.1l1.8-8 2.1 2v6h2v-7.5l-2.1-2 .6-3C14.8 12 16.8 13 19 13v-2c-1.9 0-3.5-1-4.3-2.4l-1-1.6c-.4-.6-1-1-1.7-1-.3 0-.5.1-.8.1L6 8.3V13h2V9.6l1.8-.7"/></symbol><symbol viewBox="0 0 24 24" id="ic_edit_location_24px"><path d="M12 2C8.14 2 5 5.14 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.86-3.14-7-7-7zm-1.56 10H9v-1.44l3.35-3.34 1.43 1.43L10.44 12zm4.45-4.45l-.7.7-1.44-1.44.7-.7a.38.38 0 0 1 .54 0l.9.9c.15.15.15.39 0 .54z"/></symbol><symbol viewBox="0 0 24 24" id="ic_ev_station_24px"><path d="M19.77 7.23l.01-.01-3.72-3.72L15 4.56l2.11 2.11c-.94.36-1.61 1.26-1.61 2.33a2.5 2.5 0 0 0 2.5 2.5c.36 0 .69-.08 1-.21v7.21c0 .55-.45 1-1 1s-1-.45-1-1V14c0-1.1-.9-2-2-2h-1V5c0-1.1-.9-2-2-2H6c-1.1 0-2 .9-2 2v16h10v-7.5h1.5v5a2.5 2.5 0 0 0 5 0V9c0-.69-.28-1.32-.73-1.77zM18 10c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1zM8 18v-4.5H6L10 6v5h2l-4 7z"/></symbol><symbol viewBox="0 0 24 24" id="ic_flight_24px"><path d="M21 16v-2l-8-5V3.5c0-.83-.67-1.5-1.5-1.5S10 2.67 10 3.5V9l-8 5v2l8-2.5V19l-2 1.5V22l3.5-1 3.5 1v-1.5L13 19v-5.5l8 2.5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_hotel_24px"><path d="M7 13c1.66 0 3-1.34 3-3S8.66 7 7 7s-3 1.34-3 3 1.34 3 3 3zm12-6h-8v7H3V5H1v15h2v-3h18v3h2v-9a4 4 0 0 0-4-4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_layers_24px"><path d="M11.99 18.54l-7.37-5.73L3 14.07l9 7 9-7-1.63-1.27-7.38 5.74zM12 16l7.36-5.73L21 9l-9-7-9 7 1.63 1.27L12 16z"/></symbol><symbol viewBox="0 0 24 24" id="ic_layers_clear_24px"><path d="M19.81 14.99l1.19-.92-1.43-1.43-1.19.92 1.43 1.43zm-.45-4.72L21 9l-9-7-2.91 2.27 7.87 7.88 2.4-1.88zM3.27 1L2 2.27l4.22 4.22L3 9l1.63 1.27L12 16l2.1-1.63 1.43 1.43L12 18.54l-7.37-5.73L3 14.07l9 7 4.95-3.85L20.73 21 22 19.73 3.27 1z"/></symbol><symbol viewBox="0 0 24 24" id="ic_local_activity_24px"><path d="M20 12c0-1.1.9-2 2-2V6c0-1.1-.9-2-2-2H4c-1.1 0-1.99.9-1.99 2v4c1.1 0 1.99.9 1.99 2s-.89 2-2 2v4c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2v-4c-1.1 0-2-.9-2-2zm-4.42 4.8L12 14.5l-3.58 2.3 1.08-4.12-3.29-2.69 4.24-.25L12 5.8l1.54 3.95 4.24.25-3.29 2.69 1.09 4.11z"/></symbol><symbol viewBox="0 0 24 24" id="ic_local_airport_24px"><path d="M21 16v-2l-8-5V3.5c0-.83-.67-1.5-1.5-1.5S10 2.67 10 3.5V9l-8 5v2l8-2.5V19l-2 1.5V22l3.5-1 3.5 1v-1.5L13 19v-5.5l8 2.5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_local_atm_24px"><path d="M11 17h2v-1h1c.55 0 1-.45 1-1v-3c0-.55-.45-1-1-1h-3v-1h4V8h-2V7h-2v1h-1c-.55 0-1 .45-1 1v3c0 .55.45 1 1 1h3v1H9v2h2v1zm9-13H4c-1.11 0-1.99.89-1.99 2L2 18c0 1.11.89 2 2 2h16c1.11 0 2-.89 2-2V6c0-1.11-.89-2-2-2zm0 14H4V6h16v12z"/></symbol><symbol viewBox="0 0 24 24" id="ic_local_bar_24px"><path d="M21 5V3H3v2l8 9v5H6v2h12v-2h-5v-5l8-9zM7.43 7L5.66 5h12.69l-1.78 2H7.43z"/></symbol><symbol viewBox="0 0 24 24" id="ic_local_cafe_24px"><path d="M20 3H4v10a4 4 0 0 0 4 4h6a4 4 0 0 0 4-4v-3h2c1.11 0 2-.89 2-2V5c0-1.11-.89-2-2-2zm0 5h-2V5h2v3zM2 21h18v-2H2v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_local_car_wash_24px"><path d="M17 5c.83 0 1.5-.67 1.5-1.5 0-1-1.5-2.7-1.5-2.7s-1.5 1.7-1.5 2.7c0 .83.67 1.5 1.5 1.5zm-5 0c.83 0 1.5-.67 1.5-1.5 0-1-1.5-2.7-1.5-2.7s-1.5 1.7-1.5 2.7c0 .83.67 1.5 1.5 1.5zM7 5c.83 0 1.5-.67 1.5-1.5C8.5 2.5 7 .8 7 .8S5.5 2.5 5.5 3.5C5.5 4.33 6.17 5 7 5zm11.92 3.01C18.72 7.42 18.16 7 17.5 7h-11c-.66 0-1.21.42-1.42 1.01L3 14v8c0 .55.45 1 1 1h1c.55 0 1-.45 1-1v-1h12v1c0 .55.45 1 1 1h1c.55 0 1-.45 1-1v-8l-2.08-5.99zM6.5 18c-.83 0-1.5-.67-1.5-1.5S5.67 15 6.5 15s1.5.67 1.5 1.5S7.33 18 6.5 18zm11 0c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zM5 13l1.5-4.5h11L19 13H5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_local_convenience_store_24px"><path d="M19 7V4H5v3H2v13h8v-4h4v4h8V7h-3zm-8 3H9v1h2v1H8V9h2V8H8V7h3v3zm5 2h-1v-2h-2V7h1v2h1V7h1v5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_local_dining_24px"><path d="M8.1 13.34l2.83-2.83L3.91 3.5a4.008 4.008 0 0 0 0 5.66l4.19 4.18zm6.78-1.81c1.53.71 3.68.21 5.27-1.38 1.91-1.91 2.28-4.65.81-6.12-1.46-1.46-4.2-1.1-6.12.81-1.59 1.59-2.09 3.74-1.38 5.27L3.7 19.87l1.41 1.41L12 14.41l6.88 6.88 1.41-1.41L13.41 13l1.47-1.47z"/></symbol><symbol viewBox="0 0 24 24" id="ic_local_drink_24px"><path d="M3 2l2.01 18.23C5.13 21.23 5.97 22 7 22h10c1.03 0 1.87-.77 1.99-1.77L21 2H3zm9 17c-1.66 0-3-1.34-3-3 0-2 3-5.4 3-5.4s3 3.4 3 5.4c0 1.66-1.34 3-3 3zm6.33-11H5.67l-.44-4h13.53l-.43 4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_local_florist_24px"><path d="M12 22a9 9 0 0 0 9-9 9 9 0 0 0-9 9zM5.6 10.25a2.5 2.5 0 0 0 3.92 2.06l-.02.19a2.5 2.5 0 0 0 5 0l-.02-.19c.4.28.89.44 1.42.44a2.5 2.5 0 0 0 2.5-2.5c0-1-.59-1.85-1.43-2.25a2.49 2.49 0 0 0 1.43-2.25 2.5 2.5 0 0 0-3.92-2.06l.02-.19a2.5 2.5 0 1 0-5 0l.02.19c-.4-.28-.89-.44-1.42-.44a2.5 2.5 0 0 0-2.5 2.5c0 1 .59 1.85 1.43 2.25a2.49 2.49 0 0 0-1.43 2.25zM12 5.5a2.5 2.5 0 0 1 0 5 2.5 2.5 0 0 1 0-5zM3 13a9 9 0 0 0 9 9 9 9 0 0 0-9-9z"/></symbol><symbol viewBox="0 0 24 24" id="ic_local_gas_station_24px"><path d="M19.77 7.23l.01-.01-3.72-3.72L15 4.56l2.11 2.11c-.94.36-1.61 1.26-1.61 2.33a2.5 2.5 0 0 0 2.5 2.5c.36 0 .69-.08 1-.21v7.21c0 .55-.45 1-1 1s-1-.45-1-1V14c0-1.1-.9-2-2-2h-1V5c0-1.1-.9-2-2-2H6c-1.1 0-2 .9-2 2v16h10v-7.5h1.5v5a2.5 2.5 0 0 0 5 0V9c0-.69-.28-1.32-.73-1.77zM12 10H6V5h6v5zm6 0c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1z"/></symbol><symbol viewBox="0 0 24 24" id="ic_local_grocery_store_24px"><path d="M7 18c-1.1 0-1.99.9-1.99 2S5.9 22 7 22s2-.9 2-2-.9-2-2-2zM1 2v2h2l3.6 7.59-1.35 2.45c-.16.28-.25.61-.25.96 0 1.1.9 2 2 2h12v-2H7.42c-.14 0-.25-.11-.25-.25l.03-.12.9-1.63h7.45c.75 0 1.41-.41 1.75-1.03l3.58-6.49A1.003 1.003 0 0 0 20 4H5.21l-.94-2H1zm16 16c-1.1 0-1.99.9-1.99 2s.89 2 1.99 2 2-.9 2-2-.9-2-2-2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_local_hospital_24px"><path d="M19 3H5c-1.1 0-1.99.9-1.99 2L3 19c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-1 11h-4v4h-4v-4H6v-4h4V6h4v4h4v4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_local_hotel_24px"><path d="M7 13c1.66 0 3-1.34 3-3S8.66 7 7 7s-3 1.34-3 3 1.34 3 3 3zm12-6h-8v7H3V5H1v15h2v-3h18v3h2v-9a4 4 0 0 0-4-4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_local_laundry_service_24px"><path d="M9.17 16.83a4.008 4.008 0 0 0 5.66 0 4.008 4.008 0 0 0 0-5.66l-5.66 5.66zM18 2.01L6 2c-1.11 0-2 .89-2 2v16c0 1.11.89 2 2 2h12c1.11 0 2-.89 2-2V4c0-1.11-.89-1.99-2-1.99zM10 4c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zM7 4c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zm5 16c-3.31 0-6-2.69-6-6s2.69-6 6-6 6 2.69 6 6-2.69 6-6 6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_local_library_24px"><path d="M12 11.55C9.64 9.35 6.48 8 3 8v11c3.48 0 6.64 1.35 9 3.55 2.36-2.19 5.52-3.55 9-3.55V8c-3.48 0-6.64 1.35-9 3.55zM12 8c1.66 0 3-1.34 3-3s-1.34-3-3-3-3 1.34-3 3 1.34 3 3 3z"/></symbol><symbol viewBox="0 0 24 24" id="ic_local_mall_24px"><path d="M19 6h-2c0-2.76-2.24-5-5-5S7 3.24 7 6H5c-1.1 0-1.99.9-1.99 2L3 20c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2zm-7-3c1.66 0 3 1.34 3 3H9c0-1.66 1.34-3 3-3zm0 10c-2.76 0-5-2.24-5-5h2c0 1.66 1.34 3 3 3s3-1.34 3-3h2c0 2.76-2.24 5-5 5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_local_movies_24px"><path d="M18 3v2h-2V3H8v2H6V3H4v18h2v-2h2v2h8v-2h2v2h2V3h-2zM8 17H6v-2h2v2zm0-4H6v-2h2v2zm0-4H6V7h2v2zm10 8h-2v-2h2v2zm0-4h-2v-2h2v2zm0-4h-2V7h2v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_local_offer_24px"><path d="M21.41 11.58l-9-9C12.05 2.22 11.55 2 11 2H4c-1.1 0-2 .9-2 2v7c0 .55.22 1.05.59 1.42l9 9c.36.36.86.58 1.41.58.55 0 1.05-.22 1.41-.59l7-7c.37-.36.59-.86.59-1.41 0-.55-.23-1.06-.59-1.42zM5.5 7C4.67 7 4 6.33 4 5.5S4.67 4 5.5 4 7 4.67 7 5.5 6.33 7 5.5 7z"/></symbol><symbol viewBox="0 0 24 24" id="ic_local_parking_24px"><path d="M13 3H6v18h4v-6h3c3.31 0 6-2.69 6-6s-2.69-6-6-6zm.2 8H10V7h3.2c1.1 0 2 .9 2 2s-.9 2-2 2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_local_pharmacy_24px"><path d="M21 5h-2.64l1.14-3.14L17.15 1l-1.46 4H3v2l2 6-2 6v2h18v-2l-2-6 2-6V5zm-5 9h-3v3h-2v-3H8v-2h3V9h2v3h3v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_local_phone_24px"><path d="M6.62 10.79a15.15 15.15 0 0 0 6.59 6.59l2.2-2.2c.27-.27.67-.36 1.02-.24 1.12.37 2.33.57 3.57.57.55 0 1 .45 1 1V20c0 .55-.45 1-1 1-9.39 0-17-7.61-17-17 0-.55.45-1 1-1h3.5c.55 0 1 .45 1 1 0 1.25.2 2.45.57 3.57.11.35.03.74-.25 1.02l-2.2 2.2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_local_pizza_24px"><path d="M12 2C8.43 2 5.23 3.54 3.01 6L12 22l8.99-16C18.78 3.55 15.57 2 12 2zM7 7c0-1.1.9-2 2-2s2 .9 2 2-.9 2-2 2-2-.9-2-2zm5 8c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_local_play_24px"><path d="M20 12c0-1.1.9-2 2-2V6c0-1.1-.9-2-2-2H4c-1.1 0-1.99.9-1.99 2v4c1.1 0 1.99.9 1.99 2s-.89 2-2 2v4c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2v-4c-1.1 0-2-.9-2-2zm-4.42 4.8L12 14.5l-3.58 2.3 1.08-4.12-3.29-2.69 4.24-.25L12 5.8l1.54 3.95 4.24.25-3.29 2.69 1.09 4.11z"/></symbol><symbol viewBox="0 0 24 24" id="ic_local_post_office_24px"><path d="M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_local_printshop_24px"><path d="M19 8H5c-1.66 0-3 1.34-3 3v6h4v4h12v-4h4v-6c0-1.66-1.34-3-3-3zm-3 11H8v-5h8v5zm3-7c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1zm-1-9H6v4h12V3z"/></symbol><symbol viewBox="0 0 24 24" id="ic_local_see_24px"><circle cx="12" cy="12" r="3.2"/><path d="M9 2L7.17 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2h-3.17L15 2H9zm3 15c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_local_shipping_24px"><path d="M20 8h-3V4H3c-1.1 0-2 .9-2 2v11h2c0 1.66 1.34 3 3 3s3-1.34 3-3h6c0 1.66 1.34 3 3 3s3-1.34 3-3h2v-5l-3-4zM6 18.5c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zm13.5-9l1.96 2.5H17V9.5h2.5zm-1.5 9c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_local_taxi_24px"><path d="M18.92 6.01C18.72 5.42 18.16 5 17.5 5H15V3H9v2H6.5c-.66 0-1.21.42-1.42 1.01L3 12v8c0 .55.45 1 1 1h1c.55 0 1-.45 1-1v-1h12v1c0 .55.45 1 1 1h1c.55 0 1-.45 1-1v-8l-2.08-5.99zM6.5 16c-.83 0-1.5-.67-1.5-1.5S5.67 13 6.5 13s1.5.67 1.5 1.5S7.33 16 6.5 16zm11 0c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zM5 11l1.5-4.5h11L19 11H5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_map_24px"><path d="M20.5 3l-.16.03L15 5.1 9 3 3.36 4.9c-.21.07-.36.25-.36.48V20.5c0 .28.22.5.5.5l.16-.03L9 18.9l6 2.1 5.64-1.9c.21-.07.36-.25.36-.48V3.5c0-.28-.22-.5-.5-.5zM15 19l-6-2.11V5l6 2.11V19z"/></symbol><symbol viewBox="0 0 24 24" id="ic_my_location_24px"><path d="M12 8c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm8.94 3A8.994 8.994 0 0 0 13 3.06V1h-2v2.06A8.994 8.994 0 0 0 3.06 11H1v2h2.06A8.994 8.994 0 0 0 11 20.94V23h2v-2.06A8.994 8.994 0 0 0 20.94 13H23v-2h-2.06zM12 19c-3.87 0-7-3.13-7-7s3.13-7 7-7 7 3.13 7 7-3.13 7-7 7z"/></symbol><symbol viewBox="0 0 24 24" id="ic_navigation_24px"><path d="M12 2L4.5 20.29l.71.71L12 18l6.79 3 .71-.71z"/></symbol><symbol viewBox="0 0 24 24" id="ic_near_me_24px"><path d="M21 3L3 10.53v.98l6.84 2.65L12.48 21h.98L21 3z"/></symbol><symbol viewBox="0 0 24 24" id="ic_person_pin_24px"><path d="M19 2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h4l3 3 3-3h4c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-7 3.3A2.7 2.7 0 0 1 14.7 8a2.7 2.7 0 0 1-2.7 2.7A2.7 2.7 0 0 1 9.3 8 2.7 2.7 0 0 1 12 5.3zM18 16H6v-.9c0-2 4-3.1 6-3.1s6 1.1 6 3.1v.9z"/></symbol><symbol viewBox="0 0 24 24" id="ic_person_pin_circle_24px"><path d="M12 2C8.14 2 5 5.14 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.86-3.14-7-7-7zm0 2a2 2 0 1 1 0 4 2 2 0 0 1 0-4zm0 10c-1.67 0-3.14-.85-4-2.15.02-1.32 2.67-2.05 4-2.05s3.98.73 4 2.05A4.783 4.783 0 0 1 12 14z"/></symbol><symbol viewBox="0 0 24 24" id="ic_pin_drop_24px"><path d="M18 8c0-3.31-2.69-6-6-6S6 4.69 6 8c0 4.5 6 11 6 11s6-6.5 6-11zm-8 0c0-1.1.9-2 2-2s2 .9 2 2a2 2 0 0 1-4 0zM5 20v2h14v-2H5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_place_24px"><path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5a2.5 2.5 0 0 1 0-5 2.5 2.5 0 0 1 0 5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_rate_review_24px"><path d="M20 2H4c-1.1 0-1.99.9-1.99 2L2 22l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zM6 14v-2.47l6.88-6.88c.2-.2.51-.2.71 0l1.77 1.77c.2.2.2.51 0 .71L8.47 14H6zm12 0h-7.5l2-2H18v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_restaurant_24px"><path d="M11 9H9V2H7v7H5V2H3v7c0 2.12 1.66 3.84 3.75 3.97V22h2.5v-9.03C11.34 12.84 13 11.12 13 9V2h-2v7zm5-3v8h2.5v8H21V2c-2.76 0-5 2.24-5 4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_restaurant_menu_24px"><path d="M8.1 13.34l2.83-2.83L3.91 3.5a4.008 4.008 0 0 0 0 5.66l4.19 4.18zm6.78-1.81c1.53.71 3.68.21 5.27-1.38 1.91-1.91 2.28-4.65.81-6.12-1.46-1.46-4.2-1.1-6.12.81-1.59 1.59-2.09 3.74-1.38 5.27L3.7 19.87l1.41 1.41L12 14.41l6.88 6.88 1.41-1.41L13.41 13l1.47-1.47z"/></symbol><symbol viewBox="0 0 24 24" id="ic_satellite_24px"><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM5 4.99h3C8 6.65 6.66 8 5 8V4.99zM5 12v-2c2.76 0 5-2.25 5-5.01h2C12 8.86 8.87 12 5 12zm0 6l3.5-4.5 2.5 3.01L14.5 12l4.5 6H5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_store_mall_directory_24px"><path d="M20 4H4v2h16V4zm1 10v-2l-1-5H4l-1 5v2h1v6h10v-6h4v6h2v-6h1zm-9 4H6v-4h6v4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_streetview_24px"><path d="M12.56 14.33c-.34.27-.56.7-.56 1.17V21h7c1.1 0 2-.9 2-2v-5.98c-.94-.33-1.95-.52-3-.52-2.03 0-3.93.7-5.44 1.83z"/><circle cx="18" cy="6" r="5"/><path d="M11.5 6c0-1.08.27-2.1.74-3H5c-1.1 0-2 .9-2 2v14c0 .55.23 1.05.59 1.41l9.82-9.82A6.435 6.435 0 0 1 11.5 6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_subway_24px"><circle cx="15.5" cy="16" r="1"/><circle cx="8.5" cy="16" r="1"/><path d="M7.01 9h10v5h-10zM17.8 2.8C16 2.09 13.86 2 12 2c-1.86 0-4 .09-5.8.8C3.53 3.84 2 6.05 2 8.86V22h20V8.86c0-2.81-1.53-5.02-4.2-6.06zm.2 13.08c0 1.45-1.18 2.62-2.63 2.62l1.13 1.12V20H15l-1.5-1.5h-2.83L9.17 20H7.5v-.38l1.12-1.12A2.63 2.63 0 0 1 6 15.88V9c0-2.63 3-3 6-3 3.32 0 6 .38 6 3v6.88z"/></symbol><symbol viewBox="0 0 24 24" id="ic_terrain_24px"><path d="M14 6l-3.75 5 2.85 3.8-1.6 1.2C9.81 13.75 7 10 7 10l-6 8h22L14 6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_traffic_24px"><path d="M20 10h-3V8.86c1.72-.45 3-2 3-3.86h-3V4c0-.55-.45-1-1-1H8c-.55 0-1 .45-1 1v1H4c0 1.86 1.28 3.41 3 3.86V10H4c0 1.86 1.28 3.41 3 3.86V15H4c0 1.86 1.28 3.41 3 3.86V20c0 .55.45 1 1 1h8c.55 0 1-.45 1-1v-1.14c1.72-.45 3-2 3-3.86h-3v-1.14c1.72-.45 3-2 3-3.86zm-8 9a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm0-5a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm0-5a2 2 0 0 1-2-2c0-1.11.89-2 2-2a2 2 0 0 1 0 4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_train_24px"><path d="M12 2c-4 0-8 .5-8 4v9.5C4 17.43 5.57 19 7.5 19L6 20.5v.5h2.23l2-2H14l2 2h2v-.5L16.5 19c1.93 0 3.5-1.57 3.5-3.5V6c0-3.5-3.58-4-8-4zM7.5 17c-.83 0-1.5-.67-1.5-1.5S6.67 14 7.5 14s1.5.67 1.5 1.5S8.33 17 7.5 17zm3.5-7H6V6h5v4zm2 0V6h5v4h-5zm3.5 7c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_tram_24px"><path d="M19 16.94V8.5c0-2.79-2.61-3.4-6.01-3.49l.76-1.51H17V2H7v1.5h4.75l-.76 1.52C7.86 5.11 5 5.73 5 8.5v8.44c0 1.45 1.19 2.66 2.59 2.97L6 21.5v.5h2.23l2-2H14l2 2h2v-.5L16.5 20h-.08c1.69 0 2.58-1.37 2.58-3.06zm-7 1.56c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zm5-4.5H7V9h10v5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_transfer_within_a_station_24px"><path d="M16.49 15.5v-1.75L14 16.25l2.49 2.5V17H22v-1.5zm3.02 4.25H14v1.5h5.51V23L22 20.5 19.51 18zM9.5 5.5c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zM5.75 8.9L3 23h2.1l1.75-8L9 17v6h2v-7.55L8.95 13.4l.6-3C10.85 12 12.8 13 15 13v-2c-1.85 0-3.45-1-4.35-2.45l-.95-1.6C9.35 6.35 8.7 6 8 6c-.25 0-.5.05-.75.15L2 8.3V13h2V9.65l1.75-.75"/></symbol><symbol viewBox="0 0 24 24" id="ic_zoom_out_map_24px"><path d="M15 3l2.3 2.3-2.89 2.87 1.42 1.42L18.7 6.7 21 9V3zM3 9l2.3-2.3 2.87 2.89 1.42-1.42L6.7 5.3 9 3H3zm6 12l-2.3-2.3 2.89-2.87-1.42-1.42L5.3 17.3 3 15v6zm12-6l-2.3 2.3-2.87-2.89-1.42 1.42 2.89 2.87L15 21h6z"/></symbol></svg>
\ 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 0000000..780e5fb
--- /dev/null
+++ b/skyquake/framework/style/icons/svg-sprite-navigation-symbol.svg
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><symbol viewBox="0 0 24 24" id="ic_apps_24px"><path d="M4 8h4V4H4v4zm6 12h4v-4h-4v4zm-6 0h4v-4H4v4zm0-6h4v-4H4v4zm6 0h4v-4h-4v4zm6-10v4h4V4h-4zm-6 4h4V4h-4v4zm6 6h4v-4h-4v4zm0 6h4v-4h-4v4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_arrow_back_24px"><path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_arrow_downward_24px"><path fill="#010101" d="M20 12l-1.41-1.41L13 16.17V4h-2v12.17l-5.58-5.59L4 12l8 8 8-8z"/></symbol><symbol viewBox="0 0 24 24" id="ic_arrow_drop_down_24px"><path d="M7 10l5 5 5-5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_arrow_drop_down_circle_24px"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 12l-4-4h8l-4 4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_arrow_drop_up_24px"><path d="M7 14l5-5 5 5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_arrow_forward_24px"><path d="M12 4l-1.41 1.41L16.17 11H4v2h12.17l-5.58 5.59L12 20l8-8z"/></symbol><symbol viewBox="0 0 24 24" id="ic_arrow_upward_24px"><path d="M4 12l1.41 1.41L11 7.83V20h2V7.83l5.58 5.59L20 12l-8-8-8 8z"/></symbol><symbol viewBox="0 0 24 24" id="ic_cancel_24px"><path d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17 12 13.41 8.41 17 7 15.59 10.59 12 7 8.41 8.41 7 12 10.59 15.59 7 17 8.41 13.41 12 17 15.59z"/></symbol><symbol viewBox="0 0 24 24" id="ic_check_24px"><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/></symbol><symbol viewBox="0 0 24 24" id="ic_chevron_left_24px"><path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"/></symbol><symbol viewBox="0 0 24 24" id="ic_chevron_right_24px"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_close_24px"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></symbol><symbol viewBox="0 0 24 24" id="ic_expand_less_24px"><path d="M12 8l-6 6 1.41 1.41L12 10.83l4.59 4.58L18 14z"/></symbol><symbol viewBox="0 0 24 24" id="ic_expand_more_24px"><path d="M16.59 8.59L12 13.17 7.41 8.59 6 10l6 6 6-6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_first_page_24px"><path d="M18.41 16.59L13.82 12l4.59-4.59L17 6l-6 6 6 6zM6 6h2v12H6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_fullscreen_24px"><path d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_fullscreen_exit_24px"><path d="M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z"/></symbol><symbol viewBox="0 0 24 24" id="ic_last_page_24px"><path d="M5.59 7.41L10.18 12l-4.59 4.59L7 18l6-6-6-6zM16 6h2v12h-2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_menu_24px"><path d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z"/></symbol><symbol viewBox="0 0 24 24" id="ic_more_horiz_24px"><path d="M6 10c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm12 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm-6 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_more_vert_24px"><path d="M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_refresh_24px"><path d="M17.65 6.35A7.958 7.958 0 0 0 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08A5.99 5.99 0 0 1 12 18c-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"/></symbol><symbol viewBox="0 0 24 24" id="ic_subdirectory_arrow_left_24px"><path d="M11 9l1.42 1.42L8.83 14H18V4h2v12H8.83l3.59 3.58L11 21l-6-6 6-6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_subdirectory_arrow_right_24px"><path d="M19 15l-6 6-1.42-1.42L15.17 16H4V4h2v10h9.17l-3.59-3.58L13 9l6 6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_unfold_less_24px"><path d="M7.41 18.59L8.83 20 12 16.83 15.17 20l1.41-1.41L12 14l-4.59 4.59zm9.18-13.18L15.17 4 12 7.17 8.83 4 7.41 5.41 12 10l4.59-4.59z"/></symbol><symbol viewBox="0 0 24 24" id="ic_unfold_more_24px"><path d="M12 5.83L15.17 9l1.41-1.41L12 3 7.41 7.59 8.83 9 12 5.83zm0 12.34L8.83 15l-1.41 1.41L12 21l4.59-4.59L15.17 15 12 18.17z"/></symbol></svg>
\ 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 0000000..97f3ae5
--- /dev/null
+++ b/skyquake/framework/style/icons/svg-sprite-notification-symbol.svg
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><symbol viewBox="0 0 24 24" id="ic_adb_24px"><path d="M5 16c0 3.87 3.13 7 7 7s7-3.13 7-7v-4H5v4zM16.12 4.37l2.1-2.1-.82-.83-2.3 2.31C14.16 3.28 13.12 3 12 3s-2.16.28-3.09.75L6.6 1.44l-.82.83 2.1 2.1C6.14 5.64 5 7.68 5 10v1h14v-1c0-2.32-1.14-4.36-2.88-5.63zM9 9c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1zm6 0c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1z"/></symbol><symbol viewBox="0 0 24 24" id="ic_airline_seat_flat_24px"><path d="M22 11v2H9V7h9a4 4 0 0 1 4 4zM2 14v2h6v2h8v-2h6v-2H2zm5.14-1.9a3 3 0 0 0-.04-4.24 3 3 0 0 0-4.24.04 3 3 0 0 0 .04 4.24 3 3 0 0 0 4.24-.04z"/></symbol><symbol viewBox="0 0 24 24" id="ic_airline_seat_flat_angled_24px"><path d="M22.25 14.29l-.69 1.89L9.2 11.71l2.08-5.66 8.56 3.09a4 4 0 0 1 2.41 5.15zM1.5 12.14L8 14.48V19h8v-1.63L20.52 19l.69-1.89-19.02-6.86-.69 1.89zm5.8-1.94a3.01 3.01 0 0 0 1.41-4A3.005 3.005 0 0 0 4.7 4.8a2.99 2.99 0 0 0-1.4 4 2.99 2.99 0 0 0 4 1.4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_airline_seat_individual_suite_24px"><path d="M7 13c1.65 0 3-1.35 3-3S8.65 7 7 7s-3 1.35-3 3 1.35 3 3 3zm12-6h-8v7H3V7H1v10h22v-6a4 4 0 0 0-4-4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_airline_seat_legroom_extra_24px"><path d="M4 12V3H2v9c0 2.76 2.24 5 5 5h6v-2H7c-1.66 0-3-1.34-3-3zm18.83 5.24c-.38-.72-1.29-.97-2.03-.63l-1.09.5-3.41-6.98a2.01 2.01 0 0 0-1.79-1.12L11 9V3H5v8c0 1.66 1.34 3 3 3h7l3.41 7 3.72-1.7c.77-.36 1.1-1.3.7-2.06z"/></symbol><symbol viewBox="0 0 24 24" id="ic_airline_seat_legroom_normal_24px"><path d="M5 12V3H3v9c0 2.76 2.24 5 5 5h6v-2H8c-1.66 0-3-1.34-3-3zm15.5 6H19v-7c0-1.1-.9-2-2-2h-5V3H6v8c0 1.65 1.35 3 3 3h7v7h4.5c.83 0 1.5-.67 1.5-1.5s-.67-1.5-1.5-1.5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_airline_seat_legroom_reduced_24px"><path d="M19.97 19.2c.18.96-.55 1.8-1.47 1.8H14v-3l1-4H9c-1.65 0-3-1.35-3-3V3h6v6h5c1.1 0 2 .9 2 2l-2 7h1.44c.73 0 1.39.49 1.53 1.2zM5 12V3H3v9c0 2.76 2.24 5 5 5h4v-2H8c-1.66 0-3-1.34-3-3z"/></symbol><symbol viewBox="0 0 24 24" id="ic_airline_seat_recline_extra_24px"><path d="M5.35 5.64c-.9-.64-1.12-1.88-.49-2.79a2.01 2.01 0 0 1 2.79-.49c.9.64 1.12 1.88.49 2.79-.64.9-1.88 1.12-2.79.49zM16 19H8.93a2.99 2.99 0 0 1-2.96-2.54L4 7H2l1.99 9.76A5.01 5.01 0 0 0 8.94 21H16v-2zm.23-4h-4.88l-1.03-4.1c1.58.89 3.28 1.54 5.15 1.22V9.99c-1.63.31-3.44-.27-4.69-1.25L9.14 7.47c-.23-.18-.49-.3-.76-.38a2.21 2.21 0 0 0-.99-.06h-.02a2.268 2.268 0 0 0-1.84 2.61l1.35 5.92A3.008 3.008 0 0 0 9.83 18h6.85l3.82 3 1.5-1.5-5.77-4.5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_airline_seat_recline_normal_24px"><path d="M7.59 5.41c-.78-.78-.78-2.05 0-2.83.78-.78 2.05-.78 2.83 0 .78.78.78 2.05 0 2.83-.79.79-2.05.79-2.83 0zM6 16V7H4v9c0 2.76 2.24 5 5 5h6v-2H9c-1.66 0-3-1.34-3-3zm14 4.07L14.93 15H11.5v-3.68c1.4 1.15 3.6 2.16 5.5 2.16v-2.16c-1.66.02-3.61-.87-4.67-2.04l-1.4-1.55c-.19-.21-.43-.38-.69-.5-.29-.14-.62-.23-.96-.23h-.03C8.01 7 7 8.01 7 9.25V15c0 1.66 1.34 3 3 3h5.07l3.5 3.5L20 20.07z"/></symbol><symbol viewBox="0 0 24 24" id="ic_bluetooth_audio_24px"><path d="M14.24 12.01l2.32 2.32c.28-.72.44-1.51.44-2.33 0-.82-.16-1.59-.43-2.31l-2.33 2.32zm5.29-5.3l-1.26 1.26c.63 1.21.98 2.57.98 4.02s-.36 2.82-.98 4.02l1.2 1.2a9.936 9.936 0 0 0 1.54-5.31c-.01-1.89-.55-3.67-1.48-5.19zm-3.82 1L10 2H9v7.59L4.41 5 3 6.41 8.59 12 3 17.59 4.41 19 9 14.41V22h1l5.71-5.71-4.3-4.29 4.3-4.29zM11 5.83l1.88 1.88L11 9.59V5.83zm1.88 10.46L11 18.17v-3.76l1.88 1.88z"/></symbol><symbol viewBox="0 0 24 24" id="ic_confirmation_number_24px"><path d="M22 10V6a2 2 0 0 0-2-2H4c-1.1 0-1.99.89-1.99 2v4c1.1 0 1.99.9 1.99 2s-.89 2-2 2v4c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2v-4c-1.1 0-2-.9-2-2s.9-2 2-2zm-9 7.5h-2v-2h2v2zm0-4.5h-2v-2h2v2zm0-4.5h-2v-2h2v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_disc_full_24px"><path d="M20 16h2v-2h-2v2zm0-9v5h2V7h-2zM10 4c-4.42 0-8 3.58-8 8s3.58 8 8 8 8-3.58 8-8-3.58-8-8-8zm0 10c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_do_not_disturb_24px"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8 0-1.85.63-3.55 1.69-4.9L16.9 18.31A7.902 7.902 0 0 1 12 20zm6.31-3.1L7.1 5.69A7.902 7.902 0 0 1 12 4c4.42 0 8 3.58 8 8 0 1.85-.63 3.55-1.69 4.9z"/></symbol><symbol viewBox="0 0 24 24" id="ic_do_not_disturb_alt_24px"><path d="M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10 10-4.5 10-10S17.5 2 12 2zM4 12c0-4.4 3.6-8 8-8 1.8 0 3.5.6 4.9 1.7L5.7 16.9A7.88 7.88 0 0 1 4 12zm8 8c-1.8 0-3.5-.6-4.9-1.7L18.3 7.1C19.4 8.5 20 10.2 20 12c0 4.4-3.6 8-8 8z"/></symbol><symbol viewBox="0 0 24 24" id="ic_do_not_disturb_off_24px"><path d="M17 11v2h-1.46l4.68 4.68A9.92 9.92 0 0 0 22 12c0-5.52-4.48-10-10-10-2.11 0-4.07.66-5.68 1.78L13.54 11H17zM2.27 2.27L1 3.54l2.78 2.78A9.92 9.92 0 0 0 2 12c0 5.52 4.48 10 10 10 2.11 0 4.07-.66 5.68-1.78L20.46 23l1.27-1.27L11 11 2.27 2.27zM7 13v-2h1.46l2 2H7z"/></symbol><symbol viewBox="0 0 24 24" id="ic_do_not_disturb_on_24px"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm5 11H7v-2h10v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_drive_eta_24px"><path d="M18.92 5.01C18.72 4.42 18.16 4 17.5 4h-11c-.66 0-1.21.42-1.42 1.01L3 11v8c0 .55.45 1 1 1h1c.55 0 1-.45 1-1v-1h12v1c0 .55.45 1 1 1h1c.55 0 1-.45 1-1v-8l-2.08-5.99zM6.5 15c-.83 0-1.5-.67-1.5-1.5S5.67 12 6.5 12s1.5.67 1.5 1.5S7.33 15 6.5 15zm11 0c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zM5 10l1.5-4.5h11L19 10H5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_enhanced_encryption_24px"><path d="M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zM8.9 6c0-1.71 1.39-3.1 3.1-3.1s3.1 1.39 3.1 3.1v2H8.9V6zM16 16h-3v3h-2v-3H8v-2h3v-3h2v3h3v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_event_available_24px"><path d="M16.53 11.06L15.47 10l-4.88 4.88-2.12-2.12-1.06 1.06L10.59 17l5.94-5.94zM19 3h-1V1h-2v2H8V1H6v2H5c-1.11 0-1.99.9-1.99 2L3 19a2 2 0 0 0 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v11z"/></symbol><symbol viewBox="0 0 24 24" id="ic_event_busy_24px"><path d="M9.31 17l2.44-2.44L14.19 17l1.06-1.06-2.44-2.44 2.44-2.44L14.19 10l-2.44 2.44L9.31 10l-1.06 1.06 2.44 2.44-2.44 2.44L9.31 17zM19 3h-1V1h-2v2H8V1H6v2H5c-1.11 0-1.99.9-1.99 2L3 19a2 2 0 0 0 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v11z"/></symbol><symbol viewBox="0 0 24 24" id="ic_event_note_24px"><path d="M17 10H7v2h10v-2zm2-7h-1V1h-2v2H8V1H6v2H5c-1.11 0-1.99.9-1.99 2L3 19a2 2 0 0 0 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v11zm-5-5H7v2h7v-2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_folder_special_24px"><path d="M20 6h-8l-2-2H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2zm-2.06 11L15 15.28 12.06 17l.78-3.33-2.59-2.24 3.41-.29L15 8l1.34 3.14 3.41.29-2.59 2.24.78 3.33z"/></symbol><symbol viewBox="0 0 24 24" id="ic_live_tv_24px"><path d="M21 6h-7.59l3.29-3.29L16 2l-4 4-4-4-.71.71L10.59 6H3a2 2 0 0 0-2 2v12c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V8a2 2 0 0 0-2-2zm0 14H3V8h18v12zM9 10v8l7-4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_mms_24px"><path d="M20 2H4c-1.1 0-1.99.9-1.99 2L2 22l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zM5 14l3.5-4.5 2.5 3.01L14.5 8l4.5 6H5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_more_24px"><path d="M22 3H7c-.69 0-1.23.35-1.59.88L0 12l5.41 8.11c.36.53.97.89 1.66.89H22c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM9 13.5c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zm5 0c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zm5 0c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_network_check_24px"><path d="M15.9 5c-.17 0-.32.09-.41.23l-.07.15-5.18 11.65c-.16.29-.26.61-.26.96 0 1.11.9 2.01 2.01 2.01.96 0 1.77-.68 1.96-1.59l.01-.03L16.4 5.5c0-.28-.22-.5-.5-.5zM1 9l2 2c2.88-2.88 6.79-4.08 10.53-3.62l1.19-2.68C9.89 3.84 4.74 5.27 1 9zm20 2l2-2a15.367 15.367 0 0 0-5.59-3.57l-.53 2.82c1.5.62 2.9 1.53 4.12 2.75zm-4 4l2-2c-.8-.8-1.7-1.42-2.66-1.89l-.55 2.92c.42.27.83.59 1.21.97zM5 13l2 2a7.1 7.1 0 0 1 4.03-2l1.28-2.88c-2.63-.08-5.3.87-7.31 2.88z"/></symbol><symbol viewBox="0 0 24 24" id="ic_network_locked_24px"><path d="M19.5 10c.17 0 .33.03.5.05V1L1 20h13v-3c0-.89.39-1.68 1-2.23v-.27c0-2.48 2.02-4.5 4.5-4.5zm2.5 6v-1.5a2.5 2.5 0 0 0-5 0V16c-.55 0-1 .45-1 1v4c0 .55.45 1 1 1h5c.55 0 1-.45 1-1v-4c0-.55-.45-1-1-1zm-1 0h-3v-1.5c0-.83.67-1.5 1.5-1.5s1.5.67 1.5 1.5V16z"/></symbol><symbol viewBox="0 0 24 24" id="ic_no_encryption_24px"><path d="M21 21.78L4.22 5 3 6.22l2.04 2.04C4.42 8.6 4 9.25 4 10v10c0 1.1.9 2 2 2h12c.23 0 .45-.05.66-.12L19.78 23 21 21.78zM8.9 6c0-1.71 1.39-3.1 3.1-3.1s3.1 1.39 3.1 3.1v2H9.66L20 18.34V10c0-1.1-.9-2-2-2h-1V6c0-2.76-2.24-5-5-5-2.56 0-4.64 1.93-4.94 4.4L8.9 7.24V6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_ondemand_video_24px"><path d="M21 3H3c-1.11 0-2 .89-2 2v12a2 2 0 0 0 2 2h5v2h8v-2h5c1.1 0 1.99-.9 1.99-2L23 5a2 2 0 0 0-2-2zm0 14H3V5h18v12zm-5-6l-7 4V7z"/></symbol><symbol viewBox="0 0 24 24" id="ic_personal_video_24px"><path d="M21 3H3c-1.11 0-2 .89-2 2v12a2 2 0 0 0 2 2h5v2h8v-2h5c1.1 0 1.99-.9 1.99-2L23 5a2 2 0 0 0-2-2zm0 14H3V5h18v12z"/></symbol><symbol viewBox="0 0 24 24" id="ic_phone_bluetooth_speaker_24px"><path d="M14.71 9.5L17 7.21V11h.5l2.85-2.85L18.21 6l2.15-2.15L17.5 1H17v3.79L14.71 2.5l-.71.71L16.79 6 14 8.79l.71.71zM18 2.91l.94.94-.94.94V2.91zm0 4.3l.94.94-.94.94V7.21zm2 8.29c-1.25 0-2.45-.2-3.57-.57a1.02 1.02 0 0 0-1.02.24l-2.2 2.2a15.045 15.045 0 0 1-6.59-6.59l2.2-2.21a.96.96 0 0 0 .25-1A11.36 11.36 0 0 1 8.5 4c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1 0 9.39 7.61 17 17 17 .55 0 1-.45 1-1v-3.5c0-.55-.45-1-1-1z"/></symbol><symbol viewBox="0 0 24 24" id="ic_phone_forwarded_24px"><path d="M18 11l5-5-5-5v3h-4v4h4v3zm2 4.5c-1.25 0-2.45-.2-3.57-.57a1.02 1.02 0 0 0-1.02.24l-2.2 2.2a15.045 15.045 0 0 1-6.59-6.59l2.2-2.21a.96.96 0 0 0 .25-1A11.36 11.36 0 0 1 8.5 4c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1 0 9.39 7.61 17 17 17 .55 0 1-.45 1-1v-3.5c0-.55-.45-1-1-1z"/></symbol><symbol viewBox="0 0 24 24" id="ic_phone_in_talk_24px"><path d="M20 15.5c-1.25 0-2.45-.2-3.57-.57a1.02 1.02 0 0 0-1.02.24l-2.2 2.2a15.045 15.045 0 0 1-6.59-6.59l2.2-2.21a.96.96 0 0 0 .25-1A11.36 11.36 0 0 1 8.5 4c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1 0 9.39 7.61 17 17 17 .55 0 1-.45 1-1v-3.5c0-.55-.45-1-1-1zM19 12h2a9 9 0 0 0-9-9v2c3.87 0 7 3.13 7 7zm-4 0h2c0-2.76-2.24-5-5-5v2c1.66 0 3 1.34 3 3z"/></symbol><symbol viewBox="0 0 24 24" id="ic_phone_locked_24px"><path d="M20 15.5c-1.25 0-2.45-.2-3.57-.57a1.02 1.02 0 0 0-1.02.24l-2.2 2.2a15.045 15.045 0 0 1-6.59-6.59l2.2-2.21a.96.96 0 0 0 .25-1A11.36 11.36 0 0 1 8.5 4c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1 0 9.39 7.61 17 17 17 .55 0 1-.45 1-1v-3.5c0-.55-.45-1-1-1zM20 4v-.5a2.5 2.5 0 0 0-5 0V4c-.55 0-1 .45-1 1v4c0 .55.45 1 1 1h5c.55 0 1-.45 1-1V5c0-.55-.45-1-1-1zm-.8 0h-3.4v-.5c0-.94.76-1.7 1.7-1.7s1.7.76 1.7 1.7V4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_phone_missed_24px"><path d="M6.5 5.5L12 11l7-7-1-1-6 6-4.5-4.5H11V3H5v6h1.5V5.5zm17.21 11.17A16.97 16.97 0 0 0 12 12C7.46 12 3.34 13.78.29 16.67c-.18.18-.29.43-.29.71s.11.53.29.71l2.48 2.48c.18.18.43.29.71.29.27 0 .52-.11.7-.28.79-.74 1.69-1.36 2.66-1.85.33-.16.56-.5.56-.9v-3.1c1.45-.48 3-.73 4.6-.73 1.6 0 3.15.25 4.6.72v3.1c0 .39.23.74.56.9.98.49 1.87 1.12 2.67 1.85.18.18.43.28.7.28.28 0 .53-.11.71-.29l2.48-2.48c.18-.18.29-.43.29-.71s-.12-.52-.3-.7z"/></symbol><symbol viewBox="0 0 24 24" id="ic_phone_paused_24px"><path d="M17 3h-2v7h2V3zm3 12.5c-1.25 0-2.45-.2-3.57-.57a1.02 1.02 0 0 0-1.02.24l-2.2 2.2a15.045 15.045 0 0 1-6.59-6.59l2.2-2.21a.96.96 0 0 0 .25-1A11.36 11.36 0 0 1 8.5 4c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1 0 9.39 7.61 17 17 17 .55 0 1-.45 1-1v-3.5c0-.55-.45-1-1-1zM19 3v7h2V3h-2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_power_24px"><path d="M16.01 7L16 3h-2v4h-4V3H8v4h-.01C7 6.99 6 7.99 6 8.99v5.49L9.5 18v3h5v-3l3.5-3.51v-5.5c0-1-1-2-1.99-1.99z"/></symbol><symbol viewBox="0 0 24 24" id="ic_priority_high_24px"><circle cx="12" cy="19" r="2"/><path d="M10 3h4v12h-4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_rv_hookup_24px"><path d="M20 17v-6c0-1.1-.9-2-2-2H7V7l-3 3 3 3v-2h4v3H4v3c0 1.1.9 2 2 2h2c0 1.66 1.34 3 3 3s3-1.34 3-3h8v-2h-2zm-9 3c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1zm7-6h-4v-3h4v3zM17 2v2H9v2h8v2l3-3z"/></symbol><symbol viewBox="0 0 24 24" id="ic_sd_card_24px"><path d="M18 2h-8L4.02 8 4 20c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-6 6h-2V4h2v4zm3 0h-2V4h2v4zm3 0h-2V4h2v4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_sim_card_alert_24px"><path d="M18 2h-8L4.02 8 4 20c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-5 15h-2v-2h2v2zm0-4h-2V8h2v5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_sms_24px"><path d="M20 2H4c-1.1 0-1.99.9-1.99 2L2 22l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zM9 11H7V9h2v2zm4 0h-2V9h2v2zm4 0h-2V9h2v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_sms_failed_24px"><path d="M20 2H4c-1.1 0-1.99.9-1.99 2L2 22l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-7 12h-2v-2h2v2zm0-4h-2V6h2v4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_sync_24px"><path d="M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6 0 1.01-.25 1.97-.7 2.8l1.46 1.46A7.93 7.93 0 0 0 20 12c0-4.42-3.58-8-8-8zm0 14c-3.31 0-6-2.69-6-6 0-1.01.25-1.97.7-2.8L5.24 7.74A7.93 7.93 0 0 0 4 12c0 4.42 3.58 8 8 8v3l4-4-4-4v3z"/></symbol><symbol viewBox="0 0 24 24" id="ic_sync_disabled_24px"><path d="M10 6.35V4.26c-.8.21-1.55.54-2.23.96l1.46 1.46c.25-.12.5-.24.77-.33zm-7.14-.94l2.36 2.36a7.925 7.925 0 0 0 1.14 9.87L4 20h6v-6l-2.24 2.24A6.003 6.003 0 0 1 6 12c0-1 .25-1.94.68-2.77l8.08 8.08c-.25.13-.5.25-.77.34v2.09c.8-.21 1.55-.54 2.23-.96l2.36 2.36 1.27-1.27L4.14 4.14 2.86 5.41zM20 4h-6v6l2.24-2.24A6.003 6.003 0 0 1 18 12c0 1-.25 1.94-.68 2.77l1.46 1.46a7.925 7.925 0 0 0-1.14-9.87L20 4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_sync_problem_24px"><path d="M3 12c0 2.21.91 4.2 2.36 5.64L3 20h6v-6l-2.24 2.24A6.003 6.003 0 0 1 5 12a5.99 5.99 0 0 1 4-5.65V4.26C5.55 5.15 3 8.27 3 12zm8 5h2v-2h-2v2zM21 4h-6v6l2.24-2.24A6.003 6.003 0 0 1 19 12a5.99 5.99 0 0 1-4 5.65v2.09c3.45-.89 6-4.01 6-7.74 0-2.21-.91-4.2-2.36-5.64L21 4zm-10 9h2V7h-2v6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_system_update_24px"><path d="M17 1.01L7 1c-1.1 0-2 .9-2 2v18c0 1.1.9 2 2 2h10c1.1 0 2-.9 2-2V3c0-1.1-.9-1.99-2-1.99zM17 19H7V5h10v14zm-1-6h-3V8h-2v5H8l4 4 4-4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_tap_and_play_24px"><path d="M2 16v2c2.76 0 5 2.24 5 5h2c0-3.87-3.13-7-7-7zm0 4v3h3c0-1.66-1.34-3-3-3zm0-8v2a9 9 0 0 1 9 9h2c0-6.08-4.92-11-11-11zM17 1.01L7 1c-1.1 0-2 .9-2 2v7.37c.69.16 1.36.37 2 .64V5h10v13h-3.03c.52 1.25.84 2.59.95 4H17c1.1 0 2-.9 2-2V3c0-1.1-.9-1.99-2-1.99z"/></symbol><symbol viewBox="0 0 24 24" id="ic_time_to_leave_24px"><path d="M18.92 5.01C18.72 4.42 18.16 4 17.5 4h-11c-.66 0-1.21.42-1.42 1.01L3 11v8c0 .55.45 1 1 1h1c.55 0 1-.45 1-1v-1h12v1c0 .55.45 1 1 1h1c.55 0 1-.45 1-1v-8l-2.08-5.99zM6.5 15c-.83 0-1.5-.67-1.5-1.5S5.67 12 6.5 12s1.5.67 1.5 1.5S7.33 15 6.5 15zm11 0c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zM5 10l1.5-4.5h11L19 10H5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_vibration_24px"><path d="M0 15h2V9H0v6zm3 2h2V7H3v10zm19-8v6h2V9h-2zm-3 8h2V7h-2v10zM16.5 3h-9C6.67 3 6 3.67 6 4.5v15c0 .83.67 1.5 1.5 1.5h9c.83 0 1.5-.67 1.5-1.5v-15c0-.83-.67-1.5-1.5-1.5zM16 19H8V5h8v14z"/></symbol><symbol viewBox="0 0 24 24" id="ic_voice_chat_24px"><path d="M20 2H4c-1.1 0-1.99.9-1.99 2L2 22l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-2 12l-4-3.2V14H6V6h8v3.2L18 6v8z"/></symbol><symbol viewBox="0 0 24 24" id="ic_vpn_lock_24px"><path d="M22 4v-.5a2.5 2.5 0 0 0-5 0V4c-.55 0-1 .45-1 1v4c0 .55.45 1 1 1h5c.55 0 1-.45 1-1V5c0-.55-.45-1-1-1zm-.8 0h-3.4v-.5c0-.94.76-1.7 1.7-1.7s1.7.76 1.7 1.7V4zm-2.28 8c.04.33.08.66.08 1 0 2.08-.8 3.97-2.1 5.39-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H7v-2h2c.55 0 1-.45 1-1V8h2c1.1 0 2-.9 2-2V3.46c-.95-.3-1.95-.46-3-.46C5.48 3 1 7.48 1 13s4.48 10 10 10 10-4.48 10-10c0-.34-.02-.67-.05-1h-2.03zM10 20.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L8 16v1c0 1.1.9 2 2 2v1.93z"/></symbol><symbol viewBox="0 0 24 24" id="ic_wc_24px"><path d="M5.5 22v-7.5H4V9c0-1.1.9-2 2-2h3c1.1 0 2 .9 2 2v5.5H9.5V22h-4zM18 22v-6h3l-2.54-7.63A2.01 2.01 0 0 0 16.56 7h-.12a2 2 0 0 0-1.9 1.37L12 16h3v6h3zM7.5 6c1.11 0 2-.89 2-2s-.89-2-2-2-2 .89-2 2 .89 2 2 2zm9 0c1.11 0 2-.89 2-2s-.89-2-2-2-2 .89-2 2 .89 2 2 2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_wifi_24px"><path d="M1 9l2 2c4.97-4.97 13.03-4.97 18 0l2-2C16.93 2.93 7.08 2.93 1 9zm8 8l3 3 3-3a4.237 4.237 0 0 0-6 0zm-4-4l2 2a7.074 7.074 0 0 1 10 0l2-2C15.14 9.14 8.87 9.14 5 13z"/></symbol></svg>
\ 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 0000000..8a242c0
--- /dev/null
+++ b/skyquake/framework/style/icons/svg-sprite-places-symbol.svg
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><symbol viewBox="0 0 24 24" id="ic_ac_unit_24px"><path d="M22 11h-4.17l3.24-3.24-1.41-1.42L15 11h-2V9l4.66-4.66-1.42-1.41L13 6.17V2h-2v4.17L7.76 2.93 6.34 4.34 11 9v2H9L4.34 6.34 2.93 7.76 6.17 11H2v2h4.17l-3.24 3.24 1.41 1.42L9 13h2v2l-4.66 4.66 1.42 1.41L11 17.83V22h2v-4.17l3.24 3.24 1.42-1.41L13 15v-2h2l4.66 4.66 1.41-1.42L17.83 13H22z"/></symbol><symbol viewBox="0 0 24 24" id="ic_airport_shuttle_24px"><path d="M17 5H3a2 2 0 0 0-2 2v9h2c0 1.65 1.34 3 3 3s3-1.35 3-3h5.5c0 1.65 1.34 3 3 3s3-1.35 3-3H23v-5l-6-6zM3 11V7h4v4H3zm3 6.5c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zm7-6.5H9V7h4v4zm4.5 6.5c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zM15 11V7h1l4 4h-5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_all_inclusive_24px"><path d="M18.6 6.62c-1.44 0-2.8.56-3.77 1.53L12 10.66 10.48 12h.01L7.8 14.39c-.64.64-1.49.99-2.4.99-1.87 0-3.39-1.51-3.39-3.38S3.53 8.62 5.4 8.62c.91 0 1.76.35 2.44 1.03l1.13 1 1.51-1.34L9.22 8.2A5.37 5.37 0 0 0 5.4 6.62C2.42 6.62 0 9.04 0 12s2.42 5.38 5.4 5.38c1.44 0 2.8-.56 3.77-1.53l2.83-2.5.01.01L13.52 12h-.01l2.69-2.39c.64-.64 1.49-.99 2.4-.99 1.87 0 3.39 1.51 3.39 3.38s-1.52 3.38-3.39 3.38c-.9 0-1.76-.35-2.44-1.03l-1.14-1.01-1.51 1.34 1.27 1.12a5.386 5.386 0 0 0 3.82 1.57c2.98 0 5.4-2.41 5.4-5.38s-2.42-5.37-5.4-5.37z"/></symbol><symbol viewBox="0 0 24 24" id="ic_beach_access_24px"><path d="M13.127 14.56l1.43-1.43 6.44 6.443L19.57 21zm4.293-5.73l2.86-2.86c-3.95-3.95-10.35-3.96-14.3-.02 3.93-1.3 8.31-.25 11.44 2.88zM5.95 5.98c-3.94 3.95-3.93 10.35.02 14.3l2.86-2.86C5.7 14.29 4.65 9.91 5.95 5.98zm.02-.02l-.01.01c-.38 3.01 1.17 6.88 4.3 10.02l5.73-5.73c-3.13-3.13-7.01-4.68-10.02-4.3z"/></symbol><symbol viewBox="0 0 24 24" id="ic_business_center_24px"><path d="M10 16v-1H3.01L3 19c0 1.11.89 2 2 2h14c1.11 0 2-.89 2-2v-4h-7v1h-4zm10-9h-4.01V5l-2-2h-4l-2 2v2H4c-1.1 0-2 .9-2 2v3c0 1.11.89 2 2 2h6v-2h4v2h6c1.1 0 2-.9 2-2V9c0-1.1-.9-2-2-2zm-6 0h-4V5h4v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_casino_24px"><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM7.5 18c-.83 0-1.5-.67-1.5-1.5S6.67 15 7.5 15s1.5.67 1.5 1.5S8.33 18 7.5 18zm0-9C6.67 9 6 8.33 6 7.5S6.67 6 7.5 6 9 6.67 9 7.5 8.33 9 7.5 9zm4.5 4.5c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zm4.5 4.5c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zm0-9c-.83 0-1.5-.67-1.5-1.5S15.67 6 16.5 6s1.5.67 1.5 1.5S17.33 9 16.5 9z"/></symbol><symbol viewBox="0 0 24 24" id="ic_child_care_24px"><circle cx="14.5" cy="10.5" r="1.25"/><circle cx="9.5" cy="10.5" r="1.25"/><path d="M22.94 12.66c.04-.21.06-.43.06-.66s-.02-.45-.06-.66a4.008 4.008 0 0 0-2.81-3.17 9.114 9.114 0 0 0-2.19-2.91C16.36 3.85 14.28 3 12 3s-4.36.85-5.94 2.26c-.92.81-1.67 1.8-2.19 2.91a3.994 3.994 0 0 0-2.81 3.17c-.04.21-.06.43-.06.66s.02.45.06.66a4.008 4.008 0 0 0 2.81 3.17 8.977 8.977 0 0 0 2.17 2.89C7.62 20.14 9.71 21 12 21s4.38-.86 5.97-2.28c.9-.8 1.65-1.79 2.17-2.89a3.998 3.998 0 0 0 2.8-3.17zM19 14c-.1 0-.19-.02-.29-.03-.2.67-.49 1.29-.86 1.86C16.6 17.74 14.45 19 12 19s-4.6-1.26-5.85-3.17c-.37-.57-.66-1.19-.86-1.86-.1.01-.19.03-.29.03-1.1 0-2-.9-2-2s.9-2 2-2c.1 0 .19.02.29.03.2-.67.49-1.29.86-1.86C7.4 6.26 9.55 5 12 5s4.6 1.26 5.85 3.17c.37.57.66 1.19.86 1.86.1-.01.19-.03.29-.03 1.1 0 2 .9 2 2s-.9 2-2 2zM7.5 14c.76 1.77 2.49 3 4.5 3s3.74-1.23 4.5-3h-9z"/></symbol><symbol viewBox="0 0 24 24" id="ic_child_friendly_24px"><path d="M13 2v8h8c0-4.42-3.58-8-8-8zm6.32 13.89A7.948 7.948 0 0 0 21 11H6.44l-.95-2H2v2h2.22s1.89 4.07 2.12 4.42A3.49 3.49 0 0 0 4.5 18.5C4.5 20.43 6.07 22 8 22c1.76 0 3.22-1.3 3.46-3h2.08c.24 1.7 1.7 3 3.46 3 1.93 0 3.5-1.57 3.5-3.5 0-1.04-.46-1.97-1.18-2.61zM8 20c-.83 0-1.5-.67-1.5-1.5S7.17 17 8 17s1.5.67 1.5 1.5S8.83 20 8 20zm9 0c-.83 0-1.5-.67-1.5-1.5S16.17 17 17 17s1.5.67 1.5 1.5S17.83 20 17 20z"/></symbol><symbol viewBox="0 0 24 24" id="ic_fitness_center_24px"><path d="M20.57 14.86L22 13.43 20.57 12 17 15.57 8.43 7 12 3.43 10.57 2 9.14 3.43 7.71 2 5.57 4.14 4.14 2.71 2.71 4.14l1.43 1.43L2 7.71l1.43 1.43L2 10.57 3.43 12 7 8.43 15.57 17 12 20.57 13.43 22l1.43-1.43L16.29 22l2.14-2.14 1.43 1.43 1.43-1.43-1.43-1.43L22 16.29z"/></symbol><symbol viewBox="0 0 24 24" id="ic_free_breakfast_24px"><path d="M20 3H4v10a4 4 0 0 0 4 4h6a4 4 0 0 0 4-4v-3h2a2 2 0 0 0 2-2V5c0-1.11-.89-2-2-2zm0 5h-2V5h2v3zM4 19h16v2H4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_golf_course_24px"><circle cx="19.5" cy="19.5" r="1.5"/><path d="M17 5.92L9 2v18H7v-1.73c-1.79.35-3 .99-3 1.73 0 1.1 2.69 2 6 2s6-.9 6-2c0-.99-2.16-1.81-5-1.97V8.98l6-3.06z"/></symbol><symbol viewBox="0 0 24 24" id="ic_hot_tub_24px"><circle cx="7" cy="6" r="2"/><path d="M11.15 12c-.31-.22-.59-.46-.82-.72l-1.4-1.55c-.19-.21-.43-.38-.69-.5-.29-.14-.62-.23-.96-.23h-.03C6.01 9 5 10.01 5 11.25V12H2v8c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2v-8H11.15zM7 20H5v-6h2v6zm4 0H9v-6h2v6zm4 0h-2v-6h2v6zm4 0h-2v-6h2v6zm-.35-14.14l-.07-.07c-.57-.62-.82-1.41-.67-2.2L18 3h-1.89l-.06.43c-.2 1.36.27 2.71 1.3 3.72l.07.06c.57.62.82 1.41.67 2.2l-.11.59h1.91l.06-.43c.21-1.36-.27-2.71-1.3-3.71zm-4 0l-.07-.07c-.57-.62-.82-1.41-.67-2.2L14 3h-1.89l-.06.43c-.2 1.36.27 2.71 1.3 3.72l.07.06c.57.62.82 1.41.67 2.2l-.11.59h1.91l.06-.43c.21-1.36-.27-2.71-1.3-3.71z"/></symbol><symbol viewBox="0 0 24 24" id="ic_kitchen_24px"><path d="M18 2.01L6 2a2 2 0 0 0-2 2v16c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.11-.9-1.99-2-1.99zM18 20H6v-9.02h12V20zm0-11H6V4h12v5zM8 5h2v3H8zm0 7h2v5H8z"/></symbol><symbol viewBox="0 0 24 24" id="ic_pool_24px"><path d="M22 21c-1.11 0-1.73-.37-2.18-.64-.37-.22-.6-.36-1.15-.36-.56 0-.78.13-1.15.36-.46.27-1.07.64-2.18.64s-1.73-.37-2.18-.64c-.37-.22-.6-.36-1.15-.36-.56 0-.78.13-1.15.36-.46.27-1.08.64-2.19.64-1.11 0-1.73-.37-2.18-.64-.37-.23-.6-.36-1.15-.36s-.78.13-1.15.36c-.46.27-1.08.64-2.19.64v-2c.56 0 .78-.13 1.15-.36.46-.27 1.08-.64 2.19-.64s1.73.37 2.18.64c.37.23.59.36 1.15.36.56 0 .78-.13 1.15-.36.46-.27 1.08-.64 2.19-.64 1.11 0 1.73.37 2.18.64.37.22.6.36 1.15.36s.78-.13 1.15-.36c.45-.27 1.07-.64 2.18-.64s1.73.37 2.18.64c.37.23.59.36 1.15.36v2zm0-4.5c-1.11 0-1.73-.37-2.18-.64-.37-.22-.6-.36-1.15-.36-.56 0-.78.13-1.15.36-.45.27-1.07.64-2.18.64s-1.73-.37-2.18-.64c-.37-.22-.6-.36-1.15-.36-.56 0-.78.13-1.15.36-.45.27-1.07.64-2.18.64s-1.73-.37-2.18-.64c-.37-.22-.6-.36-1.15-.36s-.78.13-1.15.36c-.47.27-1.09.64-2.2.64v-2c.56 0 .78-.13 1.15-.36.45-.27 1.07-.64 2.18-.64s1.73.37 2.18.64c.37.22.6.36 1.15.36.56 0 .78-.13 1.15-.36.45-.27 1.07-.64 2.18-.64s1.73.37 2.18.64c.37.22.6.36 1.15.36s.78-.13 1.15-.36c.45-.27 1.07-.64 2.18-.64s1.73.37 2.18.64c.37.22.6.36 1.15.36v2zM8.67 12c.56 0 .78-.13 1.15-.36.46-.27 1.08-.64 2.19-.64 1.11 0 1.73.37 2.18.64.37.22.6.36 1.15.36s.78-.13 1.15-.36c.12-.07.26-.15.41-.23L10.48 5C8.93 3.45 7.5 2.99 5 3v2.5c1.82-.01 2.89.39 4 1.5l1 1-3.25 3.25c.31.12.56.27.77.39.37.23.59.36 1.15.36z"/><circle cx="16.5" cy="5.5" r="2.5"/></symbol><symbol viewBox="0 0 24 24" id="ic_room_service_24px"><path d="M2 17h20v2H2zm11.84-9.21A2.006 2.006 0 0 0 12 5a2.006 2.006 0 0 0-1.84 2.79C6.25 8.6 3.27 11.93 3 16h18c-.27-4.07-3.25-7.4-7.16-8.21z"/></symbol><symbol viewBox="0 0 24 24" id="ic_rv_hookup_24px"><path d="M20 17v-6c0-1.1-.9-2-2-2H7V7l-3 3 3 3v-2h4v3H4v3c0 1.1.9 2 2 2h2c0 1.66 1.34 3 3 3s3-1.34 3-3h8v-2h-2zm-9 3c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1zm7-6h-4v-3h4v3zM17 2v2H9v2h8v2l3-3z"/></symbol><symbol viewBox="0 0 24 24" id="ic_smoke_free_24px"><path d="M2 6l6.99 7H2v3h9.99l7 7 1.26-1.25-17-17zm18.5 7H22v3h-1.5zM18 13h1.5v3H18zm.85-8.12c.62-.61 1-1.45 1-2.38h-1.5c0 1.02-.83 1.85-1.85 1.85v1.5c2.24 0 4 1.83 4 4.07V12H22V9.92c0-2.23-1.28-4.15-3.15-5.04zM14.5 8.7h1.53c1.05 0 1.97.74 1.97 2.05V12h1.5v-1.59c0-1.8-1.6-3.16-3.47-3.16H14.5c-1.02 0-1.85-.98-1.85-2s.83-1.75 1.85-1.75V2a3.35 3.35 0 0 0 0 6.7zm2.5 7.23V13h-2.93z"/></symbol><symbol viewBox="0 0 24 24" id="ic_smoking_rooms_24px"><path d="M2 16h15v3H2zm18.5 0H22v3h-1.5zM18 16h1.5v3H18zm.85-8.27c.62-.61 1-1.45 1-2.38C19.85 3.5 18.35 2 16.5 2v1.5c1.02 0 1.85.83 1.85 1.85S17.52 7.2 16.5 7.2v1.5c2.24 0 4 1.83 4 4.07V15H22v-2.24c0-2.22-1.28-4.14-3.15-5.03zm-2.82 2.47H14.5c-1.02 0-1.85-.98-1.85-2s.83-1.75 1.85-1.75v-1.5a3.35 3.35 0 0 0 0 6.7h1.53c1.05 0 1.97.74 1.97 2.05V15h1.5v-1.64c0-1.81-1.6-3.16-3.47-3.16z"/></symbol><symbol viewBox="0 0 24 24" id="ic_spa_24px"><path fill="#607D8B" d="M8.55 12zm10.43-1.61z"/><path d="M15.49 9.63c-.18-2.79-1.31-5.51-3.43-7.63a12.188 12.188 0 0 0-3.55 7.63c1.28.68 2.46 1.56 3.49 2.63 1.03-1.06 2.21-1.94 3.49-2.63zm-6.5 2.65c-.14-.1-.3-.19-.45-.29.15.11.31.19.45.29zm6.42-.25c-.13.09-.27.16-.4.26.13-.1.27-.17.4-.26zM12 15.45C9.85 12.17 6.18 10 2 10c0 5.32 3.36 9.82 8.03 11.49.63.23 1.29.4 1.97.51.68-.12 1.33-.29 1.97-.51C18.64 19.82 22 15.32 22 10c-4.18 0-7.85 2.17-10 5.45z"/></symbol></svg>
\ 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 0000000..d9f371d
--- /dev/null
+++ b/skyquake/framework/style/icons/svg-sprite-social-symbol.svg
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><symbol viewBox="0 0 24 24" id="ic_cake_24px"><path d="M12 6a2 2 0 0 0 2-2c0-.38-.1-.73-.29-1.03L12 0l-1.71 2.97c-.19.3-.29.65-.29 1.03 0 1.1.9 2 2 2zm4.6 9.99l-1.07-1.07-1.08 1.07c-1.3 1.3-3.58 1.31-4.89 0l-1.07-1.07-1.09 1.07C6.75 16.64 5.88 17 4.96 17c-.73 0-1.4-.23-1.96-.61V21c0 .55.45 1 1 1h16c.55 0 1-.45 1-1v-4.61c-.56.38-1.23.61-1.96.61-.92 0-1.79-.36-2.44-1.01zM18 9h-5V7h-2v2H6c-1.66 0-3 1.34-3 3v1.54c0 1.08.88 1.96 1.96 1.96.52 0 1.02-.2 1.38-.57l2.14-2.13 2.13 2.13c.74.74 2.03.74 2.77 0l2.14-2.13 2.13 2.13c.37.37.86.57 1.38.57 1.08 0 1.96-.88 1.96-1.96V12C21 10.34 19.66 9 18 9z"/></symbol><symbol viewBox="0 0 24 24" id="ic_domain_24px"><path d="M12 7V3H2v18h20V7H12zM6 19H4v-2h2v2zm0-4H4v-2h2v2zm0-4H4V9h2v2zm0-4H4V5h2v2zm4 12H8v-2h2v2zm0-4H8v-2h2v2zm0-4H8V9h2v2zm0-4H8V5h2v2zm10 12h-8v-2h2v-2h-2v-2h2v-2h-2V9h8v10zm-2-8h-2v2h2v-2zm0 4h-2v2h2v-2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_group_24px"><path d="M16 11c1.66 0 2.99-1.34 2.99-3S17.66 5 16 5c-1.66 0-3 1.34-3 3s1.34 3 3 3zm-8 0c1.66 0 2.99-1.34 2.99-3S9.66 5 8 5C6.34 5 5 6.34 5 8s1.34 3 3 3zm0 2c-2.33 0-7 1.17-7 3.5V19h14v-2.5c0-2.33-4.67-3.5-7-3.5zm8 0c-.29 0-.62.02-.97.05 1.16.84 1.97 1.97 1.97 3.45V19h6v-2.5c0-2.33-4.67-3.5-7-3.5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_group_add_24px"><path d="M8 10H5V7H3v3H0v2h3v3h2v-3h3v-2zm10 1c1.66 0 2.99-1.34 2.99-3S19.66 5 18 5c-.32 0-.63.05-.91.14.57.81.9 1.79.9 2.86s-.34 2.04-.9 2.86c.28.09.59.14.91.14zm-5 0c1.66 0 2.99-1.34 2.99-3S14.66 5 13 5c-1.66 0-3 1.34-3 3s1.34 3 3 3zm6.62 2.16c.83.73 1.38 1.66 1.38 2.84v2h3v-2c0-1.54-2.37-2.49-4.38-2.84zM13 13c-2 0-6 1-6 3v2h12v-2c0-2-4-3-6-3z"/></symbol><symbol viewBox="0 0 24 24" id="ic_location_city_24px"><path d="M15 11V5l-3-3-3 3v2H3v14h18V11h-6zm-8 8H5v-2h2v2zm0-4H5v-2h2v2zm0-4H5V9h2v2zm6 8h-2v-2h2v2zm0-4h-2v-2h2v2zm0-4h-2V9h2v2zm0-4h-2V5h2v2zm6 12h-2v-2h2v2zm0-4h-2v-2h2v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_mood_24px"><path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm3.5-9c.83 0 1.5-.67 1.5-1.5S16.33 8 15.5 8 14 8.67 14 9.5s.67 1.5 1.5 1.5zm-7 0c.83 0 1.5-.67 1.5-1.5S9.33 8 8.5 8 7 8.67 7 9.5 7.67 11 8.5 11zm3.5 6.5c2.33 0 4.31-1.46 5.11-3.5H6.89c.8 2.04 2.78 3.5 5.11 3.5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_mood_bad_24px"><path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm3.5-9c.83 0 1.5-.67 1.5-1.5S16.33 8 15.5 8 14 8.67 14 9.5s.67 1.5 1.5 1.5zm-7 0c.83 0 1.5-.67 1.5-1.5S9.33 8 8.5 8 7 8.67 7 9.5 7.67 11 8.5 11zm3.5 3c-2.33 0-4.31 1.46-5.11 3.5h10.22c-.8-2.04-2.78-3.5-5.11-3.5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_notifications_24px"><path d="M12 22c1.1 0 2-.9 2-2h-4a2 2 0 0 0 2 2zm6-6v-5c0-3.07-1.64-5.64-4.5-6.32V4c0-.83-.67-1.5-1.5-1.5s-1.5.67-1.5 1.5v.68C7.63 5.36 6 7.92 6 11v5l-2 2v1h16v-1l-2-2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_notifications_active_24px"><path d="M7.58 4.08L6.15 2.65C3.75 4.48 2.17 7.3 2.03 10.5h2a8.445 8.445 0 0 1 3.55-6.42zm12.39 6.42h2a10.49 10.49 0 0 0-4.12-7.85l-1.42 1.43a8.495 8.495 0 0 1 3.54 6.42zM18 11c0-3.07-1.64-5.64-4.5-6.32V4c0-.83-.67-1.5-1.5-1.5s-1.5.67-1.5 1.5v.68C7.63 5.36 6 7.92 6 11v5l-2 2v1h16v-1l-2-2v-5zm-6 11c.14 0 .27-.01.4-.04a2.03 2.03 0 0 0 1.44-1.18c.1-.24.15-.5.15-.78h-4c.01 1.1.9 2 2.01 2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_notifications_none_24px"><path d="M12 22c1.1 0 2-.9 2-2h-4c0 1.1.9 2 2 2zm6-6v-5c0-3.07-1.63-5.64-4.5-6.32V4c0-.83-.67-1.5-1.5-1.5s-1.5.67-1.5 1.5v.68C7.64 5.36 6 7.92 6 11v5l-2 2v1h16v-1l-2-2zm-2 1H8v-6c0-2.48 1.51-4.5 4-4.5s4 2.02 4 4.5v6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_notifications_off_24px"><path d="M20 18.69L7.84 6.14 5.27 3.49 4 4.76l2.8 2.8v.01c-.52.99-.8 2.16-.8 3.42v5l-2 2v1h13.73l2 2L21 19.72l-1-1.03zM12 22c1.11 0 2-.89 2-2h-4c0 1.11.89 2 2 2zm6-7.32V11c0-3.08-1.64-5.64-4.5-6.32V4c0-.83-.67-1.5-1.5-1.5s-1.5.67-1.5 1.5v.68c-.15.03-.29.08-.42.12-.1.03-.2.07-.3.11h-.01c-.01 0-.01 0-.02.01-.23.09-.46.2-.68.31 0 0-.01 0-.01.01L18 14.68z"/></symbol><symbol viewBox="0 0 24 24" id="ic_notifications_paused_24px"><path d="M12 22c1.1 0 2-.9 2-2h-4a2 2 0 0 0 2 2zm6-6v-5c0-3.07-1.64-5.64-4.5-6.32V4c0-.83-.67-1.5-1.5-1.5s-1.5.67-1.5 1.5v.68C7.63 5.36 6 7.93 6 11v5l-2 2v1h16v-1l-2-2zm-3.5-6.2l-2.8 3.4h2.8V15h-5v-1.8l2.8-3.4H9.5V8h5v1.8z"/></symbol><symbol viewBox="0 0 24 24" id="ic_pages_24px"><path d="M3 5v6h5L7 7l4 1V3H5c-1.1 0-2 .9-2 2zm5 8H3v6c0 1.1.9 2 2 2h6v-5l-4 1 1-4zm9 4l-4-1v5h6c1.1 0 2-.9 2-2v-6h-5l1 4zm2-14h-6v5l4-1-1 4h5V5c0-1.1-.9-2-2-2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_party_mode_24px"><path d="M20 4h-3.17L15 2H9L7.17 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm-8 3c1.63 0 3.06.79 3.98 2H12c-1.66 0-3 1.34-3 3 0 .35.07.69.18 1H7.1A5.002 5.002 0 0 1 12 7zm0 10c-1.63 0-3.06-.79-3.98-2H12c1.66 0 3-1.34 3-3 0-.35-.07-.69-.18-1h2.08a5.002 5.002 0 0 1-4.9 6z"/></symbol><symbol viewBox="0 0 24 24" id="ic_people_24px"><path d="M16 11c1.66 0 2.99-1.34 2.99-3S17.66 5 16 5c-1.66 0-3 1.34-3 3s1.34 3 3 3zm-8 0c1.66 0 2.99-1.34 2.99-3S9.66 5 8 5C6.34 5 5 6.34 5 8s1.34 3 3 3zm0 2c-2.33 0-7 1.17-7 3.5V19h14v-2.5c0-2.33-4.67-3.5-7-3.5zm8 0c-.29 0-.62.02-.97.05 1.16.84 1.97 1.97 1.97 3.45V19h6v-2.5c0-2.33-4.67-3.5-7-3.5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_people_outline_24px"><path d="M16.5 13c-1.2 0-3.07.34-4.5 1-1.43-.67-3.3-1-4.5-1C5.33 13 1 14.08 1 16.25V19h22v-2.75c0-2.17-4.33-3.25-6.5-3.25zm-4 4.5h-10v-1.25c0-.54 2.56-1.75 5-1.75s5 1.21 5 1.75v1.25zm9 0H14v-1.25c0-.46-.2-.86-.52-1.22.88-.3 1.96-.53 3.02-.53 2.44 0 5 1.21 5 1.75v1.25zM7.5 12c1.93 0 3.5-1.57 3.5-3.5S9.43 5 7.5 5 4 6.57 4 8.5 5.57 12 7.5 12zm0-5.5c1.1 0 2 .9 2 2s-.9 2-2 2-2-.9-2-2 .9-2 2-2zm9 5.5c1.93 0 3.5-1.57 3.5-3.5S18.43 5 16.5 5 13 6.57 13 8.5s1.57 3.5 3.5 3.5zm0-5.5c1.1 0 2 .9 2 2s-.9 2-2 2-2-.9-2-2 .9-2 2-2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_person_24px"><path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_person_add_24px"><path d="M15 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm-9-2V7H4v3H1v2h3v3h2v-3h3v-2H6zm9 4c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_person_outline_24px"><path d="M12 5.9a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2m0 9c2.97 0 6.1 1.46 6.1 2.1v1.1H5.9V17c0-.64 3.13-2.1 6.1-2.1M12 4C9.79 4 8 5.79 8 8s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm0 9c-2.67 0-8 1.34-8 4v3h16v-3c0-2.66-5.33-4-8-4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_plus_one_24px"><path d="M10 8H8v4H4v2h4v4h2v-4h4v-2h-4zm4.5-1.92V7.9l2.5-.5V18h2V5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_poll_24px"><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM9 17H7v-7h2v7zm4 0h-2V7h2v10zm4 0h-2v-4h2v4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_public_24px"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39z"/></symbol><symbol viewBox="0 0 24 24" id="ic_school_24px"><path d="M5 13.18v4L12 21l7-3.82v-4L12 17l-7-3.82zM12 3L1 9l11 6 9-4.91V17h2V9L12 3z"/></symbol><symbol viewBox="0 0 24 24" id="ic_sentiment_dissatisfied_24px"><circle cx="15.5" cy="9.5" r="1.5"/><circle cx="8.5" cy="9.5" r="1.5"/><path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm0-6c-2.33 0-4.32 1.45-5.12 3.5h1.67c.69-1.19 1.97-2 3.45-2s2.75.81 3.45 2h1.67c-.8-2.05-2.79-3.5-5.12-3.5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_sentiment_neutral_24px"><path d="M9 14h6v1.5H9z"/><circle cx="15.5" cy="9.5" r="1.5"/><circle cx="8.5" cy="9.5" r="1.5"/><path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z"/></symbol><symbol viewBox="0 0 24 24" id="ic_sentiment_satisfied_24px"><circle cx="15.5" cy="9.5" r="1.5"/><circle cx="8.5" cy="9.5" r="1.5"/><path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm0-4c-1.48 0-2.75-.81-3.45-2H6.88a5.495 5.495 0 0 0 10.24 0h-1.67c-.7 1.19-1.97 2-3.45 2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_sentiment_very_dissatisfied_24px"><path d="M11.99 2C6.47 2 2 6.47 2 12s4.47 10 9.99 10S22 17.53 22 12 17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm4.18-12.24l-1.06 1.06-1.06-1.06L13 8.82l1.06 1.06L13 10.94 14.06 12l1.06-1.06L16.18 12l1.06-1.06-1.06-1.06 1.06-1.06zM7.82 12l1.06-1.06L9.94 12 11 10.94 9.94 9.88 11 8.82 9.94 7.76 8.88 8.82 7.82 7.76 6.76 8.82l1.06 1.06-1.06 1.06zM12 14c-2.33 0-4.31 1.46-5.11 3.5h10.22c-.8-2.04-2.78-3.5-5.11-3.5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_sentiment_very_satisfied_24px"><path d="M11.99 2C6.47 2 2 6.47 2 12s4.47 10 9.99 10S22 17.53 22 12 17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm1-10.06L14.06 11l1.06-1.06L16.18 11l1.06-1.06-2.12-2.12zm-4.12 0L9.94 11 11 9.94 8.88 7.82 6.76 9.94 7.82 11zM12 17.5c2.33 0 4.31-1.46 5.11-3.5H6.89c.8 2.04 2.78 3.5 5.11 3.5z"/></symbol><symbol viewBox="0 0 24 24" id="ic_share_24px"><path d="M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7s-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81 1.66 0 3-1.34 3-3s-1.34-3-3-3-3 1.34-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9c-1.66 0-3 1.34-3 3s1.34 3 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.16c-.05.21-.08.43-.08.65 0 1.61 1.31 2.92 2.92 2.92 1.61 0 2.92-1.31 2.92-2.92s-1.31-2.92-2.92-2.92z"/></symbol><symbol viewBox="0 0 24 24" id="ic_whatshot_24px"><path d="M13.5.67s.74 2.65.74 4.8c0 2.06-1.35 3.73-3.41 3.73-2.07 0-3.63-1.67-3.63-3.73l.03-.36C5.21 7.51 4 10.62 4 14c0 4.42 3.58 8 8 8s8-3.58 8-8C20 8.61 17.41 3.8 13.5.67zM11.71 19c-1.78 0-3.22-1.4-3.22-3.14 0-1.62 1.05-2.76 2.81-3.12 1.77-.36 3.6-1.21 4.62-2.58.39 1.29.59 2.65.59 4.04a4.8 4.8 0 0 1-4.8 4.8z"/></symbol></svg>
\ 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 0000000..5d4705b
--- /dev/null
+++ b/skyquake/framework/style/icons/svg-sprite-toggle-symbol.svg
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><symbol viewBox="0 0 24 24" id="ic_check_box_24px"><path d="M19 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2zm-9 14l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/></symbol><symbol viewBox="0 0 24 24" id="ic_check_box_outline_blank_24px"><path d="M19 5v14H5V5h14m0-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_indeterminate_check_box_24px"><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-2 10H7v-2h10v2z"/></symbol><symbol viewBox="0 0 24 24" id="ic_radio_button_checked_24px"><path d="M12 7c-2.76 0-5 2.24-5 5s2.24 5 5 5 5-2.24 5-5-2.24-5-5-5zm0-5C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z"/></symbol><symbol viewBox="0 0 24 24" id="ic_radio_button_unchecked_24px"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z"/></symbol><symbol viewBox="0 0 24 24" id="ic_star_24px"><path d="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z"/></symbol><symbol viewBox="0 0 24 24" id="ic_star_border_24px"><path d="M22 9.24l-7.19-.62L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21 12 17.27 18.18 21l-1.63-7.03L22 9.24zM12 15.4l-3.76 2.27 1-4.28-3.32-2.88 4.38-.38L12 6.1l1.71 4.04 4.38.38-3.32 2.88 1 4.28L12 15.4z"/></symbol><symbol viewBox="0 0 24 24" id="ic_star_half_24px"><path d="M22 9.24l-7.19-.62L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21 12 17.27 18.18 21l-1.63-7.03L22 9.24zM12 15.4V6.1l1.71 4.04 4.38.38-3.32 2.88 1 4.28L12 15.4z"/></symbol></svg>
\ 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 0000000..c40cb8c
--- /dev/null
+++ b/skyquake/framework/style/img/avatar.png
Binary files differ
diff --git a/skyquake/framework/style/img/bargraph.png b/skyquake/framework/style/img/bargraph.png
new file mode 100644
index 0000000..2b25d7f
--- /dev/null
+++ b/skyquake/framework/style/img/bargraph.png
Binary files 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 0000000..9eb06a4
--- /dev/null
+++ b/skyquake/framework/style/img/bearer-plane-diagram.png
Binary files 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 0000000..0ab6c94
--- /dev/null
+++ b/skyquake/framework/style/img/bg-tile-cross-small.png
Binary files differ
diff --git a/skyquake/framework/style/img/catalog-default.svg b/skyquake/framework/style/img/catalog-default.svg
new file mode 100644
index 0000000..ef6efc7
--- /dev/null
+++ b/skyquake/framework/style/img/catalog-default.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 90.8 78.5"><path class="st0" d="M70.8 58.2c-0.7 0-1.4 0.1-2 0.3L51.5 42.7c0.7-1.1 1.2-2.4 1.2-3.8 0-1.4-0.4-2.7-1.2-3.8l15.1-13.9c1.1 0.9 2.6 1.5 4.2 1.5 3.7 0 6.7-3 6.7-6.6 0-3.7-3-6.6-6.7-6.6 -3.7 0-6.7 3-6.7 6.6 0 1.4 0.4 2.7 1.2 3.8L50.2 33.8c-1.1-0.9-2.6-1.5-4.2-1.5 -1.6 0-3.1 0.6-4.2 1.5L26.7 19.9c0.7-1.1 1.2-2.4 1.2-3.8 0-3.7-3-6.6-6.7-6.6 -3.7 0-6.7 3-6.7 6.6 0 3.3 2.5 6.1 5.7 6.6v35.6c-3.2 0.5-5.7 3.2-5.7 6.6 0 3.7 3 6.6 6.7 6.6 3.1 0 5.8-2.2 6.5-5.1h36.7c0.7 2.9 3.3 5.1 6.5 5.1 3.7 0 6.7-3 6.7-6.6C77.5 61.2 74.5 58.2 70.8 58.2zM64.2 64.5H27.8c-0.1-2.1-1.2-4-2.8-5.1l16.7-15.4c1.1 0.9 2.6 1.5 4.2 1.5 1.6 0 3.1-0.6 4.2-1.5L67 59.4C65.3 60.6 64.3 62.4 64.2 64.5zM25.4 21.3l15.1 13.9c-0.7 1.1-1.2 2.4-1.2 3.8 0 1.4 0.4 2.7 1.2 3.8L23.2 58.5c-0.3-0.1-0.7-0.2-1.1-0.2V22.7C23.4 22.5 24.5 22 25.4 21.3z"/></svg>
\ 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 0000000..e2a4a89
--- /dev/null
+++ b/skyquake/framework/style/img/catalog-nsd-default.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 90.8 78.5" preserveAspectRatio="none"><path class="st0" d="M325.55,87.34c0,34.54-27.71,62.55-61.89,62.55s-61.89-28-61.89-62.55S229.48,24.8,263.66,24.8,325.55,52.8,325.55,87.34Zm-220.1,96.09c-34.18,0-61.89,28-61.89,62.55s27.71,62.55,61.89,62.55,61.89-28,61.89-62.55S139.63,183.43,105.45,183.43ZM259,235.18c-34.18,0-61.89,28-61.89,62.55s27.71,62.55,61.89,62.55,61.89-28,61.89-62.55S293.23,235.18,259,235.18Zm153.6-51.74c-34.18,0-61.89,28-61.89,62.55s27.71,62.55,61.89,62.55,61.89-28,61.89-62.55S446.83,183.43,412.64,183.43ZM213,127.14l-66,80.5m116.59-57.75v85.29m42.76-104.42,64,64.68" transform="scale(0.15, 0.15)"/></svg>
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 0000000..4dafa78
--- /dev/null
+++ b/skyquake/framework/style/img/catalog-vnfd-default.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 90.8 78.5" preserveAspectRatio="none"><path class="st0" d="M241.72,51.26c-92.14,0-166.83,75.48-166.83,168.59s74.69,168.59,166.83,168.59S408.55,313,408.55,219.85,333.86,51.26,241.72,51.26Z" transform="scale(0.15, 0.15)"/></svg>
diff --git a/skyquake/framework/style/img/create-account.png b/skyquake/framework/style/img/create-account.png
new file mode 100644
index 0000000..16aeeb4
--- /dev/null
+++ b/skyquake/framework/style/img/create-account.png
Binary files 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 0000000..d5b7498
--- /dev/null
+++ b/skyquake/framework/style/img/create-fleet-params-temp.png
Binary files 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 0000000..2a330ec
--- /dev/null
+++ b/skyquake/framework/style/img/create-fleet-pool-temp.png
Binary files 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 0000000..2b4578b
--- /dev/null
+++ b/skyquake/framework/style/img/create-fleet-services-temp.png
Binary files 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 0000000..39156d1
--- /dev/null
+++ b/skyquake/framework/style/img/diameter-openflow-lte-icon.png
Binary files differ
diff --git a/skyquake/framework/style/img/firewall-icon.png b/skyquake/framework/style/img/firewall-icon.png
new file mode 100644
index 0000000..c65caf4
--- /dev/null
+++ b/skyquake/framework/style/img/firewall-icon.png
Binary files differ
diff --git a/skyquake/framework/style/img/gbps-10.png b/skyquake/framework/style/img/gbps-10.png
new file mode 100644
index 0000000..1327ab9
--- /dev/null
+++ b/skyquake/framework/style/img/gbps-10.png
Binary files differ
diff --git a/skyquake/framework/style/img/gbps-50.png b/skyquake/framework/style/img/gbps-50.png
new file mode 100644
index 0000000..9cfb805
--- /dev/null
+++ b/skyquake/framework/style/img/gbps-50.png
Binary files 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 0000000..87bc437
--- /dev/null
+++ b/skyquake/framework/style/img/green-page-icon.png
Binary files differ
diff --git a/skyquake/framework/style/img/header-logo.png b/skyquake/framework/style/img/header-logo.png
new file mode 100644
index 0000000..09b47c7
--- /dev/null
+++ b/skyquake/framework/style/img/header-logo.png
Binary files differ
diff --git a/skyquake/framework/style/img/host-icon.png b/skyquake/framework/style/img/host-icon.png
new file mode 100644
index 0000000..0c508a1
--- /dev/null
+++ b/skyquake/framework/style/img/host-icon.png
Binary files 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 0000000..1254c22
--- /dev/null
+++ b/skyquake/framework/style/img/icon-host-sm.png
Binary files differ
diff --git a/skyquake/framework/style/img/icon-host.png b/skyquake/framework/style/img/icon-host.png
new file mode 100644
index 0000000..0c508a1
--- /dev/null
+++ b/skyquake/framework/style/img/icon-host.png
Binary files 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 0000000..4ed206e
--- /dev/null
+++ b/skyquake/framework/style/img/icon-open-viewport.png
Binary files differ
diff --git a/skyquake/framework/style/img/icon-switch.png b/skyquake/framework/style/img/icon-switch.png
new file mode 100644
index 0000000..88a7715
--- /dev/null
+++ b/skyquake/framework/style/img/icon-switch.png
Binary files 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 0000000..1e10ac0
--- /dev/null
+++ b/skyquake/framework/style/img/iot-industry-icon.png
Binary files 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 0000000..352f787
--- /dev/null
+++ b/skyquake/framework/style/img/iot-medical-icon.png
Binary files 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 0000000..6eb2c19
--- /dev/null
+++ b/skyquake/framework/style/img/iot-transportation-icon-active.png
Binary files 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 0000000..b90ca15
--- /dev/null
+++ b/skyquake/framework/style/img/ip-lte-icon.png
Binary files 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 0000000..0a7f5f9
--- /dev/null
+++ b/skyquake/framework/style/img/ip-softgre-icon.png
Binary files differ
diff --git a/skyquake/framework/style/img/latency.png b/skyquake/framework/style/img/latency.png
new file mode 100644
index 0000000..fd3371e
--- /dev/null
+++ b/skyquake/framework/style/img/latency.png
Binary files differ
diff --git a/skyquake/framework/style/img/latency_graph.png b/skyquake/framework/style/img/latency_graph.png
new file mode 100644
index 0000000..03e6de7
--- /dev/null
+++ b/skyquake/framework/style/img/latency_graph.png
Binary files 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 0000000..ab89e52
--- /dev/null
+++ b/skyquake/framework/style/img/launchpad-add-fleet-icon.png
Binary files 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 0000000..04d91a3
--- /dev/null
+++ b/skyquake/framework/style/img/launchpad-graph-temp.png
Binary files 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 0000000..6e7e370
--- /dev/null
+++ b/skyquake/framework/style/img/launchpad-graphs-temp/10.09.png
Binary files 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 0000000..1bbe9fe
--- /dev/null
+++ b/skyquake/framework/style/img/launchpad-graphs-temp/17.23.png
Binary files 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 0000000..2d78371
--- /dev/null
+++ b/skyquake/framework/style/img/launchpad-graphs-temp/20.05.png
Binary files 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 0000000..ee05baf
--- /dev/null
+++ b/skyquake/framework/style/img/launchpad-graphs-temp/23.08.png
Binary files 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 0000000..87931d8
--- /dev/null
+++ b/skyquake/framework/style/img/launchpad-graphs-temp/25.03.png
Binary files 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 0000000..8c8be88
--- /dev/null
+++ b/skyquake/framework/style/img/launchpad-graphs-temp/30.56.png
Binary files 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 0000000..8cc9c1a
--- /dev/null
+++ b/skyquake/framework/style/img/launchpad-graphs-temp/7.08.png
Binary files 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 0000000..e724306
--- /dev/null
+++ b/skyquake/framework/style/img/launchpad-graphs-temp/9.24.png
Binary files differ
diff --git a/skyquake/framework/style/img/link.png b/skyquake/framework/style/img/link.png
new file mode 100644
index 0000000..33210cc
--- /dev/null
+++ b/skyquake/framework/style/img/link.png
Binary files differ
diff --git a/skyquake/framework/style/img/loadbalance-icon.png b/skyquake/framework/style/img/loadbalance-icon.png
new file mode 100644
index 0000000..4164786
--- /dev/null
+++ b/skyquake/framework/style/img/loadbalance-icon.png
Binary files 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 0000000..6eb898b
--- /dev/null
+++ b/skyquake/framework/style/img/lte-mme-icon.png
Binary files differ
diff --git a/skyquake/framework/style/img/osm_header.png b/skyquake/framework/style/img/osm_header.png
new file mode 100644
index 0000000..c67dca3
--- /dev/null
+++ b/skyquake/framework/style/img/osm_header.png
Binary files 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 0000000..3055122
--- /dev/null
+++ b/skyquake/framework/style/img/osm_header_253x50.png
Binary files 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 0000000..4b7a344
--- /dev/null
+++ b/skyquake/framework/style/img/osm_header_506x100.png
Binary files differ
diff --git a/skyquake/framework/style/img/page_loader.gif b/skyquake/framework/style/img/page_loader.gif
new file mode 100644
index 0000000..07b9f3f
--- /dev/null
+++ b/skyquake/framework/style/img/page_loader.gif
Binary files 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 0000000..06f3510
--- /dev/null
+++ b/skyquake/framework/style/img/platform-nav-temp.png
Binary files 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 0000000..f7f2866
--- /dev/null
+++ b/skyquake/framework/style/img/platform-pages-temp/1a-fleet-platform-resources.png
Binary files 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 0000000..041325d
--- /dev/null
+++ b/skyquake/framework/style/img/platform-pages-temp/1a-fleet-platform-traffic.png
Binary files 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 0000000..f7f2866
--- /dev/null
+++ b/skyquake/framework/style/img/platform-pages-temp/1b-fleet-platform-resources.png
Binary files 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 0000000..041325d
--- /dev/null
+++ b/skyquake/framework/style/img/platform-pages-temp/1b-fleet-platform-traffic.png
Binary files 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 0000000..f7f2866
--- /dev/null
+++ b/skyquake/framework/style/img/platform-pages-temp/2a-fleet-platform-resources.png
Binary files 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 0000000..041325d
--- /dev/null
+++ b/skyquake/framework/style/img/platform-pages-temp/2a-fleet-platform-traffic.png
Binary files 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 0000000..f7f2866
--- /dev/null
+++ b/skyquake/framework/style/img/platform-pages-temp/2b-fleet-platform-resources.png
Binary files 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 0000000..041325d
--- /dev/null
+++ b/skyquake/framework/style/img/platform-pages-temp/2b-fleet-platform-traffic.png
Binary files 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 0000000..f7f2866
--- /dev/null
+++ b/skyquake/framework/style/img/platform-pages-temp/3a-fleet-platform-resources.png
Binary files 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 0000000..041325d
--- /dev/null
+++ b/skyquake/framework/style/img/platform-pages-temp/3a-fleet-platform-traffic.png
Binary files 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 0000000..f7f2866
--- /dev/null
+++ b/skyquake/framework/style/img/platform-pages-temp/3b-fleet-platform-resources.png
Binary files 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 0000000..041325d
--- /dev/null
+++ b/skyquake/framework/style/img/platform-pages-temp/3b-fleet-platform-traffic.png
Binary files differ
diff --git a/skyquake/framework/style/img/router-icon.png b/skyquake/framework/style/img/router-icon.png
new file mode 100644
index 0000000..93282df
--- /dev/null
+++ b/skyquake/framework/style/img/router-icon.png
Binary files 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 0000000..1a6566d
--- /dev/null
+++ b/skyquake/framework/style/img/so-pages-temp/ipTrafTemp.png
Binary files 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 0000000..f22abf4
--- /dev/null
+++ b/skyquake/framework/style/img/so-pages-temp/securityTemp.png
Binary files 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 0000000..d364ff8
--- /dev/null
+++ b/skyquake/framework/style/img/svg/launch-fleet-icn-close.svg
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 51 51" enable-background="new 0 0 51 51" xml:space="preserve">
+<polygon id="XMLID_5_" fill-rule="evenodd" clip-rule="evenodd" fill="#333333" points="40.7,7.9 39.3,6.5 23,22.8 6.7,6.5 5.3,7.9
+ 21.6,24.2 5.3,40.4 6.7,41.8 23,25.6 39.3,41.8 40.7,40.4 24.4,24.2 "/>
+<polygon id="XMLID_4_" opacity="0.15" fill-rule="evenodd" clip-rule="evenodd" fill="#333333" points="45.1,41.7 28.8,25.4
+ 43.7,10.6 40.8,7.7 26,22.6 11.2,7.7 8.3,10.6 23.2,25.4 6.9,41.7 9.7,44.5 26,28.2 42.3,44.5 "/>
+</svg>
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 0000000..b62dc8a
--- /dev/null
+++ b/skyquake/framework/style/img/svg/launch-fleet-icn-edit.svg
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 28 35.3" enable-background="new 0 0 28 35.3" xml:space="preserve">
+<g>
+ <g id="XMLID_22_">
+ <path id="XMLID_23_" fill="none" stroke="#333333" stroke-linejoin="round" stroke-miterlimit="10" d="M22.9,8.9l-5-3.4
+ C17.3,5,16.4,5.1,16,5.7l-1.2,1.7l7.3,5l1.2-1.7C23.7,10.1,23.5,9.3,22.9,8.9z M6.5,19.1c-0.3,2.2,5.1,6,7.3,5L21,13.9l-7.3-5
+ L6.5,19.1z M5.3,20.8c-0.5,1.1-1,6.4-0.8,7.8c0,0.3,0.1,0.4,0.2,0.5c0.5,0.4,6.8-2.6,7.8-3.4C10.3,26.4,5.3,22.9,5.3,20.8z"/>
+ </g>
+</g>
+</svg>
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 0000000..f3de69a
--- /dev/null
+++ b/skyquake/framework/style/img/svg/launch-fleet-network-01-active.svg
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 90.8 78.5" style="enable-background:new 0 0 90.8 78.5;" xml:space="preserve">
+<style type="text/css">
+ .st0{fill:#FFFFFF;}
+</style>
+<g>
+ <g id="XMLID_70_">
+ <path id="XMLID_71_" class="st0" d="M71.1,57.3V21.2c3.3-0.5,5.8-3.3,5.8-6.7c0-3.7-3-6.7-6.8-6.7c-3.4,0-6.2,2.5-6.7,5.8h-37
+ c-0.5-3.3-3.3-5.8-6.7-5.8c-3.7,0-6.8,3-6.8,6.7c0,3.4,2.5,6.2,5.8,6.7v36.2c-3.3,0.5-5.8,3.3-5.8,6.7c0,3.7,3,6.7,6.8,6.7
+ c3.2,0,5.9-2.2,6.6-5.1h37.3c0.7,3,3.4,5.1,6.6,5.1c3.7,0,6.8-3,6.8-6.7C76.9,60.6,74.4,57.8,71.1,57.3z M68,57.6L50.5,41.5
+ c0.8-1.1,1.2-2.4,1.2-3.8c0-1.4-0.4-2.7-1.2-3.8l15.3-14.1c0.9,0.8,2.1,1.3,3.3,1.4v36.2C68.7,57.4,68.4,57.4,68,57.6z M64.5,18.3
+ L49.2,32.4c-1.2-0.9-2.7-1.5-4.3-1.5s-3.1,0.6-4.3,1.5L25.3,18.3c0.6-0.8,1-1.8,1.1-2.9h37C63.5,16.5,63.9,17.5,64.5,18.3z
+ M23.9,19.7l15.3,14.1c-0.8,1.1-1.2,2.4-1.2,3.8c0,1.4,0.4,2.7,1.2,3.8L21.7,57.6c-0.4-0.1-0.7-0.2-1.1-0.2V21.2
+ C21.9,21,23,20.5,23.9,19.7z M23.6,58.5l17-15.6c1.2,0.9,2.7,1.5,4.3,1.5s3.1-0.6,4.3-1.5l17,15.6c-1.6,1.2-2.7,3-2.8,5.2H26.4
+ C26.3,61.5,25.2,59.7,23.6,58.5z"/>
+ </g>
+</g>
+</svg>
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 0000000..19700d2
--- /dev/null
+++ b/skyquake/framework/style/img/svg/launch-fleet-network-01-inactive.svg
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 90.8 78.5" style="enable-background:new 0 0 90.8 78.5;" xml:space="preserve">
+<style type="text/css">
+ .st0{fill:#CCCCCC;}
+</style>
+<g>
+ <g id="XMLID_44_">
+ <path id="XMLID_45_" class="st0" d="M71.1,57.3V21.2c3.3-0.5,5.8-3.3,5.8-6.7c0-3.7-3-6.7-6.8-6.7c-3.4,0-6.2,2.5-6.7,5.8h-37
+ c-0.5-3.3-3.3-5.8-6.7-5.8c-3.7,0-6.8,3-6.8,6.7c0,3.4,2.5,6.2,5.8,6.7v36.2c-3.3,0.5-5.8,3.3-5.8,6.7c0,3.7,3,6.7,6.8,6.7
+ c3.2,0,5.9-2.2,6.6-5.1h37.3c0.7,3,3.4,5.1,6.6,5.1c3.7,0,6.8-3,6.8-6.7C76.9,60.6,74.4,57.8,71.1,57.3z M68,57.6L50.5,41.5
+ c0.8-1.1,1.2-2.4,1.2-3.8c0-1.4-0.4-2.7-1.2-3.8l15.3-14.1c0.9,0.8,2.1,1.3,3.3,1.4v36.2C68.7,57.4,68.4,57.4,68,57.6z M64.5,18.3
+ L49.2,32.4c-1.2-0.9-2.7-1.5-4.3-1.5s-3.1,0.6-4.3,1.5L25.3,18.3c0.6-0.8,1-1.8,1.1-2.9h37C63.5,16.5,63.9,17.5,64.5,18.3z
+ M23.9,19.7l15.3,14.1c-0.8,1.1-1.2,2.4-1.2,3.8c0,1.4,0.4,2.7,1.2,3.8L21.7,57.6c-0.4-0.1-0.7-0.2-1.1-0.2V21.2
+ C21.9,21,23,20.5,23.9,19.7z M23.6,58.5l17-15.6c1.2,0.9,2.7,1.5,4.3,1.5s3.1-0.6,4.3-1.5l17,15.6c-1.6,1.2-2.7,3-2.8,5.2H26.4
+ C26.3,61.5,25.2,59.7,23.6,58.5z"/>
+ </g>
+</g>
+</svg>
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 0000000..c29934b
--- /dev/null
+++ b/skyquake/framework/style/img/svg/launch-fleet-network-02-active.svg
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 90.8 78.5" style="enable-background:new 0 0 90.8 78.5;" xml:space="preserve">
+<style type="text/css">
+ .st0{fill:#FFFFFF;}
+</style>
+<g>
+ <g id="XMLID_40_">
+ <path id="XMLID_41_" class="st0" d="M65.9,41.2c0.2,1.1,0.6,2.1,1.5,3c2.1,2.1,5.4,2.1,7.5,0c2.1-2.1,2.1-5.4,0-7.5
+ c-2.1-2.1-5.4-2.1-7.5,0c-0.8,0.8-1.3,1.9-1.5,3l-15.2,0c-0.1-0.8-0.5-1.7-1-2.4L68,19c2.1,1.6,5,1.4,6.9-0.5
+ c2.1-2.1,2.1-5.4,0-7.5c-2.1-2.1-5.4-2.1-7.5,0c-1.9,1.9-2,4.8-0.5,6.9L48.6,36.3c-0.7-0.5-1.5-0.9-2.3-1L46.2,20
+ c1.1-0.2,2.1-0.6,3-1.5c2.1-2.1,2.1-5.4,0-7.5c-2.1-2.1-5.4-2.1-7.5,0c-2.1,2.1-2.1,5.4,0,7.5c0.8,0.8,1.9,1.3,3,1.5l0.1,15.3
+ c-1,0.1-2,0.6-2.8,1.3L23.5,18.5c2-2.1,2-5.4,0-7.5C21.4,9,18.1,9,16,11c-2.1,2.1-2.1,5.4,0,7.5c1.7,1.7,4.3,2,6.3,0.9L41,37.7
+ c-0.4,0.6-0.6,1.3-0.8,2.1l-15.2,0c-0.2-1.1-0.6-2.2-1.5-3c-2.1-2.1-5.4-2.1-7.5,0c-2.1,2.1-2.1,5.4,0,7.5c2.1,2.1,5.4,2.1,7.5,0
+ c0.8-0.8,1.3-1.9,1.5-2.9l15.2,0c0.1,0.9,0.5,1.7,1,2.4L22.9,62c-2.1-1.6-5-1.4-6.9,0.5c-2.1,2.1-2.1,5.4,0,7.5
+ c2.1,2.1,5.4,2.1,7.5,0c1.9-1.9,2-4.8,0.5-6.9l18.3-18.3c0.8,0.6,1.6,0.9,2.5,1l0.1,15.2c-1.2,0.1-2.3,0.6-3.2,1.5
+ c-2.1,2.1-2.1,5.4,0,7.5c2.1,2.1,5.4,2.1,7.5,0c2.1-2.1,2.1-5.4,0-7.5c-0.8-0.8-1.8-1.3-2.8-1.5l-0.1-15.2
+ c0.7-0.1,1.4-0.4,2.1-0.8l18.5,18.1c-1.5,2.1-1.4,5,0.5,6.9c2.1,2.1,5.4,2.1,7.5,0c2.1-2.1,2.1-5.4,0-7.5c-1.9-1.9-4.9-2-6.9-0.4
+ L49.5,43.9c0.7-0.8,1.1-1.7,1.2-2.7L65.9,41.2z"/>
+ </g>
+</g>
+</svg>
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 0000000..4d15e2b
--- /dev/null
+++ b/skyquake/framework/style/img/svg/launch-fleet-network-02-inactive.svg
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 90.8 78.5" style="enable-background:new 0 0 90.8 78.5;" xml:space="preserve">
+<style type="text/css">
+ .st0{fill:#CCCCCC;}
+</style>
+<g>
+ <g id="XMLID_40_">
+ <path id="XMLID_41_" class="st0" d="M65.9,41.2c0.2,1.1,0.6,2.1,1.5,3c2.1,2.1,5.4,2.1,7.5,0c2.1-2.1,2.1-5.4,0-7.5
+ c-2.1-2.1-5.4-2.1-7.5,0c-0.8,0.8-1.3,1.9-1.5,3l-15.2,0c-0.1-0.8-0.5-1.7-1-2.4L68,19c2.1,1.6,5,1.4,6.9-0.5
+ c2.1-2.1,2.1-5.4,0-7.5c-2.1-2.1-5.4-2.1-7.5,0c-1.9,1.9-2,4.8-0.5,6.9L48.6,36.3c-0.7-0.5-1.5-0.9-2.3-1L46.2,20
+ c1.1-0.2,2.1-0.6,3-1.5c2.1-2.1,2.1-5.4,0-7.5c-2.1-2.1-5.4-2.1-7.5,0c-2.1,2.1-2.1,5.4,0,7.5c0.8,0.8,1.9,1.3,3,1.5l0.1,15.3
+ c-1,0.1-2,0.6-2.8,1.3L23.5,18.5c2-2.1,2-5.4,0-7.5C21.4,9,18.1,9,16,11c-2.1,2.1-2.1,5.4,0,7.5c1.7,1.7,4.3,2,6.3,0.9L41,37.7
+ c-0.4,0.6-0.6,1.3-0.8,2.1l-15.2,0c-0.2-1.1-0.6-2.2-1.5-3c-2.1-2.1-5.4-2.1-7.5,0c-2.1,2.1-2.1,5.4,0,7.5c2.1,2.1,5.4,2.1,7.5,0
+ c0.8-0.8,1.3-1.9,1.5-2.9l15.2,0c0.1,0.9,0.5,1.7,1,2.4L22.9,62c-2.1-1.6-5-1.4-6.9,0.5c-2.1,2.1-2.1,5.4,0,7.5
+ c2.1,2.1,5.4,2.1,7.5,0c1.9-1.9,2-4.8,0.5-6.9l18.3-18.3c0.8,0.6,1.6,0.9,2.5,1l0.1,15.2c-1.2,0.1-2.3,0.6-3.2,1.5
+ c-2.1,2.1-2.1,5.4,0,7.5c2.1,2.1,5.4,2.1,7.5,0c2.1-2.1,2.1-5.4,0-7.5c-0.8-0.8-1.8-1.3-2.8-1.5l-0.1-15.2
+ c0.7-0.1,1.4-0.4,2.1-0.8l18.5,18.1c-1.5,2.1-1.4,5,0.5,6.9c2.1,2.1,5.4,2.1,7.5,0c2.1-2.1,2.1-5.4,0-7.5c-1.9-1.9-4.9-2-6.9-0.4
+ L49.5,43.9c0.7-0.8,1.1-1.7,1.2-2.7L65.9,41.2z"/>
+ </g>
+</g>
+</svg>
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 0000000..d1b1aec
--- /dev/null
+++ b/skyquake/framework/style/img/svg/launch-fleet-network-03-active.svg
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 90.8 78.5" style="enable-background:new 0 0 90.8 78.5;" xml:space="preserve">
+<style type="text/css">
+ .st0{fill:#FFFFFF;}
+</style>
+<g>
+ <g id="XMLID_58_">
+ <path id="XMLID_59_" class="st0" d="M70.8,58.2c-0.7,0-1.4,0.1-2,0.3L51.5,42.7c0.7-1.1,1.2-2.4,1.2-3.8c0-1.4-0.4-2.7-1.2-3.8
+ l15.1-13.9c1.1,0.9,2.6,1.5,4.2,1.5c3.7,0,6.7-3,6.7-6.6c0-3.7-3-6.6-6.7-6.6c-3.7,0-6.7,3-6.7,6.6c0,1.4,0.4,2.7,1.2,3.8
+ L50.2,33.8c-1.1-0.9-2.6-1.5-4.2-1.5c-1.6,0-3.1,0.6-4.2,1.5L26.7,19.9c0.7-1.1,1.2-2.4,1.2-3.8c0-3.7-3-6.6-6.7-6.6
+ c-3.7,0-6.7,3-6.7,6.6c0,3.3,2.5,6.1,5.7,6.6v35.6c-3.2,0.5-5.7,3.2-5.7,6.6c0,3.7,3,6.6,6.7,6.6c3.1,0,5.8-2.2,6.5-5.1h36.7
+ c0.7,2.9,3.3,5.1,6.5,5.1c3.7,0,6.7-3,6.7-6.6C77.5,61.2,74.5,58.2,70.8,58.2z M64.2,64.5H27.8c-0.1-2.1-1.2-4-2.8-5.1l16.7-15.4
+ c1.1,0.9,2.6,1.5,4.2,1.5c1.6,0,3.1-0.6,4.2-1.5L67,59.4C65.3,60.6,64.3,62.4,64.2,64.5z M25.4,21.3l15.1,13.9
+ c-0.7,1.1-1.2,2.4-1.2,3.8c0,1.4,0.4,2.7,1.2,3.8L23.2,58.5c-0.3-0.1-0.7-0.2-1.1-0.2V22.7C23.4,22.5,24.5,22,25.4,21.3z"/>
+ </g>
+</g>
+</svg>
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 0000000..03c74fd
--- /dev/null
+++ b/skyquake/framework/style/img/svg/launch-fleet-network-03-inactive.svg
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 90.8 78.5" style="enable-background:new 0 0 90.8 78.5;" xml:space="preserve">
+<style type="text/css">
+ .st0{fill:#CCCCCC;}
+</style>
+<g>
+ <g id="XMLID_58_">
+ <path id="XMLID_59_" class="st0" d="M70.8,58.2c-0.7,0-1.4,0.1-2,0.3L51.5,42.7c0.7-1.1,1.2-2.4,1.2-3.8c0-1.4-0.4-2.7-1.2-3.8
+ l15.1-13.9c1.1,0.9,2.6,1.5,4.2,1.5c3.7,0,6.7-3,6.7-6.6c0-3.7-3-6.6-6.7-6.6c-3.7,0-6.7,3-6.7,6.6c0,1.4,0.4,2.7,1.2,3.8
+ L50.2,33.8c-1.1-0.9-2.6-1.5-4.2-1.5c-1.6,0-3.1,0.6-4.2,1.5L26.7,19.9c0.7-1.1,1.2-2.4,1.2-3.8c0-3.7-3-6.6-6.7-6.6
+ c-3.7,0-6.7,3-6.7,6.6c0,3.3,2.5,6.1,5.7,6.6v35.6c-3.2,0.5-5.7,3.2-5.7,6.6c0,3.7,3,6.6,6.7,6.6c3.1,0,5.8-2.2,6.5-5.1h36.7
+ c0.7,2.9,3.3,5.1,6.5,5.1c3.7,0,6.7-3,6.7-6.6C77.5,61.2,74.5,58.2,70.8,58.2z M64.2,64.5H27.8c-0.1-2.1-1.2-4-2.8-5.1l16.7-15.4
+ c1.1,0.9,2.6,1.5,4.2,1.5c1.6,0,3.1-0.6,4.2-1.5L67,59.4C65.3,60.6,64.3,62.4,64.2,64.5z M25.4,21.3l15.1,13.9
+ c-0.7,1.1-1.2,2.4-1.2,3.8c0,1.4,0.4,2.7,1.2,3.8L23.2,58.5c-0.3-0.1-0.7-0.2-1.1-0.2V22.7C23.4,22.5,24.5,22,25.4,21.3z"/>
+ </g>
+</g>
+</svg>
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 0000000..774653a
--- /dev/null
+++ b/skyquake/framework/style/img/svg/launch-fleet-network-04-active.svg
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 90.8 78.5" style="enable-background:new 0 0 90.8 78.5;" xml:space="preserve">
+<style type="text/css">
+ .st0{fill:#FFFFFF;}
+</style>
+<g>
+ <g id="XMLID_76_">
+ <path id="XMLID_77_" class="st0" d="M17,26.5c-3.1,0-5.5,2.5-5.5,5.5s2.5,5.5,5.5,5.5c3.1,0,5.5-2.5,5.5-5.5S20.1,26.5,17,26.5z
+ M17,64.4c-3.1,0-5.5,2.5-5.5,5.5s2.5,5.5,5.5,5.5c3.1,0,5.5-2.5,5.5-5.5S20.1,64.4,17,64.4z M36,18.6c1.2,0,2.4-0.4,3.3-1.1
+ l11.2,11.2c-0.7,0.9-1.1,2.1-1.1,3.3c0,3.1,2.5,5.5,5.5,5.5c3.1,0,5.5-2.5,5.5-5.5S58,26.5,55,26.5c-1.2,0-2.4,0.4-3.3,1.1
+ L40.4,16.3c0.7-0.9,1.1-2.1,1.1-3.3c0-3.1-2.5-5.5-5.5-5.5c-3.1,0-5.5,2.5-5.5,5.5S33,18.6,36,18.6z M74,18.6
+ c3.1,0,5.5-2.5,5.5-5.5S77,7.5,74,7.5S68.4,10,68.4,13S70.9,18.6,74,18.6z M36,45.5c-3.1,0-5.5,2.5-5.5,5.5s2.5,5.5,5.5,5.5
+ c3.1,0,5.5-2.5,5.5-5.5S39.1,45.5,36,45.5z M74,45.5c-3.1,0-5.5,2.5-5.5,5.5c0,1.5,0.6,2.9,1.6,3.9L58.9,66c-1-1-2.4-1.6-3.9-1.6
+ c-3.1,0-5.5,2.5-5.5,5.5s2.5,5.5,5.5,5.5c3.1,0,5.5-2.5,5.5-5.5c0-1-0.3-1.9-0.7-2.7l11.5-11.5c0.8,0.4,1.7,0.7,2.7,0.7
+ c3.1,0,5.5-2.5,5.5-5.5S77,45.5,74,45.5z"/>
+ </g>
+</g>
+</svg>
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 0000000..3897a1e
--- /dev/null
+++ b/skyquake/framework/style/img/svg/launch-fleet-network-04-inactive.svg
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 90.8 78.5" style="enable-background:new 0 0 90.8 78.5;" xml:space="preserve">
+<style type="text/css">
+ .st0{fill:#CCCCCC;}
+</style>
+<g>
+ <g id="XMLID_76_">
+ <path id="XMLID_77_" class="st0" d="M17,26.5c-3.1,0-5.5,2.5-5.5,5.5s2.5,5.5,5.5,5.5c3.1,0,5.5-2.5,5.5-5.5S20.1,26.5,17,26.5z
+ M17,64.4c-3.1,0-5.5,2.5-5.5,5.5s2.5,5.5,5.5,5.5c3.1,0,5.5-2.5,5.5-5.5S20.1,64.4,17,64.4z M36,18.6c1.2,0,2.4-0.4,3.3-1.1
+ l11.2,11.2c-0.7,0.9-1.1,2.1-1.1,3.3c0,3.1,2.5,5.5,5.5,5.5c3.1,0,5.5-2.5,5.5-5.5S58,26.5,55,26.5c-1.2,0-2.4,0.4-3.3,1.1
+ L40.4,16.3c0.7-0.9,1.1-2.1,1.1-3.3c0-3.1-2.5-5.5-5.5-5.5c-3.1,0-5.5,2.5-5.5,5.5S33,18.6,36,18.6z M74,18.6
+ c3.1,0,5.5-2.5,5.5-5.5S77,7.5,74,7.5S68.4,10,68.4,13S70.9,18.6,74,18.6z M36,45.5c-3.1,0-5.5,2.5-5.5,5.5s2.5,5.5,5.5,5.5
+ c3.1,0,5.5-2.5,5.5-5.5S39.1,45.5,36,45.5z M74,45.5c-3.1,0-5.5,2.5-5.5,5.5c0,1.5,0.6,2.9,1.6,3.9L58.9,66c-1-1-2.4-1.6-3.9-1.6
+ c-3.1,0-5.5,2.5-5.5,5.5s2.5,5.5,5.5,5.5c3.1,0,5.5-2.5,5.5-5.5c0-1-0.3-1.9-0.7-2.7l11.5-11.5c0.8,0.4,1.7,0.7,2.7,0.7
+ c3.1,0,5.5-2.5,5.5-5.5S77,45.5,74,45.5z"/>
+ </g>
+</g>
+</svg>
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 0000000..774653a
--- /dev/null
+++ b/skyquake/framework/style/img/svg/launch-fleet-network-05-active.svg
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 90.8 78.5" style="enable-background:new 0 0 90.8 78.5;" xml:space="preserve">
+<style type="text/css">
+ .st0{fill:#FFFFFF;}
+</style>
+<g>
+ <g id="XMLID_76_">
+ <path id="XMLID_77_" class="st0" d="M17,26.5c-3.1,0-5.5,2.5-5.5,5.5s2.5,5.5,5.5,5.5c3.1,0,5.5-2.5,5.5-5.5S20.1,26.5,17,26.5z
+ M17,64.4c-3.1,0-5.5,2.5-5.5,5.5s2.5,5.5,5.5,5.5c3.1,0,5.5-2.5,5.5-5.5S20.1,64.4,17,64.4z M36,18.6c1.2,0,2.4-0.4,3.3-1.1
+ l11.2,11.2c-0.7,0.9-1.1,2.1-1.1,3.3c0,3.1,2.5,5.5,5.5,5.5c3.1,0,5.5-2.5,5.5-5.5S58,26.5,55,26.5c-1.2,0-2.4,0.4-3.3,1.1
+ L40.4,16.3c0.7-0.9,1.1-2.1,1.1-3.3c0-3.1-2.5-5.5-5.5-5.5c-3.1,0-5.5,2.5-5.5,5.5S33,18.6,36,18.6z M74,18.6
+ c3.1,0,5.5-2.5,5.5-5.5S77,7.5,74,7.5S68.4,10,68.4,13S70.9,18.6,74,18.6z M36,45.5c-3.1,0-5.5,2.5-5.5,5.5s2.5,5.5,5.5,5.5
+ c3.1,0,5.5-2.5,5.5-5.5S39.1,45.5,36,45.5z M74,45.5c-3.1,0-5.5,2.5-5.5,5.5c0,1.5,0.6,2.9,1.6,3.9L58.9,66c-1-1-2.4-1.6-3.9-1.6
+ c-3.1,0-5.5,2.5-5.5,5.5s2.5,5.5,5.5,5.5c3.1,0,5.5-2.5,5.5-5.5c0-1-0.3-1.9-0.7-2.7l11.5-11.5c0.8,0.4,1.7,0.7,2.7,0.7
+ c3.1,0,5.5-2.5,5.5-5.5S77,45.5,74,45.5z"/>
+ </g>
+</g>
+</svg>
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 0000000..3897a1e
--- /dev/null
+++ b/skyquake/framework/style/img/svg/launch-fleet-network-05-inactive.svg
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 90.8 78.5" style="enable-background:new 0 0 90.8 78.5;" xml:space="preserve">
+<style type="text/css">
+ .st0{fill:#CCCCCC;}
+</style>
+<g>
+ <g id="XMLID_76_">
+ <path id="XMLID_77_" class="st0" d="M17,26.5c-3.1,0-5.5,2.5-5.5,5.5s2.5,5.5,5.5,5.5c3.1,0,5.5-2.5,5.5-5.5S20.1,26.5,17,26.5z
+ M17,64.4c-3.1,0-5.5,2.5-5.5,5.5s2.5,5.5,5.5,5.5c3.1,0,5.5-2.5,5.5-5.5S20.1,64.4,17,64.4z M36,18.6c1.2,0,2.4-0.4,3.3-1.1
+ l11.2,11.2c-0.7,0.9-1.1,2.1-1.1,3.3c0,3.1,2.5,5.5,5.5,5.5c3.1,0,5.5-2.5,5.5-5.5S58,26.5,55,26.5c-1.2,0-2.4,0.4-3.3,1.1
+ L40.4,16.3c0.7-0.9,1.1-2.1,1.1-3.3c0-3.1-2.5-5.5-5.5-5.5c-3.1,0-5.5,2.5-5.5,5.5S33,18.6,36,18.6z M74,18.6
+ c3.1,0,5.5-2.5,5.5-5.5S77,7.5,74,7.5S68.4,10,68.4,13S70.9,18.6,74,18.6z M36,45.5c-3.1,0-5.5,2.5-5.5,5.5s2.5,5.5,5.5,5.5
+ c3.1,0,5.5-2.5,5.5-5.5S39.1,45.5,36,45.5z M74,45.5c-3.1,0-5.5,2.5-5.5,5.5c0,1.5,0.6,2.9,1.6,3.9L58.9,66c-1-1-2.4-1.6-3.9-1.6
+ c-3.1,0-5.5,2.5-5.5,5.5s2.5,5.5,5.5,5.5c3.1,0,5.5-2.5,5.5-5.5c0-1-0.3-1.9-0.7-2.7l11.5-11.5c0.8,0.4,1.7,0.7,2.7,0.7
+ c3.1,0,5.5-2.5,5.5-5.5S77,45.5,74,45.5z"/>
+ </g>
+</g>
+</svg>
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 0000000..31d7ae3
--- /dev/null
+++ b/skyquake/framework/style/img/svg/launch-fleet-network-06-active.svg
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 90.8 78.5" style="enable-background:new 0 0 90.8 78.5;" xml:space="preserve">
+<style type="text/css">
+ .st0{fill:#FFFFFF;}
+</style>
+<g id="XMLID_28_">
+ <g id="XMLID_88_">
+ <path id="XMLID_89_" class="st0" d="M76.9,65.5v-9.7c1.1-0.1,2.2-0.6,3.1-1.4c2-2,2-5.2,0-7.1c-1.7-1.7-4.4-1.9-6.4-0.6L60.4,34
+ c1.4-2,1.2-4.7-0.5-6.5c-1-1-2.3-1.5-3.6-1.5l-5.9-13.3c0.3-0.2,0.6-0.4,0.9-0.7c2-2,2-5.2,0-7.1c-2-2-5.2-2-7.1,0
+ c-2,2-2,5.2,0,7.1c0.3,0.3,0.6,0.5,0.9,0.7l-6,13.4c-1.9-1.1-4.4-0.8-6,0.8c-1.9,1.9-1.9,4.9-0.2,6.8L18.5,46.7
+ c-2-1.4-4.7-1.3-6.5,0.5c-2,2-2,5.2,0,7.1c0.7,0.7,1.7,1.2,2.6,1.4v9.8c-1,0.2-1.9,0.6-2.6,1.4c-2,2-2,5.2,0,7.1c2,2,5.2,2,7.1,0
+ c2-2,2-5.2,0-7.1c-0.9-0.9-2-1.3-3.1-1.4v-9.7c1.1-0.1,2.2-0.6,3.1-1.4c1.8-1.8,1.9-4.7,0.4-6.6l14.4-12.9
+ c0.6,0.4,1.2,0.6,1.8,0.7v10.3c-1,0.2-1.9,0.6-2.7,1.4c-2,2-2,5.2,0,7.1c0.8,0.8,1.7,1.2,2.7,1.4v9.8c-1,0.2-1.9,0.6-2.7,1.4
+ c-2,2-2,5.2,0,7.1c2,2,5.2,2,7.1,0c2-2,2-5.2,0-7.1c-0.8-0.8-1.9-1.3-3-1.4v-9.7c1.1-0.1,2.2-0.6,3-1.4c2-2,2-5.2,0-7.1
+ c-0.8-0.8-1.9-1.3-3-1.4V35.6c1.1-0.1,2.2-0.6,3-1.4c1.9-1.9,2-5.1,0.1-7.1l6.1-13.8c0.9,0.2,1.8,0.2,2.7,0l5.8,12.9
+ c-0.7,0.2-1.4,0.6-2,1.2c-2,2-2,5.2,0,7.1c0.8,0.8,1.8,1.2,2.8,1.4l0,9.7c-1.1,0.1-2.1,0.6-3,1.4c-2,2-2,5.2,0,7.1
+ c0.8,0.8,1.8,1.2,2.8,1.4v9.8c-1,0.2-2,0.6-2.8,1.4c-2,2-2,5.2,0,7.1c2,2,5.2,2,7.1,0c2-2,2-5.2,0-7.1c-0.8-0.8-1.9-1.3-2.9-1.4
+ v-9.7c1.1-0.1,2.1-0.6,2.9-1.4c2-2,2-5.2,0-7.1C58.9,46.4,58,46,57,45.8l0-9.7c0.9-0.1,1.7-0.5,2.4-1l13.2,12.6
+ c-1.6,2-1.5,4.9,0.3,6.7c0.7,0.7,1.7,1.2,2.6,1.4v9.8c-1,0.2-1.9,0.6-2.6,1.4c-2,2-2,5.2,0,7.1c2,2,5.2,2,7.1,0c2-2,2-5.2,0-7.1
+ C79.2,66,78.1,65.6,76.9,65.5z"/>
+ </g>
+</g>
+</svg>
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 0000000..e5a845b
--- /dev/null
+++ b/skyquake/framework/style/img/svg/launch-fleet-network-06-inactive.svg
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 90.8 78.5" style="enable-background:new 0 0 90.8 78.5;" xml:space="preserve">
+<style type="text/css">
+ .st0{fill:#CCCCCC;}
+</style>
+<g id="XMLID_28_">
+ <g id="XMLID_88_">
+ <path id="XMLID_89_" class="st0" d="M76.9,65.5v-9.7c1.1-0.1,2.2-0.6,3.1-1.4c2-2,2-5.2,0-7.1c-1.7-1.7-4.4-1.9-6.4-0.6L60.4,34
+ c1.4-2,1.2-4.7-0.5-6.5c-1-1-2.3-1.5-3.6-1.5l-5.9-13.3c0.3-0.2,0.6-0.4,0.9-0.7c2-2,2-5.2,0-7.1c-2-2-5.2-2-7.1,0
+ c-2,2-2,5.2,0,7.1c0.3,0.3,0.6,0.5,0.9,0.7l-6,13.4c-1.9-1.1-4.4-0.8-6,0.8c-1.9,1.9-1.9,4.9-0.2,6.8L18.5,46.7
+ c-2-1.4-4.7-1.3-6.5,0.5c-2,2-2,5.2,0,7.1c0.7,0.7,1.7,1.2,2.6,1.4v9.8c-1,0.2-1.9,0.6-2.6,1.4c-2,2-2,5.2,0,7.1c2,2,5.2,2,7.1,0
+ c2-2,2-5.2,0-7.1c-0.9-0.9-2-1.3-3.1-1.4v-9.7c1.1-0.1,2.2-0.6,3.1-1.4c1.8-1.8,1.9-4.7,0.4-6.6l14.4-12.9
+ c0.6,0.4,1.2,0.6,1.8,0.7v10.3c-1,0.2-1.9,0.6-2.7,1.4c-2,2-2,5.2,0,7.1c0.8,0.8,1.7,1.2,2.7,1.4v9.8c-1,0.2-1.9,0.6-2.7,1.4
+ c-2,2-2,5.2,0,7.1c2,2,5.2,2,7.1,0c2-2,2-5.2,0-7.1c-0.8-0.8-1.9-1.3-3-1.4v-9.7c1.1-0.1,2.2-0.6,3-1.4c2-2,2-5.2,0-7.1
+ c-0.8-0.8-1.9-1.3-3-1.4V35.6c1.1-0.1,2.2-0.6,3-1.4c1.9-1.9,2-5.1,0.1-7.1l6.1-13.8c0.9,0.2,1.8,0.2,2.7,0l5.8,12.9
+ c-0.7,0.2-1.4,0.6-2,1.2c-2,2-2,5.2,0,7.1c0.8,0.8,1.8,1.2,2.8,1.4l0,9.7c-1.1,0.1-2.1,0.6-3,1.4c-2,2-2,5.2,0,7.1
+ c0.8,0.8,1.8,1.2,2.8,1.4v9.8c-1,0.2-2,0.6-2.8,1.4c-2,2-2,5.2,0,7.1c2,2,5.2,2,7.1,0c2-2,2-5.2,0-7.1c-0.8-0.8-1.9-1.3-2.9-1.4
+ v-9.7c1.1-0.1,2.1-0.6,2.9-1.4c2-2,2-5.2,0-7.1C58.9,46.4,58,46,57,45.8l0-9.7c0.9-0.1,1.7-0.5,2.4-1l13.2,12.6
+ c-1.6,2-1.5,4.9,0.3,6.7c0.7,0.7,1.7,1.2,2.6,1.4v9.8c-1,0.2-1.9,0.6-2.6,1.4c-2,2-2,5.2,0,7.1c2,2,5.2,2,7.1,0c2-2,2-5.2,0-7.1
+ C79.2,66,78.1,65.6,76.9,65.5z"/>
+ </g>
+</g>
+</svg>
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 0000000..4888d81
--- /dev/null
+++ b/skyquake/framework/style/img/svg/launch-fleet-network-07-active.svg
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 90.8 78.5" style="enable-background:new 0 0 90.8 78.5;" xml:space="preserve">
+<style type="text/css">
+ .st0{fill:#FFFFFF;}
+</style>
+<g>
+ <g id="XMLID_70_">
+ <path id="XMLID_71_" class="st0" d="M68.4,56.8V25.2c2.9-0.4,5.1-2.9,5.1-5.8c0-3.3-2.7-5.9-5.9-5.9c-3,0-5.5,2.2-5.9,5.1H29.3
+ c-0.4-2.9-2.9-5.1-5.9-5.1c-3.3,0-5.9,2.6-5.9,5.9c0,3,2.2,5.4,5.1,5.8v31.6c-2.9,0.4-5.1,2.9-5.1,5.8c0,3.3,2.7,5.9,5.9,5.9
+ c2.8,0,5.1-1.9,5.8-4.5h32.6c0.6,2.6,3,4.5,5.8,4.5c3.3,0,5.9-2.6,5.9-5.9C73.5,59.6,71.3,57.2,68.4,56.8z M65.7,57L50.4,42.9
+ c0.7-0.9,1-2.1,1-3.3c0-1.2-0.4-2.4-1-3.3L63.8,24c0.8,0.7,1.8,1.1,2.9,1.3v31.6C66.4,56.8,66.1,56.9,65.7,57z M62.7,22.7L49.2,35
+ c-1-0.8-2.3-1.3-3.7-1.3c-1.4,0-2.7,0.5-3.7,1.3L28.3,22.7c0.5-0.7,0.8-1.6,1-2.5h32.4C61.8,21.2,62.2,22,62.7,22.7z M27.2,24
+ l13.4,12.3c-0.7,0.9-1,2.1-1,3.3c0,1.2,0.4,2.4,1,3.3L25.3,57c-0.3-0.1-0.6-0.2-1-0.2V25.2C25.4,25.1,26.4,24.6,27.2,24z
+ M26.9,57.8l14.9-13.6c1,0.8,2.3,1.3,3.7,1.3c1.4,0,2.7-0.5,3.7-1.3l14.9,13.6c-1.4,1-2.4,2.7-2.5,4.5H29.4
+ C29.3,60.5,28.3,58.8,26.9,57.8z"/>
+ </g>
+</g>
+</svg>
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 0000000..81ee3cc
--- /dev/null
+++ b/skyquake/framework/style/img/svg/launch-fleet-network-07-inactive.svg
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 90.8 78.5" style="enable-background:new 0 0 90.8 78.5;" xml:space="preserve">
+<style type="text/css">
+ .st0{fill:#CCCCCC;}
+</style>
+<g>
+ <g id="XMLID_70_">
+ <path id="XMLID_71_" class="st0" d="M68.4,56.8V25.2c2.9-0.4,5.1-2.9,5.1-5.8c0-3.3-2.7-5.9-5.9-5.9c-3,0-5.5,2.2-5.9,5.1H29.3
+ c-0.4-2.9-2.9-5.1-5.9-5.1c-3.3,0-5.9,2.6-5.9,5.9c0,3,2.2,5.4,5.1,5.8v31.6c-2.9,0.4-5.1,2.9-5.1,5.8c0,3.3,2.7,5.9,5.9,5.9
+ c2.8,0,5.1-1.9,5.8-4.5h32.6c0.6,2.6,3,4.5,5.8,4.5c3.3,0,5.9-2.6,5.9-5.9C73.5,59.6,71.3,57.2,68.4,56.8z M65.7,57L50.4,42.9
+ c0.7-0.9,1-2.1,1-3.3c0-1.2-0.4-2.4-1-3.3L63.8,24c0.8,0.7,1.8,1.1,2.9,1.3v31.6C66.4,56.8,66.1,56.9,65.7,57z M62.7,22.7L49.2,35
+ c-1-0.8-2.3-1.3-3.7-1.3c-1.4,0-2.7,0.5-3.7,1.3L28.3,22.7c0.5-0.7,0.8-1.6,1-2.5h32.4C61.8,21.2,62.2,22,62.7,22.7z M27.2,24
+ l13.4,12.3c-0.7,0.9-1,2.1-1,3.3c0,1.2,0.4,2.4,1,3.3L25.3,57c-0.3-0.1-0.6-0.2-1-0.2V25.2C25.4,25.1,26.4,24.6,27.2,24z
+ M26.9,57.8l14.9-13.6c1,0.8,2.3,1.3,3.7,1.3c1.4,0,2.7-0.5,3.7-1.3l14.9,13.6c-1.4,1-2.4,2.7-2.5,4.5H29.4
+ C29.3,60.5,28.3,58.8,26.9,57.8z"/>
+ </g>
+</g>
+</svg>
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 0000000..9ce2e16
--- /dev/null
+++ b/skyquake/framework/style/img/svg/launch-fleet-network-08-active.svg
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 90.8 78.5" style="enable-background:new 0 0 90.8 78.5;" xml:space="preserve">
+<style type="text/css">
+ .st0{fill:#FFFFFF;}
+</style>
+<g id="XMLID_28_">
+ <g id="XMLID_99_">
+ <path id="XMLID_100_" class="st0" d="M78.1,67.1c-1,0-1.9,0.3-2.7,0.8l-7.8-7.7c0.5-0.8,0.9-1.8,0.9-2.8c0-2.5-1.8-4.5-4.2-4.9
+ V25.9c2.4-0.3,4.2-2.4,4.2-4.9c0-1.1-0.4-2.2-1-3l8.2-8.2c0.8,0.6,1.8,1,3,1c2.7,0,4.9-2.2,4.9-5s-2.2-5-4.9-5
+ c-2.7,0-4.9,2.2-4.9,5c0,1.1,0.4,2.1,1,2.9l-8.2,8.2c-0.8-0.6-1.8-1-2.9-1c-2.5,0-4.5,1.8-4.9,4.2H31.8c-0.3-2.4-2.4-4.2-4.9-4.2
+ c-1.1,0-2,0.3-2.9,0.9l-7.7-7.7c0.6-0.8,1-1.8,1-2.9c0-2.7-2.2-5-4.9-5s-4.9,2.2-4.9,5c0,2.7,2.2,5,4.9,5c1.1,0,2.1-0.4,3-1
+ l7.7,7.7c-0.7,0.8-1.1,1.9-1.1,3c0,2.5,1.8,4.5,4.2,4.9v26.6c-2.4,0.3-4.2,2.4-4.2,4.9c0,1.1,0.4,2.2,1,3l-6.8,6.7
+ c-0.8-0.6-1.8-1-3-1c-2.7,0-4.9,2.2-4.9,5s2.2,5,4.9,5c2.7,0,4.9-2.2,4.9-5c0-1.1-0.4-2.1-1-2.9l6.8-6.8c0.8,0.6,1.8,1,2.9,1
+ c2.3,0,4.2-1.6,4.8-3.8h27.1c0.5,2.2,2.5,3.8,4.8,3.8c1.2,0,2.3-0.4,3.1-1.1l7.7,7.6c-0.7,0.9-1.2,2-1.2,3.2c0,2.7,2.2,5,4.9,5
+ s4.9-2.2,4.9-5C83,69.3,80.8,67.1,78.1,67.1z M48.8,41.5c0.9-0.9,1.4-2.1,1.4-3.4c0-0.8-0.2-1.5-0.5-2.2L60.6,25
+ c0.7,0.5,1.4,0.9,2.3,1v26.6c-0.8,0.1-1.5,0.4-2.1,0.8L48.8,41.5z M59.6,23.9L48.8,34.7c-0.9-1-2.2-1.6-3.6-1.6
+ c-1.4,0-2.6,0.6-3.5,1.5L31,23.9c0.4-0.6,0.7-1.3,0.8-2.1h26.9C58.8,22.6,59.1,23.3,59.6,23.9z M30,24.9l10.9,10.8
+ c-0.4,0.7-0.6,1.5-0.6,2.3c0,1.4,0.6,2.7,1.5,3.6L30,53.5c-0.7-0.5-1.4-0.9-2.3-1V25.9C28.6,25.8,29.4,25.4,30,24.9z M31,54.5
+ L43,42.5c0.7,0.3,1.4,0.6,2.2,0.6c0.9,0,1.7-0.2,2.4-0.6l12.1,12c-0.6,0.8-1,1.8-1.1,2.8H31.9C31.8,56.2,31.5,55.3,31,54.5z"/>
+ </g>
+</g>
+</svg>
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 0000000..02525a6
--- /dev/null
+++ b/skyquake/framework/style/img/svg/launch-fleet-network-08-inactive.svg
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 90.8 78.5" style="enable-background:new 0 0 90.8 78.5;" xml:space="preserve">
+<style type="text/css">
+ .st0{fill:#CCCCCC;}
+</style>
+<g id="XMLID_28_">
+ <g id="XMLID_99_">
+ <path id="XMLID_100_" class="st0" d="M78.1,67.1c-1,0-1.9,0.3-2.7,0.8l-7.8-7.7c0.5-0.8,0.9-1.8,0.9-2.8c0-2.5-1.8-4.5-4.2-4.9
+ V25.9c2.4-0.3,4.2-2.4,4.2-4.9c0-1.1-0.4-2.2-1-3l8.2-8.2c0.8,0.6,1.8,1,3,1c2.7,0,4.9-2.2,4.9-5s-2.2-5-4.9-5
+ c-2.7,0-4.9,2.2-4.9,5c0,1.1,0.4,2.1,1,2.9l-8.2,8.2c-0.8-0.6-1.8-1-2.9-1c-2.5,0-4.5,1.8-4.9,4.2H31.8c-0.3-2.4-2.4-4.2-4.9-4.2
+ c-1.1,0-2,0.3-2.9,0.9l-7.7-7.7c0.6-0.8,1-1.8,1-2.9c0-2.7-2.2-5-4.9-5s-4.9,2.2-4.9,5c0,2.7,2.2,5,4.9,5c1.1,0,2.1-0.4,3-1
+ l7.7,7.7c-0.7,0.8-1.1,1.9-1.1,3c0,2.5,1.8,4.5,4.2,4.9v26.6c-2.4,0.3-4.2,2.4-4.2,4.9c0,1.1,0.4,2.2,1,3l-6.8,6.7
+ c-0.8-0.6-1.8-1-3-1c-2.7,0-4.9,2.2-4.9,5s2.2,5,4.9,5c2.7,0,4.9-2.2,4.9-5c0-1.1-0.4-2.1-1-2.9l6.8-6.8c0.8,0.6,1.8,1,2.9,1
+ c2.3,0,4.2-1.6,4.8-3.8h27.1c0.5,2.2,2.5,3.8,4.8,3.8c1.2,0,2.3-0.4,3.1-1.1l7.7,7.6c-0.7,0.9-1.2,2-1.2,3.2c0,2.7,2.2,5,4.9,5
+ s4.9-2.2,4.9-5C83,69.3,80.8,67.1,78.1,67.1z M48.8,41.5c0.9-0.9,1.4-2.1,1.4-3.4c0-0.8-0.2-1.5-0.5-2.2L60.6,25
+ c0.7,0.5,1.4,0.9,2.3,1v26.6c-0.8,0.1-1.5,0.4-2.1,0.8L48.8,41.5z M59.6,23.9L48.8,34.7c-0.9-1-2.2-1.6-3.6-1.6
+ c-1.4,0-2.6,0.6-3.5,1.5L31,23.9c0.4-0.6,0.7-1.3,0.8-2.1h26.9C58.8,22.6,59.1,23.3,59.6,23.9z M30,24.9l10.9,10.8
+ c-0.4,0.7-0.6,1.5-0.6,2.3c0,1.4,0.6,2.7,1.5,3.6L30,53.5c-0.7-0.5-1.4-0.9-2.3-1V25.9C28.6,25.8,29.4,25.4,30,24.9z M31,54.5
+ L43,42.5c0.7,0.3,1.4,0.6,2.2,0.6c0.9,0,1.7-0.2,2.4-0.6l12.1,12c-0.6,0.8-1,1.8-1.1,2.8H31.9C31.8,56.2,31.5,55.3,31,54.5z"/>
+ </g>
+</g>
+</svg>
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 0000000..a703d2e
--- /dev/null
+++ b/skyquake/framework/style/img/svg/launch-fleet-network-09-active.svg
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 80 80" style="enable-background:new 0 0 80 80;" xml:space="preserve">
+<style type="text/css">
+ .st0{fill:#FFFFFF;}
+</style>
+<g id="XMLID_28_">
+ <g id="XMLID_113_">
+ <path id="XMLID_114_" class="st0" d="M6.5,22.3c-3.6,0-6.5,2.9-6.5,6.5c0,3.6,2.9,6.5,6.5,6.5c3.6,0,6.5-2.9,6.5-6.5
+ C13,25.2,10.1,22.3,6.5,22.3z M6.5,67C2.9,67,0,69.9,0,73.5C0,77.1,2.9,80,6.5,80c3.6,0,6.5-2.9,6.5-6.5C13,69.9,10.1,67,6.5,67z
+ M28.8,13c1.5,0,2.8-0.5,3.9-1.3L46,24.9c-0.8,1.1-1.3,2.4-1.3,3.9c0,3.6,2.9,6.5,6.5,6.5c3.6,0,6.5-2.9,6.5-6.5
+ c0-3.6-2.9-6.5-6.5-6.5c-1.5,0-2.8,0.5-3.9,1.3L34,10.4c0.8-1.1,1.3-2.4,1.3-3.9c0-3.6-2.9-6.5-6.5-6.5c-3.6,0-6.5,2.9-6.5,6.5
+ C22.3,10.1,25.2,13,28.8,13z M73.5,13c3.6,0,6.5-2.9,6.5-6.5C80,2.9,77.1,0,73.5,0C69.9,0,67,2.9,67,6.5C67,10.1,69.9,13,73.5,13z
+ M28.8,44.7c-3.6,0-6.5,2.9-6.5,6.5c0,3.6,2.9,6.5,6.5,6.5c3.6,0,6.5-2.9,6.5-6.5C35.3,47.6,32.4,44.7,28.8,44.7z M73.5,44.7
+ c-3.6,0-6.5,2.9-6.5,6.5c0,1.8,0.7,3.4,1.9,4.6L55.7,68.8c-1.2-1.2-2.8-1.9-4.6-1.9c-3.6,0-6.5,2.9-6.5,6.5c0,3.6,2.9,6.5,6.5,6.5
+ c3.6,0,6.5-2.9,6.5-6.5c0-1.1-0.3-2.2-0.8-3.1l13.5-13.5c0.9,0.5,2,0.8,3.1,0.8c3.6,0,6.5-2.9,6.5-6.5
+ C80,47.6,77.1,44.7,73.5,44.7z"/>
+ </g>
+</g>
+</svg>
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 0000000..8e1d702
--- /dev/null
+++ b/skyquake/framework/style/img/svg/launch-fleet-network-09-inactive.svg
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 80 80" style="enable-background:new 0 0 80 80;" xml:space="preserve">
+<style type="text/css">
+ .st0{fill:#CCCCCC;}
+</style>
+<g id="XMLID_28_">
+ <g id="XMLID_100_">
+ <path id="XMLID_101_" class="st0" d="M6.5,22.3c-3.6,0-6.5,2.9-6.5,6.5c0,3.6,2.9,6.5,6.5,6.5c3.6,0,6.5-2.9,6.5-6.5
+ C13,25.2,10.1,22.3,6.5,22.3z M6.5,67C2.9,67,0,69.9,0,73.5C0,77.1,2.9,80,6.5,80c3.6,0,6.5-2.9,6.5-6.5C13,69.9,10.1,67,6.5,67z
+ M28.8,13c1.5,0,2.8-0.5,3.9-1.3L46,24.9c-0.8,1.1-1.3,2.4-1.3,3.9c0,3.6,2.9,6.5,6.5,6.5c3.6,0,6.5-2.9,6.5-6.5
+ c0-3.6-2.9-6.5-6.5-6.5c-1.5,0-2.8,0.5-3.9,1.3L34,10.4c0.8-1.1,1.3-2.4,1.3-3.9c0-3.6-2.9-6.5-6.5-6.5c-3.6,0-6.5,2.9-6.5,6.5
+ C22.3,10.1,25.2,13,28.8,13z M73.5,13c3.6,0,6.5-2.9,6.5-6.5C80,2.9,77.1,0,73.5,0C69.9,0,67,2.9,67,6.5C67,10.1,69.9,13,73.5,13z
+ M28.8,44.7c-3.6,0-6.5,2.9-6.5,6.5c0,3.6,2.9,6.5,6.5,6.5c3.6,0,6.5-2.9,6.5-6.5C35.3,47.6,32.4,44.7,28.8,44.7z M73.5,44.7
+ c-3.6,0-6.5,2.9-6.5,6.5c0,1.8,0.7,3.4,1.9,4.6L55.7,68.8c-1.2-1.2-2.8-1.9-4.6-1.9c-3.6,0-6.5,2.9-6.5,6.5c0,3.6,2.9,6.5,6.5,6.5
+ c3.6,0,6.5-2.9,6.5-6.5c0-1.1-0.3-2.2-0.8-3.1l13.5-13.5c0.9,0.5,2,0.8,3.1,0.8c3.6,0,6.5-2.9,6.5-6.5
+ C80,47.6,77.1,44.7,73.5,44.7z"/>
+ </g>
+</g>
+</svg>
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 0000000..a9b5496
--- /dev/null
+++ b/skyquake/framework/style/img/svg/launch-fleet-network-10-active.svg
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 94 74" style="enable-background:new 0 0 94 74;" xml:space="preserve">
+<style type="text/css">
+ .st0{fill:#FFFFFF;}
+</style>
+<g>
+ <g id="XMLID_96_">
+ <path id="XMLID_97_" class="st0" d="M89.1,54.9l-9.6-13c0.2-0.1,0.3-0.3,0.4-0.4c2.3-2.3,2.4-5.9,0.5-8.3L89.6,20
+ c1.1,0.6,2.6,0.5,3.6-0.5c1.2-1.2,1.2-3.1,0-4.3c-1.2-1.2-3-1.2-4.2,0c-1.2,1.2-1.2,3.1,0,4.3c0.1,0.1,0.1,0.1,0.2,0.1l-9,13
+ c0,0-0.1-0.1-0.1-0.1c-1.8-1.9-4.6-2.3-6.8-1.4L51,8.8c0.9-2.3,0.5-5-1.3-6.9c-2.4-2.5-6.4-2.5-8.8,0c-1.9,1.9-2.3,4.8-1.2,7.2
+ l-22,22.2c-1.8-0.9-4-0.8-5.7,0.2L4.8,19.8c0.1-0.1,0.2-0.2,0.3-0.2c1.2-1.2,1.2-3.1,0-4.3c-1.2-1.2-3-1.2-4.2,0
+ c-1.2,1.2-1.2,3.1,0,4.3c0.9,0.9,2.3,1.1,3.4,0.6l7.2,11.7c-0.3,0.2-0.6,0.5-0.9,0.7c-2.4,2.5-2.4,6.5,0,8.9
+ c0.3,0.3,0.6,0.6,0.9,0.8L4.2,54.5c-1.1-0.5-2.5-0.3-3.4,0.6c-1.2,1.2-1.2,3.1,0,4.3c1.2,1.2,3,1.2,4.2,0c1.2-1.2,1.2-3.1,0-4.3
+ c-0.1-0.1-0.2-0.2-0.3-0.3L12,42.6c1.5,0.8,3.2,1,4.7,0.5l22.5,22.8c-0.6,2.2-0.1,4.6,1.6,6.3c2.4,2.5,6.4,2.5,8.8,0
+ c1.6-1.6,2.1-3.9,1.7-5.9l22.8-23.1c1.7,0.4,3.5,0.1,4.9-0.9l9.6,13l0.2-0.2c-1.2,1.2-1.2,3.1,0,4.3c1.2,1.2,3,1.2,4.2,0
+ c1.2-1.2,1.2-3.1,0-4.3C92,53.9,90.3,53.9,89.1,54.9z M71.6,32.1c-0.2,0.1-0.3,0.3-0.4,0.4c-1.5,1.5-2.1,3.6-1.7,5.6h-18
+ c0.3-2-0.2-4-1.7-5.6c-1-1-2.3-1.6-3.6-1.8V12.6c1.3-0.2,2.6-0.8,3.6-1.8c0.1-0.1,0.3-0.3,0.4-0.4L71.6,32.1z M40.7,10.6
+ c0.1,0.1,0.1,0.1,0.2,0.2c1,1,2.2,1.6,3.4,1.8v18.2c-1.3,0.2-2.5,0.8-3.4,1.8c-1.5,1.5-2.1,3.6-1.7,5.6h-18c0.4-2-0.2-4.1-1.7-5.6
+ c-0.1-0.1-0.1-0.1-0.2-0.2L40.7,10.6z M18.5,42.2c0.3-0.2,0.6-0.5,0.9-0.8c0.5-0.5,0.8-1,1.1-1.5h19.2c0.3,0.6,0.7,1.1,1.1,1.6
+ c1,1,2.2,1.6,3.4,1.8v18.1c-1.3,0.2-2.5,0.8-3.4,1.8c-0.3,0.3-0.5,0.6-0.8,0.9L18.5,42.2z M50.6,64.4c-0.3-0.4-0.6-0.8-0.9-1.2
+ c-1-1-2.3-1.6-3.6-1.8V43.3c1.3-0.2,2.6-0.8,3.6-1.8c0.5-0.5,0.9-1,1.1-1.6H70c0.3,0.6,0.7,1.1,1.1,1.5c0.4,0.4,0.8,0.7,1.2,0.9
+ L50.6,64.4z"/>
+ </g>
+</g>
+</svg>
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 0000000..91b2b59
--- /dev/null
+++ b/skyquake/framework/style/img/svg/launch-fleet-network-10-inactive.svg
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 94 74" style="enable-background:new 0 0 94 74;" xml:space="preserve">
+<style type="text/css">
+ .st0{fill:#CCCCCC;}
+</style>
+<g>
+ <g id="XMLID_96_">
+ <path id="XMLID_97_" class="st0" d="M89.1,54.9l-9.6-13c0.2-0.1,0.3-0.3,0.4-0.4c2.3-2.3,2.4-5.9,0.5-8.3L89.6,20
+ c1.1,0.6,2.6,0.5,3.6-0.5c1.2-1.2,1.2-3.1,0-4.3c-1.2-1.2-3-1.2-4.2,0c-1.2,1.2-1.2,3.1,0,4.3c0.1,0.1,0.1,0.1,0.2,0.1l-9,13
+ c0,0-0.1-0.1-0.1-0.1c-1.8-1.9-4.6-2.3-6.8-1.4L51,8.8c0.9-2.3,0.5-5-1.3-6.9c-2.4-2.5-6.4-2.5-8.8,0c-1.9,1.9-2.3,4.8-1.2,7.2
+ l-22,22.2c-1.8-0.9-4-0.8-5.7,0.2L4.8,19.8c0.1-0.1,0.2-0.2,0.3-0.2c1.2-1.2,1.2-3.1,0-4.3c-1.2-1.2-3-1.2-4.2,0
+ c-1.2,1.2-1.2,3.1,0,4.3c0.9,0.9,2.3,1.1,3.4,0.6l7.2,11.7c-0.3,0.2-0.6,0.5-0.9,0.7c-2.4,2.5-2.4,6.5,0,8.9
+ c0.3,0.3,0.6,0.6,0.9,0.8L4.2,54.5c-1.1-0.5-2.5-0.3-3.4,0.6c-1.2,1.2-1.2,3.1,0,4.3c1.2,1.2,3,1.2,4.2,0c1.2-1.2,1.2-3.1,0-4.3
+ c-0.1-0.1-0.2-0.2-0.3-0.3L12,42.6c1.5,0.8,3.2,1,4.7,0.5l22.5,22.8c-0.6,2.2-0.1,4.6,1.6,6.3c2.4,2.5,6.4,2.5,8.8,0
+ c1.6-1.6,2.1-3.9,1.7-5.9l22.8-23.1c1.7,0.4,3.5,0.1,4.9-0.9l9.6,13l0.2-0.2c-1.2,1.2-1.2,3.1,0,4.3c1.2,1.2,3,1.2,4.2,0
+ c1.2-1.2,1.2-3.1,0-4.3C92,53.9,90.3,53.9,89.1,54.9z M71.6,32.1c-0.2,0.1-0.3,0.3-0.4,0.4c-1.5,1.5-2.1,3.6-1.7,5.6h-18
+ c0.3-2-0.2-4-1.7-5.6c-1-1-2.3-1.6-3.6-1.8V12.6c1.3-0.2,2.6-0.8,3.6-1.8c0.1-0.1,0.3-0.3,0.4-0.4L71.6,32.1z M40.7,10.6
+ c0.1,0.1,0.1,0.1,0.2,0.2c1,1,2.2,1.6,3.4,1.8v18.2c-1.3,0.2-2.5,0.8-3.4,1.8c-1.5,1.5-2.1,3.6-1.7,5.6h-18c0.4-2-0.2-4.1-1.7-5.6
+ c-0.1-0.1-0.1-0.1-0.2-0.2L40.7,10.6z M18.5,42.2c0.3-0.2,0.6-0.5,0.9-0.8c0.5-0.5,0.8-1,1.1-1.5h19.2c0.3,0.6,0.7,1.1,1.1,1.6
+ c1,1,2.2,1.6,3.4,1.8v18.1c-1.3,0.2-2.5,0.8-3.4,1.8c-0.3,0.3-0.5,0.6-0.8,0.9L18.5,42.2z M50.6,64.4c-0.3-0.4-0.6-0.8-0.9-1.2
+ c-1-1-2.3-1.6-3.6-1.8V43.3c1.3-0.2,2.6-0.8,3.6-1.8c0.5-0.5,0.9-1,1.1-1.6H70c0.3,0.6,0.7,1.1,1.1,1.5c0.4,0.4,0.8,0.7,1.2,0.9
+ L50.6,64.4z"/>
+ </g>
+</g>
+</svg>
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 0000000..e18114c
--- /dev/null
+++ b/skyquake/framework/style/img/svg/launch-fleet-network-11-active.svg
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 92.6 93.7" enable-background="new 0 0 92.6 93.7" xml:space="preserve">
+<g>
+ <g id="XMLID_40_">
+ <path id="XMLID_41_" fill="#FFFFFF" d="M77.5,73.7c-1,0-1.9,0.3-2.6,0.8L67.3,67c0.5-0.8,0.8-1.7,0.8-2.7c0-2.7-2.1-4.8-4.8-4.8
+ c-1,0-2,0.3-2.7,0.9L49,48.8c0.8-0.9,1.4-2.1,1.4-3.4c0-0.8-0.2-1.5-0.5-2.2l10.6-10.6c0.8,0.6,1.8,1,2.9,1c2.6,0,4.8-2.2,4.8-4.8
+ c0-1.1-0.4-2.1-1-2.9l8-7.9c0.8,0.6,1.8,1,2.9,1c2.6,0,4.8-2.2,4.8-4.8c0-2.7-2.1-4.8-4.8-4.8c-2.6,0-4.8,2.2-4.8,4.8
+ c0,1.1,0.4,2.1,1,2.9l-8,8c-0.8-0.6-1.8-0.9-2.8-0.9c-2.6,0-4.8,2.2-4.8,4.8c0,1.1,0.3,2,0.9,2.8L49,42.1
+ c-0.9-0.9-2.1-1.5-3.5-1.5c-1.3,0-2.5,0.5-3.4,1.4L31.7,31.6c0.5-0.8,0.9-1.7,0.9-2.7c0-2.7-2.1-4.8-4.8-4.8c-1,0-2,0.3-2.8,0.9
+ l-7.5-7.5c0.6-0.8,0.9-1.8,0.9-2.9c0-2.7-2.1-4.8-4.8-4.8c-2.6,0-4.8,2.2-4.8,4.8c0,2.7,2.1,4.8,4.8,4.8c1.1,0,2.1-0.4,2.9-1
+ l7.5,7.5c-0.6,0.8-1,1.8-1,3c0,2.7,2.1,4.8,4.8,4.8c1.1,0,2.2-0.4,3-1.1l10.6,10.5c-0.4,0.7-0.6,1.5-0.6,2.3
+ c0,1.4,0.6,2.6,1.5,3.5L30.7,60.5c-0.8-0.6-1.8-1-2.9-1c-2.6,0-4.8,2.2-4.8,4.8c0,1.1,0.4,2.1,1,2.9l-6.6,6.6
+ c-0.8-0.6-1.8-1-2.9-1c-2.6,0-4.8,2.2-4.8,4.8c0,2.7,2.1,4.8,4.8,4.8c2.6,0,4.8-2.2,4.8-4.8c0-1.1-0.4-2.1-1-2.9l6.6-6.6
+ c0.8,0.6,1.8,0.9,2.8,0.9c2.6,0,4.8-2.2,4.8-4.8c0-1.1-0.3-2-0.9-2.8l11.8-11.8c0.7,0.3,1.4,0.5,2.2,0.5c0.8,0,1.6-0.2,2.3-0.6
+ l11.8,11.7c-0.7,0.8-1.1,1.9-1.1,3c0,2.7,2.1,4.8,4.8,4.8c1.2,0,2.2-0.4,3-1.1l7.5,7.4c-0.7,0.8-1.2,1.9-1.2,3.1
+ c0,2.7,2.1,4.8,4.8,4.8s4.8-2.2,4.8-4.8C82.3,75.9,80.2,73.7,77.5,73.7z"/>
+ </g>
+</g>
+</svg>
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 0000000..173d3b1
--- /dev/null
+++ b/skyquake/framework/style/img/svg/launch-fleet-network-11-inactive.svg
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 92.6 93.7" enable-background="new 0 0 92.6 93.7" xml:space="preserve">
+<g>
+ <g id="XMLID_14_">
+ <path id="XMLID_15_" fill="#CCCCCC" d="M77.5,73.7c-1,0-1.9,0.3-2.6,0.8L67.3,67c0.5-0.8,0.8-1.7,0.8-2.7c0-2.7-2.1-4.8-4.8-4.8
+ c-1,0-2,0.3-2.7,0.9L49,48.8c0.8-0.9,1.4-2.1,1.4-3.4c0-0.8-0.2-1.5-0.5-2.2l10.6-10.6c0.8,0.6,1.8,1,2.9,1c2.6,0,4.8-2.2,4.8-4.8
+ c0-1.1-0.4-2.1-1-2.9l8-7.9c0.8,0.6,1.8,1,2.9,1c2.6,0,4.8-2.2,4.8-4.8c0-2.7-2.1-4.8-4.8-4.8c-2.6,0-4.8,2.2-4.8,4.8
+ c0,1.1,0.4,2.1,1,2.9l-8,8c-0.8-0.6-1.8-0.9-2.8-0.9c-2.6,0-4.8,2.2-4.8,4.8c0,1.1,0.3,2,0.9,2.8L49,42.1
+ c-0.9-0.9-2.1-1.5-3.5-1.5c-1.3,0-2.5,0.5-3.4,1.4L31.7,31.6c0.5-0.8,0.9-1.7,0.9-2.7c0-2.7-2.1-4.8-4.8-4.8c-1,0-2,0.3-2.8,0.9
+ l-7.5-7.5c0.6-0.8,0.9-1.8,0.9-2.9c0-2.7-2.1-4.8-4.8-4.8c-2.6,0-4.8,2.2-4.8,4.8c0,2.7,2.1,4.8,4.8,4.8c1.1,0,2.1-0.4,2.9-1
+ l7.5,7.5c-0.6,0.8-1,1.8-1,3c0,2.7,2.1,4.8,4.8,4.8c1.1,0,2.2-0.4,3-1.1l10.6,10.5c-0.4,0.7-0.6,1.5-0.6,2.3
+ c0,1.4,0.6,2.6,1.5,3.5L30.7,60.5c-0.8-0.6-1.8-1-2.9-1c-2.6,0-4.8,2.2-4.8,4.8c0,1.1,0.4,2.1,1,2.9l-6.6,6.6
+ c-0.8-0.6-1.8-1-2.9-1c-2.6,0-4.8,2.2-4.8,4.8c0,2.7,2.1,4.8,4.8,4.8c2.6,0,4.8-2.2,4.8-4.8c0-1.1-0.4-2.1-1-2.9l6.6-6.6
+ c0.8,0.6,1.8,0.9,2.8,0.9c2.6,0,4.8-2.2,4.8-4.8c0-1.1-0.3-2-0.9-2.8l11.8-11.8c0.7,0.3,1.4,0.5,2.2,0.5c0.8,0,1.6-0.2,2.3-0.6
+ l11.8,11.7c-0.7,0.8-1.1,1.9-1.1,3c0,2.7,2.1,4.8,4.8,4.8c1.2,0,2.2-0.4,3-1.1l7.5,7.4c-0.7,0.8-1.2,1.9-1.2,3.1
+ c0,2.7,2.1,4.8,4.8,4.8s4.8-2.2,4.8-4.8C82.3,75.9,80.2,73.7,77.5,73.7z"/>
+ </g>
+</g>
+</svg>
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 0000000..b555087
--- /dev/null
+++ b/skyquake/framework/style/img/svg/launch-fleet-pool-01-active.svg
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 339.3 128.7" enable-background="new 0 0 339.3 128.7" xml:space="preserve">
+<g>
+ <g id="XMLID_412_">
+ <path id="XMLID_413_" fill="#FFFFFF" d="M34.7,119.8h37.4V7.8H34.7V119.8z M81.5,119.8H119V7.8H81.5V119.8z M175.1,119.8h37.4V7.8
+ h-37.4V119.8z M222,119.8h37.4V7.8H222V119.8z M268.8,119.8h37.4V7.8h-37.4V119.8z M322.2,108.2h-0.9v0.9h1.9v-1.9h-0.9V108.2z
+ M171.5,109.1h2.8v-0.9h-2.8V109.1z M29.2,109.1H32v-0.9h-2.8V109.1z M216.4,109.1h2.8v-0.9h-2.8V109.1z M261.4,109.1h2.8v-0.9
+ h-2.8V109.1z M164,109.1h2.8v-0.9H164V109.1z M74.1,109.1H77v-0.9h-2.8V109.1z M119.1,109.1h2.8v-0.9h-2.8V109.1z M126.6,109.1
+ h2.8v-0.9h-2.8V109.1z M134.1,109.1h2.8v-0.9h-2.8V109.1z M156.5,109.1h2.8v-0.9h-2.8V109.1z M149,109.1h2.8v-0.9H149V109.1z
+ M141.5,109.1h2.8v-0.9h-2.8V109.1z M21.7,109.1h2.8v-0.9h-2.8V109.1z M306.3,109.1h2.8v-0.9h-2.8V109.1z M313.8,109.1h2.8v-0.9
+ h-2.8V109.1z M16.1,107.2h-0.9v1.9H17v-0.9h-0.9V107.2z M16.1,56.1h-0.9v2.7h0.9V56.1z M16.1,48.8h-0.9v2.7h0.9V48.8z M16.1,41.5
+ h-0.9v2.7h0.9V41.5z M16.1,34.2h-0.9v2.7h0.9V34.2z M16.1,26.9h-0.9v2.7h0.9V26.9z M16.1,92.6h-0.9v2.7h0.9V92.6z M16.1,78h-0.9
+ v2.7h0.9V78z M16.1,85.3h-0.9v2.7h0.9V85.3z M16.1,63.4h-0.9v2.7h0.9V63.4z M16.1,70.7h-0.9v2.7h0.9V70.7z M16.1,99.9h-0.9v2.7
+ h0.9V99.9z M15.2,22.3h0.9v-0.9H17v-0.9h-1.9V22.3z M21.7,21.4h2.8v-0.9h-2.8V21.4z M159.3,20.4h-2.8v0.9h2.8V20.4z M166.8,20.4
+ H164v0.9h2.8V20.4z M151.8,20.4H149v0.9h2.8V20.4z M136.9,20.4h-2.8v0.9h2.8V20.4z M144.4,20.4h-2.8v0.9h2.8V20.4z M129.4,20.4
+ h-2.8v0.9h2.8V20.4z M313.8,21.4h2.8v-0.9h-2.8V21.4z M261.4,21.4h2.8v-0.9h-2.8V21.4z M171.5,21.4h2.8v-0.9h-2.8V21.4z
+ M306.3,21.4h2.8v-0.9h-2.8V21.4z M216.4,21.4h2.8v-0.9h-2.8V21.4z M74.1,21.4H77v-0.9h-2.8V21.4z M119.1,21.4h2.8v-0.9h-2.8V21.4
+ z M29.2,21.4H32v-0.9h-2.8V21.4z M321.3,20.4v0.9h0.9v0.9h0.9v-1.9H321.3z M322.2,44.2h0.9v-2.7h-0.9V44.2z M322.2,51.5h0.9v-2.7
+ h-0.9V51.5z M322.2,36.9h0.9v-2.7h-0.9V36.9z M322.2,29.6h0.9v-2.7h-0.9V29.6z M322.2,58.8h0.9v-2.7h-0.9V58.8z M322.2,95.4h0.9
+ v-2.7h-0.9V95.4z M322.2,102.7h0.9v-2.7h-0.9V102.7z M322.2,88.1h0.9v-2.7h-0.9V88.1z M322.2,73.4h0.9v-2.7h-0.9V73.4z
+ M322.2,66.1h0.9v-2.7h-0.9V66.1z M322.2,80.7h0.9V78h-0.9V80.7z M126.1,99.1h42.1V28.4h-42.1V99.1z"/>
+ </g>
+</g>
+</svg>
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 0000000..c8041db
--- /dev/null
+++ b/skyquake/framework/style/img/svg/launch-fleet-pool-01-inactive.svg
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 339.3 128.7" enable-background="new 0 0 339.3 128.7" xml:space="preserve">
+<g>
+ <g id="XMLID_412_">
+ <path id="XMLID_413_" fill="#CCCCCC" d="M34.7,119.8h37.4V7.8H34.7V119.8z M81.5,119.8H119V7.8H81.5V119.8z M175.1,119.8h37.4V7.8
+ h-37.4V119.8z M222,119.8h37.4V7.8H222V119.8z M268.8,119.8h37.4V7.8h-37.4V119.8z M322.2,108.2h-0.9v0.9h1.9v-1.9h-0.9V108.2z
+ M216.4,109.1h2.8v-0.9h-2.8V109.1z M74.1,109.1H77v-0.9h-2.8V109.1z M126.6,109.1h2.8v-0.9h-2.8V109.1z M119.1,109.1h2.8v-0.9
+ h-2.8V109.1z M141.5,109.1h2.8v-0.9h-2.8V109.1z M171.5,109.1h2.8v-0.9h-2.8V109.1z M134.1,109.1h2.8v-0.9h-2.8V109.1z M149,109.1
+ h2.8v-0.9H149V109.1z M164,109.1h2.8v-0.9H164V109.1z M156.5,109.1h2.8v-0.9h-2.8V109.1z M261.4,109.1h2.8v-0.9h-2.8V109.1z
+ M306.3,109.1h2.8v-0.9h-2.8V109.1z M21.7,109.1h2.8v-0.9h-2.8V109.1z M29.2,109.1H32v-0.9h-2.8V109.1z M313.8,109.1h2.8v-0.9
+ h-2.8V109.1z M16.1,107.2h-0.9v1.9H17v-0.9h-0.9V107.2z M16.1,41.5h-0.9v2.7h0.9V41.5z M16.1,48.8h-0.9v2.7h0.9V48.8z M16.1,26.9
+ h-0.9v2.7h0.9V26.9z M16.1,56.1h-0.9v2.7h0.9V56.1z M16.1,34.2h-0.9v2.7h0.9V34.2z M16.1,92.6h-0.9v2.7h0.9V92.6z M16.1,99.9h-0.9
+ v2.7h0.9V99.9z M16.1,78h-0.9v2.7h0.9V78z M16.1,85.3h-0.9v2.7h0.9V85.3z M16.1,63.4h-0.9v2.7h0.9V63.4z M16.1,70.7h-0.9v2.7h0.9
+ V70.7z M15.2,22.3h0.9v-0.9H17v-0.9h-1.9V22.3z M151.8,20.4H149v0.9h2.8V20.4z M159.3,20.4h-2.8v0.9h2.8V20.4z M313.8,21.4h2.8
+ v-0.9h-2.8V21.4z M144.4,20.4h-2.8v0.9h2.8V20.4z M129.4,20.4h-2.8v0.9h2.8V20.4z M21.7,21.4h2.8v-0.9h-2.8V21.4z M136.9,20.4
+ h-2.8v0.9h2.8V20.4z M216.4,21.4h2.8v-0.9h-2.8V21.4z M74.1,21.4H77v-0.9h-2.8V21.4z M171.5,21.4h2.8v-0.9h-2.8V21.4z M166.8,20.4
+ H164v0.9h2.8V20.4z M29.2,21.4H32v-0.9h-2.8V21.4z M119.1,21.4h2.8v-0.9h-2.8V21.4z M306.3,21.4h2.8v-0.9h-2.8V21.4z M261.4,21.4
+ h2.8v-0.9h-2.8V21.4z M321.3,20.4v0.9h0.9v0.9h0.9v-1.9H321.3z M322.2,36.9h0.9v-2.7h-0.9V36.9z M322.2,29.6h0.9v-2.7h-0.9V29.6z
+ M322.2,44.2h0.9v-2.7h-0.9V44.2z M322.2,95.4h0.9v-2.7h-0.9V95.4z M322.2,88.1h0.9v-2.7h-0.9V88.1z M322.2,102.7h0.9v-2.7h-0.9
+ V102.7z M322.2,80.7h0.9V78h-0.9V80.7z M322.2,66.1h0.9v-2.7h-0.9V66.1z M322.2,58.8h0.9v-2.7h-0.9V58.8z M322.2,51.5h0.9v-2.7
+ h-0.9V51.5z M322.2,73.4h0.9v-2.7h-0.9V73.4z M126.1,99.1h42.1V28.4h-42.1V99.1z"/>
+ </g>
+</g>
+</svg>
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 0000000..229a451
--- /dev/null
+++ b/skyquake/framework/style/img/svg/launch-fleet-pool-02-active.svg
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 354.1 137.3" enable-background="new 0 0 354.1 137.3" xml:space="preserve">
+<g>
+ <g id="XMLID_466_">
+ <path id="XMLID_467_" fill="#FFFFFF" d="M125.9,48.9h-45v37.6h45V48.9z M225.9,48.9h-45v37.6h45V48.9z M283.4,42.2h-10V8.1h-40
+ v34.1h-60V22.6h1.1v-1h-1.1V8.1h-40v13.5h-1.9v1h1.9v19.6h-60V8.1h-40v120h40v-35h60v22.5h-1.9v1h1.9v11.5h40v-11.5h1.1v-1h-1.1
+ V93.1h60v35h40v-35h10v35h40V8.1h-40V42.2z M133.4,92.1h-60V43.2h60V92.1z M233.4,92.1h-60V43.2h60V92.1z M283.4,92.1h-10V43.2h10
+ V92.1z M340.6,115.6h-1v1h2v-2h-1V115.6z M187.6,116.6h3v-1h-3V116.6z M195.6,116.6h3v-1h-3V116.6z M203.6,116.6h3v-1h-3V116.6z
+ M227.6,116.6h3v-1h-3V116.6z M219.6,116.6h3v-1h-3V116.6z M179.6,116.6h3v-1h-3V116.6z M211.6,116.6h3v-1h-3V116.6z M107.6,116.6
+ h3v-1h-3V116.6z M75.6,116.6h3v-1h-3V116.6z M27.6,116.6h3v-1h-3V116.6z M19.6,116.6h3v-1h-3V116.6z M123.6,116.6h3v-1h-3V116.6z
+ M91.6,116.6h3v-1h-3V116.6z M115.6,116.6h3v-1h-3V116.6z M99.6,116.6h3v-1h-3V116.6z M275.6,116.6h3v-1h-3V116.6z M83.6,116.6h3
+ v-1h-3V116.6z M331.6,116.6h3v-1h-3V116.6z M323.6,116.6h3v-1h-3V116.6z M13.6,114.6h-1v2h2v-1h-1V114.6z M13.6,91.2h-1v2.9h1
+ V91.2z M13.6,99h-1v2.9h1V99z M13.6,83.3h-1v2.9h1V83.3z M13.6,36.4h-1v2.9h1V36.4z M13.6,106.8h-1v2.9h1V106.8z M13.6,44.2h-1
+ v2.9h1V44.2z M13.6,28.5h-1v2.9h1V28.5z M13.6,52h-1V55h1V52z M13.6,59.8h-1v2.9h1V59.8z M13.6,67.7h-1v2.9h1V67.7z M13.6,75.5h-1
+ v2.9h1V75.5z M12.6,23.6h1v-1h1v-1h-2V23.6z M198.6,21.6h-3v1h3V21.6z M206.6,21.6h-3v1h3V21.6z M182.6,21.6h-3v1h3V21.6z
+ M190.6,21.6h-3v1h3V21.6z M86.6,21.6h-3v1h3V21.6z M214.6,21.6h-3v1h3V21.6z M222.6,21.6h-3v1h3V21.6z M230.6,21.6h-3v1h3V21.6z
+ M94.6,21.6h-3v1h3V21.6z M102.6,21.6h-3v1h3V21.6z M78.6,21.6h-3v1h3V21.6z M126.6,21.6h-3v1h3V21.6z M118.6,21.6h-3v1h3V21.6z
+ M110.6,21.6h-3v1h3V21.6z M278.6,21.6h-3v1h3V21.6z M27.6,22.6h3v-1h-3V22.6z M331.6,22.6h3v-1h-3V22.6z M323.6,22.6h3v-1h-3
+ V22.6z M19.6,22.6h3v-1h-3V22.6z M339.6,21.6v1h1v1h1v-2H339.6z M340.6,47.1h1v-2.9h-1V47.1z M340.6,109.7h1v-2.9h-1V109.7z
+ M340.6,31.5h1v-2.9h-1V31.5z M340.6,39.3h1v-2.9h-1V39.3z M340.6,55h1V52h-1V55z M340.6,78.4h1v-2.9h-1V78.4z M340.6,62.8h1v-2.9
+ h-1V62.8z M340.6,70.6h1v-2.9h-1V70.6z M340.6,94.1h1v-2.9h-1V94.1z M340.6,86.3h1v-2.9h-1V86.3z M340.6,101.9h1V99h-1V101.9z"/>
+ </g>
+</g>
+</svg>
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 0000000..4e45a01
--- /dev/null
+++ b/skyquake/framework/style/img/svg/launch-fleet-pool-02-inactive.svg
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 354.1 137.3" enable-background="new 0 0 354.1 137.3" xml:space="preserve">
+<g>
+ <g id="XMLID_440_">
+ <path id="XMLID_441_" fill="#CCCCCC" d="M125.9,48.9h-45v37.6h45V48.9z M225.9,48.9h-45v37.6h45V48.9z M283.4,42.2h-10V8.1h-40
+ v34.1h-60V22.6h1.1v-1h-1.1V8.1h-40v13.5h-1.9v1h1.9v19.6h-60V8.1h-40v120h40v-35h60v22.5h-1.9v1h1.9v11.5h40v-11.5h1.1v-1h-1.1
+ V93.1h60v35h40v-35h10v35h40V8.1h-40V42.2z M133.4,92.1h-60V43.2h60V92.1z M233.4,92.1h-60V43.2h60V92.1z M283.4,92.1h-10V43.2h10
+ V92.1z M340.6,115.6h-1v1h2v-2h-1V115.6z M275.6,116.6h3v-1h-3V116.6z M203.6,116.6h3v-1h-3V116.6z M323.6,116.6h3v-1h-3V116.6z
+ M219.6,116.6h3v-1h-3V116.6z M227.6,116.6h3v-1h-3V116.6z M331.6,116.6h3v-1h-3V116.6z M75.6,116.6h3v-1h-3V116.6z M83.6,116.6h3
+ v-1h-3V116.6z M91.6,116.6h3v-1h-3V116.6z M211.6,116.6h3v-1h-3V116.6z M195.6,116.6h3v-1h-3V116.6z M99.6,116.6h3v-1h-3V116.6z
+ M27.6,116.6h3v-1h-3V116.6z M19.6,116.6h3v-1h-3V116.6z M187.6,116.6h3v-1h-3V116.6z M179.6,116.6h3v-1h-3V116.6z M123.6,116.6h3
+ v-1h-3V116.6z M107.6,116.6h3v-1h-3V116.6z M115.6,116.6h3v-1h-3V116.6z M13.6,114.6h-1v2h2v-1h-1V114.6z M13.6,91.2h-1v2.9h1
+ V91.2z M13.6,83.3h-1v2.9h1V83.3z M13.6,99h-1v2.9h1V99z M13.6,106.8h-1v2.9h1V106.8z M13.6,28.5h-1v2.9h1V28.5z M13.6,75.5h-1
+ v2.9h1V75.5z M13.6,59.8h-1v2.9h1V59.8z M13.6,67.7h-1v2.9h1V67.7z M13.6,36.4h-1v2.9h1V36.4z M13.6,44.2h-1v2.9h1V44.2z M13.6,52
+ h-1V55h1V52z M12.6,23.6h1v-1h1v-1h-2V23.6z M278.6,21.6h-3v1h3V21.6z M198.6,21.6h-3v1h3V21.6z M118.6,21.6h-3v1h3V21.6z
+ M110.6,21.6h-3v1h3V21.6z M126.6,21.6h-3v1h3V21.6z M230.6,21.6h-3v1h3V21.6z M182.6,21.6h-3v1h3V21.6z M94.6,21.6h-3v1h3V21.6z
+ M102.6,21.6h-3v1h3V21.6z M78.6,21.6h-3v1h3V21.6z M222.6,21.6h-3v1h3V21.6z M214.6,21.6h-3v1h3V21.6z M190.6,21.6h-3v1h3V21.6z
+ M206.6,21.6h-3v1h3V21.6z M86.6,21.6h-3v1h3V21.6z M19.6,22.6h3v-1h-3V22.6z M331.6,22.6h3v-1h-3V22.6z M323.6,22.6h3v-1h-3V22.6
+ z M27.6,22.6h3v-1h-3V22.6z M339.6,21.6v1h1v1h1v-2H339.6z M340.6,47.1h1v-2.9h-1V47.1z M340.6,55h1V52h-1V55z M340.6,31.5h1v-2.9
+ h-1V31.5z M340.6,39.3h1v-2.9h-1V39.3z M340.6,62.8h1v-2.9h-1V62.8z M340.6,101.9h1V99h-1V101.9z M340.6,109.7h1v-2.9h-1V109.7z
+ M340.6,94.1h1v-2.9h-1V94.1z M340.6,86.3h1v-2.9h-1V86.3z M340.6,70.6h1v-2.9h-1V70.6z M340.6,78.4h1v-2.9h-1V78.4z"/>
+ </g>
+</g>
+</svg>
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 0000000..f1ef102
--- /dev/null
+++ b/skyquake/framework/style/img/svg/launchpad-icn-create-environment-large.svg
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 98.7 98" enable-background="new 0 0 98.7 98" xml:space="preserve">
+<path fill-rule="evenodd" clip-rule="evenodd" fill="#333333" d="M45.8,4.5h2v84h-2V4.5z"/>
+<path fill-rule="evenodd" clip-rule="evenodd" fill="#333333" d="M4.8,45.5h84v2h-84V45.5z"/>
+<polygon id="XMLID_4_" opacity="0.15" fill-rule="evenodd" clip-rule="evenodd" fill="#333333" points="92.8,47.5 51.8,47.5
+ 51.8,8.5 47.8,8.5 47.8,47.5 8.8,47.5 8.8,51.5 47.8,51.5 47.8,92.5 51.8,92.5 51.8,51.5 92.8,51.5 "/>
+</svg>
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 0000000..ab00cfa
--- /dev/null
+++ b/skyquake/framework/style/img/svg/launchpad-icn-newTab.svg
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 43 46.4" enable-background="new 0 0 43 46.4" xml:space="preserve">
+<g>
+ <path fill="#FFFFFF" d="M27.5,15.9v12h-12v-12H27.5 M29.5,13.9h-16v16h16V13.9L29.5,13.9z"/>
+</g>
+<path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#FFFFFF" stroke-width="2" stroke-miterlimit="10" d="M22.5,34.9
+ h-14v-14"/>
+<path opacity="0.15" fill-rule="evenodd" clip-rule="evenodd" fill="#333333" d="M12.5,35.9h15v4h-15V35.9z"/>
+<path opacity="0.15" fill-rule="evenodd" clip-rule="evenodd" fill="#333333" d="M17.5,29.9h12v4h-12V29.9z"/>
+<path opacity="0.15" fill-rule="evenodd" clip-rule="evenodd" fill="#333333" d="M29.5,17.9h4v16h-4V17.9z"/>
+</svg>
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 0000000..696e698
--- /dev/null
+++ b/skyquake/framework/style/img/svg/launchpad-icn-play.svg
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 31.3 31.3" enable-background="new 0 0 31.3 31.3" xml:space="preserve">
+<g id="XMLID_1_">
+ <path id="XMLID_20_" fill="#FFFFFF" d="M9.2,8.9l10.2,6.7L9.2,21.6V8.9 M7.2,5.2v20l16-9.5L7.2,5.2L7.2,5.2z"/>
+</g>
+</svg>
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 0000000..103c9ba
--- /dev/null
+++ b/skyquake/framework/style/img/svg/launchpad-icn-sliders.svg
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 33.4 32.4" enable-background="new 0 0 33.4 32.4" xml:space="preserve">
+<g>
+ <g>
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="#333333" d="M24.7,8.7h-1.5v-2c0-0.6-0.4-1-1-1c-0.6,0-1,0.4-1,1v2h-1.5
+ c-0.8,0-1.5,0.7-1.5,1.5c0,0.8,0.7,1.5,1.5,1.5h1.5v14c0,0.6,0.4,1,1,1c0.6,0,1-0.4,1-1v-14h1.5c0.8,0,1.5-0.7,1.5-1.5
+ C26.2,9.4,25.5,8.7,24.7,8.7z M14.7,19.7h-1.5v-13c0-0.6-0.4-1-1-1c-0.6,0-1,0.4-1,1v13H9.7c-0.8,0-1.5,0.7-1.5,1.5
+ c0,0.8,0.7,1.5,1.5,1.5h1.5v3c0,0.6,0.4,1,1,1c0.6,0,1-0.4,1-1v-3h1.5c0.8,0,1.5-0.7,1.5-1.5C16.2,20.4,15.5,19.7,14.7,19.7z"/>
+ </g>
+</g>
+</svg>
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 0000000..d2955f4
--- /dev/null
+++ b/skyquake/framework/style/img/svg/riftio_logo.svg
@@ -0,0 +1 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/" x="0px" y="0px" width="117.272px" height="79.447px" viewBox="0 0 117.272 79.447" style="enable-background:new 0 0 117.272 79.447;" xml:space="preserve"><defs></defs><path id="XMLID_2_" style="fill:#0080C8;" d="M87.178,46.6c-0.262-0.379-0.536-0.75-0.822-1.112c-0.143-0.181-0.29-0.359-0.439-0.535c-0.299-0.352-0.609-0.694-0.932-1.027c-0.161-0.166-0.325-0.33-0.492-0.491c-0.167-0.161-0.337-0.319-0.509-0.475c-1.208-1.089-2.552-2.045-4.016-2.842c0.008-0.222,0.012-0.443,0.012-0.663c0-10.05-8.176-18.227-18.227-18.227c-2.779,0-5.462,0.615-7.909,1.799c-1.498-2.115-3.31-3.944-5.356-5.436c-0.898,1.836-1.404,3.898-1.404,6.076c0,0.491,0.032,0.98,0.083,1.467c1.043,1.139,1.944,2.43,2.668,3.855l1.766,3.476l3.112-2.35c0.255-0.193,0.516-0.374,0.783-0.544c0.4-0.255,0.813-0.484,1.237-0.686c0.424-0.202,0.859-0.378,1.303-0.527c0.296-0.099,0.596-0.186,0.9-0.261c0.456-0.112,0.919-0.197,1.389-0.254c0.47-0.057,0.946-0.085,1.427-0.085c2.016,0,3.914,0.512,5.571,1.414c0.331,0.18,0.653,0.376,0.965,0.587c0.778,0.526,1.49,1.143,2.119,1.836c0.252,0.277,0.491,0.566,0.716,0.867c0.337,0.451,0.643,0.926,0.913,1.423c0.18,0.331,0.345,0.673,0.493,1.022c0.148,0.35,0.28,0.708,0.394,1.074c0.057,0.183,0.11,0.368,0.158,0.555c0.096,0.373,0.175,0.754,0.234,1.141c0.089,0.58,0.135,1.174,0.135,1.779c0,0.673-0.062,1.365-0.184,2.056l-0.462,2.613l2.463,0.986c0.645,0.258,1.264,0.559,1.853,0.897c0.196,0.113,0.39,0.23,0.579,0.351c0.455,0.291,0.892,0.605,1.307,0.941c0.277,0.224,0.545,0.457,0.803,0.7c0.258,0.242,0.507,0.494,0.746,0.754c0.358,0.39,0.694,0.799,1.006,1.224c1.404,1.917,2.324,4.179,2.644,6.606c0.083,0.628,0.128,1.267,0.128,1.915c0,0.455-0.022,0.905-0.064,1.35c-0.226,2.383-1.044,4.61-2.305,6.529c-0.808,1.229-1.797,2.332-2.932,3.265c-2.189,1.798-4.913,2.97-7.903,3.219l-5.312,0.056H25.045l-5.311-0.056C12.321,72.245,6.529,65.956,6.529,58.5c0-1.314,0.181-2.6,0.523-3.83c0.098-0.351,0.208-0.698,0.332-1.04c0.494-1.366,1.19-2.648,2.058-3.802c0.217-0.288,0.445-0.569,0.683-0.841c0.119-0.136,0.241-0.27,0.365-0.401c0.372-0.394,0.768-0.768,1.184-1.12c0.555-0.469,1.147-0.897,1.773-1.28c0.156-0.096,0.315-0.189,0.476-0.279c0.321-0.18,0.651-0.348,0.988-0.504c0.169-0.078,0.339-0.153,0.511-0.224c0.172-0.072,0.346-0.14,0.522-0.205l2.773-1.027l-0.748-2.861c-0.37-1.416-0.558-2.879-0.558-4.35c0-9.378,7.572-17.016,16.922-17.133c0.344-2.263,0.971-4.435,1.844-6.478c-0.539-0.036-1.081-0.057-1.627-0.057c-13.05,0-23.668,10.617-23.668,23.668c0,1.095,0.076,2.188,0.226,3.27C4.367,43.606,0,50.724,0,58.5c0,5.314,1.994,10.381,5.613,14.267c2.475,2.657,5.552,4.597,8.94,5.681c0.616,0.197,1.242,0.366,1.877,0.505c0.317,0.07,0.637,0.132,0.958,0.187c0.643,0.11,1.294,0.191,1.95,0.241l5.654,0.066l40.858,0l5.458-0.058l0.213-0.009c0.656-0.05,1.307-0.13,1.95-0.241c0.321-0.055,0.641-0.117,0.958-0.187c0.635-0.139,1.261-0.308,1.877-0.505c3.388-1.083,6.466-3.024,8.94-5.681c0.906-0.972,1.709-2.019,2.406-3.124c0.655-1.039,1.21-2.133,1.67-3.265c0.839-2.067,1.345-4.268,1.489-6.529c0.029-0.448,0.048-0.898,0.048-1.35c0-0.644-0.032-1.283-0.089-1.915C90.445,52.933,89.186,49.511,87.178,46.6z"/><path id="XMLID_1_" style="fill:#135296;" d="M106.378,27.051c0.008-0.222,0.012-0.443,0.012-0.663c0-10.05-8.176-18.227-18.227-18.227c-2.779,0-5.462,0.615-7.909,1.799C75.853,3.743,68.733,0,60.959,0c-1.414,0-2.8,0.125-4.147,0.364c-2.02,0.359-3.952,0.975-5.762,1.813c-0.201,0.093-0.401,0.189-0.599,0.287c-0.167,0.083-0.332,0.168-0.497,0.255c-0.882,0.465-1.732,0.984-2.544,1.554c-0.09,0.063-0.179,0.126-0.268,0.19c-0.23,0.166-0.458,0.336-0.682,0.511c-0.064,0.05-0.128,0.1-0.191,0.15c-1.888,1.498-3.542,3.278-4.9,5.275c-0.688,1.012-1.298,2.08-1.825,3.195c-0.116,0.245-0.227,0.492-0.335,0.742c-0.752,1.746-1.3,3.6-1.613,5.532c-0.187,1.156-0.29,2.339-0.303,3.544c-0.001,0.085-0.003,0.171-0.003,0.256c0,1.095,0.076,2.188,0.226,3.27c-6.741,3.6-11.108,10.718-11.108,18.494c0,1.661,0.195,3.297,0.573,4.881c0.832,3.485,2.552,6.715,5.04,9.386c0.264,0.284,0.536,0.559,0.813,0.826c1.944,1.87,4.209,3.328,6.67,4.322c0.69,0.279,1.396,0.522,2.115,0.726c0.168,0.048,0.336,0.094,0.505,0.137c0.205,0.053,0.411,0.102,0.617,0.149c0.377,0.085,0.756,0.158,1.139,0.222c0.247,0.041,0.495,0.08,0.744,0.112c0.372,0.049,0.746,0.09,1.123,0.119l5.654,0.066l25.984,0c1.787-1.7,3.001-3.991,3.322-6.529H51.455l-5.311-0.056c-1.158-0.096-2.277-0.331-3.34-0.688c-0.851-0.285-1.666-0.648-2.437-1.079c-0.193-0.108-0.383-0.22-0.57-0.336c-0.375-0.233-0.738-0.482-1.088-0.747c-1.052-0.795-1.992-1.729-2.793-2.774c-0.534-0.697-1.005-1.442-1.407-2.228c-1.004-1.965-1.57-4.181-1.57-6.511c0-6.006,3.783-11.442,9.414-13.526l2.773-1.027l-0.748-2.861c-0.078-0.298-0.146-0.599-0.208-0.901c-0.173-0.849-0.284-1.712-0.328-2.58c-0.015-0.289-0.022-0.579-0.022-0.869c0-0.222,0.006-0.443,0.015-0.663s0.024-0.439,0.04-0.657c0.178-2.325,0.821-4.52,1.838-6.495c0.544-1.057,1.193-2.052,1.936-2.967c3.145-3.874,7.942-6.356,13.309-6.356c4.054,0,7.862,1.406,10.866,3.874c1.202,0.987,2.275,2.145,3.184,3.451c0.114,0.163,0.225,0.329,0.333,0.497c0.325,0.503,0.627,1.027,0.903,1.57l1.766,3.476l3.112-2.35c2.041-1.542,4.476-2.357,7.04-2.357c0.266,0,0.531,0.009,0.793,0.027c1.31,0.089,2.561,0.395,3.719,0.881c0.463,0.195,0.911,0.418,1.342,0.668c0.128,0.074,0.254,0.15,0.378,0.229c0.41,0.259,0.803,0.543,1.177,0.85c0.374,0.307,0.728,0.636,1.061,0.986c0.167,0.175,0.328,0.355,0.483,0.54c1.089,1.294,1.904,2.827,2.352,4.508c0.256,0.96,0.393,1.969,0.393,3.009c0,0.673-0.062,1.365-0.184,2.056l-0.462,2.613l0.986,0.395l1.477,0.591c0.264,0.106,0.522,0.22,0.776,0.34c0.163,0.076,0.324,0.155,0.483,0.237c0.15,0.077,0.298,0.157,0.445,0.238c0.353,0.197,0.695,0.407,1.027,0.631c0.092,0.062,0.183,0.124,0.273,0.188c3.526,2.494,5.768,6.454,6.035,10.865c0.018,0.294,0.027,0.59,0.027,0.888c0,1.207-0.152,2.383-0.438,3.51c-0.858,3.379-2.923,6.311-5.717,8.281c-0.113,0.079-0.227,0.157-0.342,0.233c-0.4,0.266-0.815,0.511-1.242,0.737c-0.196,0.103-0.394,0.202-0.594,0.296c-0.178,0.084-0.358,0.164-0.54,0.24c-0.251,0.106-0.506,0.204-0.764,0.296c-1.131,0.401-2.326,0.667-3.568,0.77l-3.178,0.034c-0.121,2.247-0.544,4.446-1.255,6.543l4.614-0.049l0.213-0.009c0.328-0.025,0.655-0.058,0.98-0.098c1.95-0.241,3.84-0.752,5.621-1.51c2.672-1.138,5.1-2.832,7.125-5.006c2.489-2.672,4.209-5.902,5.04-9.386c0.378-1.584,0.573-3.22,0.573-4.881C117.272,37.665,113.069,30.694,106.378,27.051z"/></svg>
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 0000000..d456d76
--- /dev/null
+++ b/skyquake/framework/style/img/svg/riftio_logo_white.svg
@@ -0,0 +1 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/" x="0px" y="0px" width="117.272px" height="79.447px" viewBox="0 0 117.272 79.447" style="enable-background:new 0 0 117.272 79.447;" xml:space="preserve"><defs></defs><path id="XMLID_2_" style="fill:#fff;" d="M87.178,46.6c-0.262-0.379-0.536-0.75-0.822-1.112c-0.143-0.181-0.29-0.359-0.439-0.535c-0.299-0.352-0.609-0.694-0.932-1.027c-0.161-0.166-0.325-0.33-0.492-0.491c-0.167-0.161-0.337-0.319-0.509-0.475c-1.208-1.089-2.552-2.045-4.016-2.842c0.008-0.222,0.012-0.443,0.012-0.663c0-10.05-8.176-18.227-18.227-18.227c-2.779,0-5.462,0.615-7.909,1.799c-1.498-2.115-3.31-3.944-5.356-5.436c-0.898,1.836-1.404,3.898-1.404,6.076c0,0.491,0.032,0.98,0.083,1.467c1.043,1.139,1.944,2.43,2.668,3.855l1.766,3.476l3.112-2.35c0.255-0.193,0.516-0.374,0.783-0.544c0.4-0.255,0.813-0.484,1.237-0.686c0.424-0.202,0.859-0.378,1.303-0.527c0.296-0.099,0.596-0.186,0.9-0.261c0.456-0.112,0.919-0.197,1.389-0.254c0.47-0.057,0.946-0.085,1.427-0.085c2.016,0,3.914,0.512,5.571,1.414c0.331,0.18,0.653,0.376,0.965,0.587c0.778,0.526,1.49,1.143,2.119,1.836c0.252,0.277,0.491,0.566,0.716,0.867c0.337,0.451,0.643,0.926,0.913,1.423c0.18,0.331,0.345,0.673,0.493,1.022c0.148,0.35,0.28,0.708,0.394,1.074c0.057,0.183,0.11,0.368,0.158,0.555c0.096,0.373,0.175,0.754,0.234,1.141c0.089,0.58,0.135,1.174,0.135,1.779c0,0.673-0.062,1.365-0.184,2.056l-0.462,2.613l2.463,0.986c0.645,0.258,1.264,0.559,1.853,0.897c0.196,0.113,0.39,0.23,0.579,0.351c0.455,0.291,0.892,0.605,1.307,0.941c0.277,0.224,0.545,0.457,0.803,0.7c0.258,0.242,0.507,0.494,0.746,0.754c0.358,0.39,0.694,0.799,1.006,1.224c1.404,1.917,2.324,4.179,2.644,6.606c0.083,0.628,0.128,1.267,0.128,1.915c0,0.455-0.022,0.905-0.064,1.35c-0.226,2.383-1.044,4.61-2.305,6.529c-0.808,1.229-1.797,2.332-2.932,3.265c-2.189,1.798-4.913,2.97-7.903,3.219l-5.312,0.056H25.045l-5.311-0.056C12.321,72.245,6.529,65.956,6.529,58.5c0-1.314,0.181-2.6,0.523-3.83c0.098-0.351,0.208-0.698,0.332-1.04c0.494-1.366,1.19-2.648,2.058-3.802c0.217-0.288,0.445-0.569,0.683-0.841c0.119-0.136,0.241-0.27,0.365-0.401c0.372-0.394,0.768-0.768,1.184-1.12c0.555-0.469,1.147-0.897,1.773-1.28c0.156-0.096,0.315-0.189,0.476-0.279c0.321-0.18,0.651-0.348,0.988-0.504c0.169-0.078,0.339-0.153,0.511-0.224c0.172-0.072,0.346-0.14,0.522-0.205l2.773-1.027l-0.748-2.861c-0.37-1.416-0.558-2.879-0.558-4.35c0-9.378,7.572-17.016,16.922-17.133c0.344-2.263,0.971-4.435,1.844-6.478c-0.539-0.036-1.081-0.057-1.627-0.057c-13.05,0-23.668,10.617-23.668,23.668c0,1.095,0.076,2.188,0.226,3.27C4.367,43.606,0,50.724,0,58.5c0,5.314,1.994,10.381,5.613,14.267c2.475,2.657,5.552,4.597,8.94,5.681c0.616,0.197,1.242,0.366,1.877,0.505c0.317,0.07,0.637,0.132,0.958,0.187c0.643,0.11,1.294,0.191,1.95,0.241l5.654,0.066l40.858,0l5.458-0.058l0.213-0.009c0.656-0.05,1.307-0.13,1.95-0.241c0.321-0.055,0.641-0.117,0.958-0.187c0.635-0.139,1.261-0.308,1.877-0.505c3.388-1.083,6.466-3.024,8.94-5.681c0.906-0.972,1.709-2.019,2.406-3.124c0.655-1.039,1.21-2.133,1.67-3.265c0.839-2.067,1.345-4.268,1.489-6.529c0.029-0.448,0.048-0.898,0.048-1.35c0-0.644-0.032-1.283-0.089-1.915C90.445,52.933,89.186,49.511,87.178,46.6z"/><path id="XMLID_1_" style="fill:#fff;" d="M106.378,27.051c0.008-0.222,0.012-0.443,0.012-0.663c0-10.05-8.176-18.227-18.227-18.227c-2.779,0-5.462,0.615-7.909,1.799C75.853,3.743,68.733,0,60.959,0c-1.414,0-2.8,0.125-4.147,0.364c-2.02,0.359-3.952,0.975-5.762,1.813c-0.201,0.093-0.401,0.189-0.599,0.287c-0.167,0.083-0.332,0.168-0.497,0.255c-0.882,0.465-1.732,0.984-2.544,1.554c-0.09,0.063-0.179,0.126-0.268,0.19c-0.23,0.166-0.458,0.336-0.682,0.511c-0.064,0.05-0.128,0.1-0.191,0.15c-1.888,1.498-3.542,3.278-4.9,5.275c-0.688,1.012-1.298,2.08-1.825,3.195c-0.116,0.245-0.227,0.492-0.335,0.742c-0.752,1.746-1.3,3.6-1.613,5.532c-0.187,1.156-0.29,2.339-0.303,3.544c-0.001,0.085-0.003,0.171-0.003,0.256c0,1.095,0.076,2.188,0.226,3.27c-6.741,3.6-11.108,10.718-11.108,18.494c0,1.661,0.195,3.297,0.573,4.881c0.832,3.485,2.552,6.715,5.04,9.386c0.264,0.284,0.536,0.559,0.813,0.826c1.944,1.87,4.209,3.328,6.67,4.322c0.69,0.279,1.396,0.522,2.115,0.726c0.168,0.048,0.336,0.094,0.505,0.137c0.205,0.053,0.411,0.102,0.617,0.149c0.377,0.085,0.756,0.158,1.139,0.222c0.247,0.041,0.495,0.08,0.744,0.112c0.372,0.049,0.746,0.09,1.123,0.119l5.654,0.066l25.984,0c1.787-1.7,3.001-3.991,3.322-6.529H51.455l-5.311-0.056c-1.158-0.096-2.277-0.331-3.34-0.688c-0.851-0.285-1.666-0.648-2.437-1.079c-0.193-0.108-0.383-0.22-0.57-0.336c-0.375-0.233-0.738-0.482-1.088-0.747c-1.052-0.795-1.992-1.729-2.793-2.774c-0.534-0.697-1.005-1.442-1.407-2.228c-1.004-1.965-1.57-4.181-1.57-6.511c0-6.006,3.783-11.442,9.414-13.526l2.773-1.027l-0.748-2.861c-0.078-0.298-0.146-0.599-0.208-0.901c-0.173-0.849-0.284-1.712-0.328-2.58c-0.015-0.289-0.022-0.579-0.022-0.869c0-0.222,0.006-0.443,0.015-0.663s0.024-0.439,0.04-0.657c0.178-2.325,0.821-4.52,1.838-6.495c0.544-1.057,1.193-2.052,1.936-2.967c3.145-3.874,7.942-6.356,13.309-6.356c4.054,0,7.862,1.406,10.866,3.874c1.202,0.987,2.275,2.145,3.184,3.451c0.114,0.163,0.225,0.329,0.333,0.497c0.325,0.503,0.627,1.027,0.903,1.57l1.766,3.476l3.112-2.35c2.041-1.542,4.476-2.357,7.04-2.357c0.266,0,0.531,0.009,0.793,0.027c1.31,0.089,2.561,0.395,3.719,0.881c0.463,0.195,0.911,0.418,1.342,0.668c0.128,0.074,0.254,0.15,0.378,0.229c0.41,0.259,0.803,0.543,1.177,0.85c0.374,0.307,0.728,0.636,1.061,0.986c0.167,0.175,0.328,0.355,0.483,0.54c1.089,1.294,1.904,2.827,2.352,4.508c0.256,0.96,0.393,1.969,0.393,3.009c0,0.673-0.062,1.365-0.184,2.056l-0.462,2.613l0.986,0.395l1.477,0.591c0.264,0.106,0.522,0.22,0.776,0.34c0.163,0.076,0.324,0.155,0.483,0.237c0.15,0.077,0.298,0.157,0.445,0.238c0.353,0.197,0.695,0.407,1.027,0.631c0.092,0.062,0.183,0.124,0.273,0.188c3.526,2.494,5.768,6.454,6.035,10.865c0.018,0.294,0.027,0.59,0.027,0.888c0,1.207-0.152,2.383-0.438,3.51c-0.858,3.379-2.923,6.311-5.717,8.281c-0.113,0.079-0.227,0.157-0.342,0.233c-0.4,0.266-0.815,0.511-1.242,0.737c-0.196,0.103-0.394,0.202-0.594,0.296c-0.178,0.084-0.358,0.164-0.54,0.24c-0.251,0.106-0.506,0.204-0.764,0.296c-1.131,0.401-2.326,0.667-3.568,0.77l-3.178,0.034c-0.121,2.247-0.544,4.446-1.255,6.543l4.614-0.049l0.213-0.009c0.328-0.025,0.655-0.058,0.98-0.098c1.95-0.241,3.84-0.752,5.621-1.51c2.672-1.138,5.1-2.832,7.125-5.006c2.489-2.672,4.209-5.902,5.04-9.386c0.378-1.584,0.573-3.22,0.573-4.881C117.272,37.665,113.069,30.694,106.378,27.051z"/></svg>
diff --git a/skyquake/framework/style/img/switch-icon.png b/skyquake/framework/style/img/switch-icon.png
new file mode 100644
index 0000000..88a7715
--- /dev/null
+++ b/skyquake/framework/style/img/switch-icon.png
Binary files 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 0000000..8f4c8a8
--- /dev/null
+++ b/skyquake/framework/style/img/table-cell-bg.png
Binary files 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 0000000..db63b6e
--- /dev/null
+++ b/skyquake/framework/style/img/traffic-sim-diagram.png
Binary files differ
diff --git a/skyquake/framework/style/img/tunnels.png b/skyquake/framework/style/img/tunnels.png
new file mode 100644
index 0000000..b4fe6d9
--- /dev/null
+++ b/skyquake/framework/style/img/tunnels.png
Binary files 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 0000000..0690c8e
--- /dev/null
+++ b/skyquake/framework/style/img/viewport-dash-temp.png
Binary files 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 0000000..144bdc3
--- /dev/null
+++ b/skyquake/framework/style/img/viewport-dash-v2-temp.png
Binary files 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 0000000..cbdfd6e
--- /dev/null
+++ b/skyquake/framework/style/img/viewport-dash-v3-temp.png
Binary files 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 0000000..d9f7669
--- /dev/null
+++ b/skyquake/framework/style/img/viewport-nav-bottom.png
Binary files 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 0000000..2edeb3f
--- /dev/null
+++ b/skyquake/framework/style/img/viewport-nav-center.png
Binary files 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 0000000..9b017de
--- /dev/null
+++ b/skyquake/framework/style/img/viewport-nav-left.png
Binary files 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 0000000..5fc43b4
--- /dev/null
+++ b/skyquake/framework/style/img/viewport-nav-right.png
Binary files 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 0000000..7c0717d
--- /dev/null
+++ b/skyquake/framework/style/img/viewport-nav-top.png
Binary files 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 0000000..cbdfd6e
--- /dev/null
+++ b/skyquake/framework/style/img/viewport-platform-temp.png
Binary files 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 0000000..cd120cc
--- /dev/null
+++ b/skyquake/framework/style/img/viewport-sla-graph.svg
@@ -0,0 +1,45 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid" width="176" height="176" viewBox="0 0 176 176">
+ <defs>
+ <style>
+
+ .cls-3 {
+ stroke: #7cc118;
+ stroke-width: 16px;
+ fill: none;
+ }
+
+ .cls-4 {
+ font-size: 48px;
+ }
+
+ .cls-5 {
+ fill: #333333;
+ font-size: 48px;
+ font-family: "Roboto-Light";
+ }
+
+ .cls-6 {
+ font-size: 14px;
+ }
+
+ .cls-7 {
+ fill: #333333;
+ font-size: 14px;
+ font-family: "Roboto-Bold";
+ font-weight: bold;
+ }
+
+ .cls-8 {
+ fill: #ffffff;
+ stroke: #bcbec0;
+ stroke-width: 1px;
+ }
+ </style>
+ </defs>
+ <g id="circle-chart">
+ <circle id="circle-1" class="cls-3" cx="88" cy="88" r="80"/>
+ <text class="cls-4" x="45px" y="105px"><tspan class="cls-5">100</tspan></text>
+ <text class="cls-6" x="121px" y="104px"><tspan class="cls-7">%</tspan></text>
+ <path d="M85.500,0.500 C85.500,0.500 75.500,0.500 75.500,0.500 C75.500,0.500 75.500,31.500 75.500,31.500 C75.500,31.500 80.500,37.500 80.500,37.500 C80.500,37.500 85.500,30.500 85.500,30.500 C85.500,30.500 85.500,0.500 85.500,0.500 Z" id="path-1" class="cls-8" fill-rule="evenodd"/>
+ </g>
+</svg>
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 0000000..0f8709f
--- /dev/null
+++ b/skyquake/framework/style/img/viewport-vim-temp.png
Binary files 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 0000000..4b7c77c
--- /dev/null
+++ b/skyquake/framework/style/img/viewport-vnf-10.svg
@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid" width="232" height="212" viewBox="0 0 232 212">
+ <image xlink:href="data:img/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOgAAADUCAYAAACWEW+uAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABmJLR0QAAAAAAAD5Q7t/AAAACXBIWXMAAAsSAAALEgHS3X78AAAqAklEQVR42u2deZgcVbn/P6eqenrWLJ2ETMIWEgIiIEuzRCEaIIR7FRC1AwqighBAICAgyY/levUqEDZZ3BIgIireQKuAXEWJIQoiWwOyM4EkbElIJjVr9/RWdX5/VE/SGWbprap6Zs7nefohU139vucU/e2zvy8oRjQrV60OrFy12vC7HIrSUP/jhjErV63eA7gLGAPouf8aQBMQABpzt84GnvC7vIriUQId3jTW1dUdtf+++4AQGLqBEALD0BFCoOs6sedfpL2jw+9yKkpECXSYo2saTU1NfhdD4RKa3wVQKBQDowSqUFQxqotbjUTNnYDd8l5TgBAwAZiY+3fjOR09437TsGFIc5fHp/+94Y+tGV0jZWiyR9eI65ps1zX5ga6x1tDkq7rGS0LwcsuRzQm/q6/YjhKoX0TNILAP8DHg47l/7wPsCQQLMWEjCnKVtoQWz2rBnN0xeW8dnH+fEFD/UGu6NiDNGkO+G9DlqwFdPmHoPNByZLPp9yMbjSiBekHU1IF9gUNzrzBwIFX2/KWERFqrSaRpBpqBw4AzhOCuhj+2Jutr7PeCAflCjS4fMXR+23Jkc9LvMo90quoLMmJwBHkQMCf3ms2OLdewQkqIp7TaeEqbCcwETtY1uXz8n7a01gfl80HDftDQuVt1jyuPEmiliJqTgM8BJwBzqRJBCk0QFLLidi1b0JbQJ7YlmAfMMzT54wmPbNlYX2P/pTYgb1kzu/klv+s+ElACLYeouRcwH0eYs6DAQaGH6JqOge26n6wtxNZufepW9DOAMxofbu0ZW2f9oy4gl7z16ebH/H4OwxUl0GKJmrsApwCn0meSRbGd7qRW153UjgOOa3q4NTGmzno8oPPd9XMmP+132YYTSqCFEDVrcVrKz+KIsypaSglIWfnua6XpSmr1XUntuHHZzQeKu1pahOAuLZtekV2wn5pkGgIl0MGImh8HFgBfA8YDW6kScQIkpI5lW34XoyCCWsbqiIvJEjlZSmbbeuBWsbxlhdC0n9rf2PPffpevWlEC7UvUFDhjysuAz/R5dwKwFpjudzGHG00ivqXVWbrpZayUcoG0rAXirpa/C8Gt9piJDxAJVX+XwEOUQHtxurGnA5cCew9ypzoaUgKJ7lTdQDtLJfIzUvIZ0bHlbbG85XaBvMM6c2+1ZIPaiwtRs5GouRh4B1jG4OIEZ7eP+9OiI4hGPZlKpLWxQ90nYYYt5S2WZJ22vOVyffmb9X6X3W9Gr0CjZh1R82JgPXAtsFOBn6wFXvO7+MOJOhnfWuRHdrKlXGJJ3teWt/yX/su3R+15utEn0KhZQ9S8CGcs+SOccWWxjL7nViJCSjq6rFKeMcB4W8rvWdnsOm35m5cQNUfdkGx0fdGi5knAK8At7DhhUSz7AGqMVABjA4nudFYUtPl/ECbYkptEx5Y3tOUtJ/tdJy8ZHQKNmgcTNVcDf8DZS1ouAnjD72oNB/RsorNStnJj1BXirpantV+sOdzvunnByBZo1BxH1Pw58BwfXTIpl1K7baMGHUu2d8oplbYrkYfZtv2kuKtlmXHHa+P9rqebjFyBRs1TcVq5c3Bnc8HuwBa/q1nNjDXibZYUbm3s0CTy7Kymt2jLW87IrV+POEaeQKPmHkTNvwK/ASa77O0dv6tbzVjJZMYDNxNtKZeLjta/679Ys4ffda40I0egUVMQNc8FXgaO9cjrNL+rXQg1Lhw3G4qglrE748LtH8htSORsy7Zf0pa3nD+SWtORIdCouTPwZ+BnQIOHnicC6/yu/mAYhk4A7wXaJOKbfdiz12hL+WPR0bpqpLSmw1+gUfMUnFbzOJ9K0O73I6hGEvF0nV++JXKOZdsvane9earfz6Fchu/Cr7N39hacSSA/+RjO1r/h/2NXIRr1ZKo7JYbc2ucyY2z4jVjecowurQuy39ynx+/nUgrD80sVNWcCT+G/OAHqgNf9cJySgmy2+o6blbC1zzWklGdaaDH9F2v29bsspTD8BOp0aZ8HDvC7KB6TwZk1fg74C/BbXchlhXxwQl12S6jB2jKmzu6qr7EzmotTKLmtfSG/H1Y+EvaxbPuZ4djlHT6zXVFTA34ILPa7KP0ggR6g3NMXGZy129dyrzdxRPkusLHvWcmVq1aPNQyj/TOzjxjQ4Kuvvc6mDzefNvfoOffmX5/5+KaPWTYHWLY4KGuLg9JZMSOVEc3xtKi37NLXLscZie72rfHGUj/vNpoQN9hjJi4mEhoWJ5KGxxg0ao7BWdc83u+iDIDAEdNBRXzGBl4CnsFpFZ8GXiMSynpR4DWzm9/A+TFYkX99ryc2GZbNvIwljk9nxax4Stu7O6XVFxpZRc/GO9me9rDqsKX8juhoPUDcs/5k+2vTqv5sb/UL1BlvPoATfb2aKWTL2UvAY7nXaiKhqvuCtBzZnAX+lHsBMPPxTZMzljg7mRHHdye1T3SntH5naHVh0d4hm6u9YyaR80h0/t5Y+vK52XP2X+N3eQajugUaNQ8FfoeTjLbamQa04qyN9pICVgEPA38kEnrP70KWwprZzR8CP8i92PPxTYemMuLCeEqb157QJ9u51nWsnjBNKapq/Nkforv9TWlljraE+Ffgx89+PnPBof/0u0wDUb0CjZr/CURxxnWbcMZ4vq2tFch6nMRGjwL3Ag8QCVXsNEe18Nbs5mdxAqkx8/FNu6ay4squpPYlqyOZ9rtsQ5JMvC+z6T0RAinlhEw69ahx21Nfyy6cFfW7aP1RnbO4UfMbwENsn3RpBt4DH7bEFE4Lzjh5CpHQfxAJ3eOBODOWZSW6urv7fTOdyWC2tduAa924NbOb33v3qMnntv3npEldqcAhmhBXCXjL5XqX+LRSHSS7xyGEnne1Lpu1Vhi3PXWR38Xrj+obLETNy4ElA7z7Ik7SoWohg9PKLwP+7kdEupWrVp8YDAZ/f2j4ID0Y3PFc9EuvvMqWLa03zD16zuVel0tbvuYoKeUFEvl5QC/bYLnYdkZ0tppSDLw/2DCMK7ILZ13rd1HzqS6BOsG7hnpAL+K/SDtxRHkrkdD7PpeFlatWn9fY0PDT8MEHYhjOqGXDxk28/sabLwKHzz16jm9dT/0Xa6bZUl4kpTwTv/LVSIno2Py2FNqMIctrGNdYC2dd6Us5+6F6BBo1ryQ3CVEAb+BssfOaLcD1wLJqG1uuXLX62vHjxy0+8BOfIJVK8fSzzyUtyzp07tFzXvG7bAD6L99ukpb1bVvKiylsxrtiiI4tr0snTE1hZdWNH1kXzbrE62fUb9n9LgAAUfM6YFERn0gCbTiZp71gK3Aj8GMioe5yjbnBylWrBfD75sk7ndSTTNLR0Xnx3KPn3Op3ufritVC17rY3bSs7VCjVj5ZTN263Lpq10I9nlI//Ai2u5cxnM84kkpuL4j3ADcAN1SrMfFauWl2Ps6zTBcybe/Scqp1U0+5ZP5Zs+kpbyoUUmFG8aJLxd0nGd0GIkiZDq6G7669Ao+b5wI/LsPAWThoGN2ajfw38v2oYYxbDylWrJwGBuUfP2eB3WQpBX96yuw3XSCkru082k2oj3hFEiLK2X+qGcbm1cNYNfj0f/wQaNb8K/KoClv5NZTfOvwCcRySk0uR5iL685dO25GcSWf6OMdtKi44t7VLTCw1GPihGIHBB9sLDf+LHc/FHoFHzczjrnJVq+V6guH2w/REH/gu4zav9sIo+RE1D69hymQ1XUurQRUpEx4drpdArmeDKNgzjFD82M3gv0Kh5MPA45Z/86MvrFDFT14e/AAuIhN71/HkoPoL+izV72Lb8pUTOLvazonPL61KW/D0YjJ5AIHBM5sLD/+Xls/BWoFFzV5xTG27MvqZwlkF2KeIzCeA7wM9U2rsqI2oKrbP1YlvKH1Dgj7nobntDWlnXlt+EEK2GEZiVufCwt716DN4J1Dky9g/cPWi9FagBCkm28zRwOpFQVZ9mGO3od72xl424Tw71vUl2ryeZ2K3UGdtCEUK8HTCMQ9MXHt7mRf292YvrHLb+Le5HQZiAs/wy1GHcm4HZSpzVj/XNj7UYmeThmhOxsX8yqa30xCe7LU4AKeWMrGVFvQrt6dVm+e8Cn/XI1wycBEn90Q58kUjoUiIhL4IqKypA5twDUvY39/6WJkQEZ5vldmwrJbrbbDTNs5NOtm0frX/QcqMXvtz/FYiaxwN/9KIyfeg7s/sacAKR0FofyqKoEPryln1sKR+UMDO3x3a9FNo0P8piGMap2YWzfuumD3cFGjX3BJ4FxrnqZ2BeBfbFOTB9KpFQl0/lUFQQ7Z71Y2Um/b+0b54mhS97sh2EiAeMwKcyFx72kmt1da3wTtza3+GfOAH2wgk0dpIS58jB/tq0Djl24uc0w3jc14JI2ZDNZu4zlr1S65YLN8eg1wOfcNH+UNjAJURCVxEJVV/wWEV5REK2ddGsBbphXMXQk4KuIaXcW/Z0/9Qt++50cZ2dQg+7VegCyACnEQnd72MZFB5h3Pb017LZzF34GMLHCNR8OXvhYSvKt7QjlRdo1JyMkytlkgfPpT96gOOJhFb55F/hA8btzxyfzWSiIN05GTMEQogOI1DzicwFh1Z0N5obXdy78U+cncDnlDhHH9kLD3vYCAS+BCLuh38p5Vgrm/1lpe1WVqBOsK//8OiZ9KUTmEck9JhP/hU+k73wsP8zAoHj/RKpbVtzjNuevqCSNivXxY2azTihSPzIatUDHEMk5OlGZkV1Yvz42eOy6fSDvnR3hegO1AT3y5x/SEWyr1eyBf05/ogzg7OMosSpACB7waF/MQzjy4D3xwalbLQymbsrZa4yAo2a84HPe/4wnOn1U4mE/uqDb0UVk114+ANGoOZMfFiCsW1rjnH7M2dWwlb5Xdyo2YTTtZ3q9YMALiAS8uWku2J4oN/21JVWNltKzKuyEIgtAV3slb7oU+3l2KlEC3o1/ojzViVOxVBYC2f9UNf1u7z2K5GTLLTryrVTnkCj5seAi72uPM7m+0t98KsYhgR6us7VNH2l134tK3uWsezlso5YltuC3or3mcdewxl3qu17ioJILp6XNYScL4Tw+iSTbqVTZXWvSxdo1DwBJ9Vej4cV7gK+MBxi1Cqqi/RFn2o3DOMkhDdrpCLV0yrMTe/Lze8dX3PdX08r2U5Jn3IiJLwI7A9042TPKjeqXiF8kUjoDx74UYxQjNufPjWbyfzGNQdWNq11tq63hZgJmgDQNG1dnZWcGf+fU4ru9ZXagn4VR5zghEc8CCc9oJtp525U4lSUS/bCw+/VdeOOihuWEq2j9S3R0WrZQt+rV5wAtm3vka4fV1J6w+Jb0KhZg9Ni7jbAHa/gRO2bUMHqvwDMIhKq/gSxiqrHWPZKrRXvfF5KuyLhOUWicwPJeEBqxoB70IUmWoN1DXskFx1b1PCslBb0WwwsToD9cHYUxXB2+ZRLAviKEqeiUmQX7Jc0DP0Uyp0/SSc7NXPjeplOTR1MnADSlhMtyyomQRhQbAvqtJ7rKHzdsw2n61vOwe1ziYSWlvF5haJf9NueXmRlM8WvVdpWVuvcus6WcjpCKzg5sdOK1u+aXDQvWehnim1Bz6S4TQnjccS5Fihl8/BqnES5CkXFsabOvF7T9GcK/oCUaF3mWtG+ucdGzCxGnJBrRW1ZVN7RwlvQqGkALcAeZTyTF3GykRWSabkH2E9F4VO4SeC2p/fOZDMvAIOG7RTJ7g9JdEmpGc3l+BOatqXW0HbtueqEVCH3F9OCnkZ54gQndX0QZ3w61JTzlUqcCrfJLDz8Td0wfjjwDem41rbpbZlMTC5XnADStidl9ZpvFXp/MQK9rELPJAiEcdI0vD7APS8Bt1XIn0IxKLptXS+E1rLDRWnbWseWFrrNGlvoMyp5dNpKp84v9N7CBBo1j8KZna0kO+FkI2sBNvZ57wK1lU/hFemLj8johrEtEoKIt78j2jZ32ZK9EHrFt7LaljWj5rq/nlTIvYW2oAtdfD57Ac3A8zjjznuJhPyNd6oYdWQvPOxRrSb4a9Gx+X2ZyewuNd3V4ANWJnNxIfcNLdCoOQ040eXnI4CDcZLoLnbZl0LRL1qg9iopRUWycg+FlUl/Onj934bMJl5IC3pegfdVgjuJhN7zyJdCsQPZc/Z/xwjW3u2RO2Fl0kMuuQwuPGdp5eseFbgNWOKRL4WiX4xA4CqhaZ3lWxoaK5P6Ys0t/xx0jDtUy/hZYLJHz+ZaIqF2j3wpFP2SvHzuFqMm+LPyLQ2NtOV4kt2nDnbPUAL9hkfPZSvgWn4LhaIYdE1c510rmj1jsPcHFmjUnASc4NEz+RGRkC/BhhWKviQXH9eu1wQ92WJqZdJH1i5ZuetA7w/Wgn4Zb5LRdAAq+JeiqjA0bYnQPIm+oFtW9psDvTmYQE/26Fn8XI09FdVGcvG8Vr0m6Gr27F6sbOYLA73Xv0Cj5lTgCC/Khhp7KqoU3ai5AQ8CX9vZ7P61Sx7td5/7QC3ol3Ard+iOPEgkVNF0bQpFpUhdfkyLHqjxIlOesCyr3+XMgQT6RY+egdoQr6hq9GDwFi/8WNnMSf1d/2grGTUnAJtxf/fQ60RCQ251Uij8RnzvoXelZe1avqXBnAgZbBrbnLrsqM35l/sT4bF4s7Xvbg98KBRlYxgB9yeLpBQym/lIz7U/IXqRgNcGfuWBH4WibLRA8Cd4M1n02b7XduziRk0BbMA5/uUmfyYS+mz5ZkYe4XA4P/LbdcDiWCxW0T3K4XB4AU68qP4CZi0B1sZiMRULKg/9f/7vCSuTdnVlQ2iiTe736QlEQrL3Wt+NCAfgvjgBPFlfqmb6CHERjmDc9jkfR5TTB7ltUe7e3h8HJVRADwTud1ug0pbjg2/HZqfgH73X+gr0GA/qmgYe8sBPVZJrvTwPI5r7QSgmxOR4YGk4HA7HYrFzvC5vtaHBbxDiRqR0dXedbVnzyBNo3zGoF5sTVhEJdXjgp9ppwzmcnv86JBaLCaCiguhHnL2+Z8RiMdH7AmbkrucHa1sQDodHfVzi5OJ5rbphPOW2H9vK7qDBvr8GR3pQ19954KNqyXUZPes2hsPhuewozvtjsVi/2zhjsdhanDHoklwXt7cbvsBpSEd3d1fTjQetTMZVjdhWdockZNtb0Ki5NzCpWIMl8CcPfCi2k9/6DSjOvsRiscXseID+unA47Po4uZrRdd31xkXacmzw+pUH9/6d38X1ovV8mUhogwd+FGybFOqdEFpLkV3nnEhjuT/HAwv8rpOfJBcdu07TdddjNduWtW0uKF+gh3lQx0c88KHYzvy8fy+JxWJtJdjID+I2v4TPjyg0I/CY2z6kld2mxXyBHuhB/f7qgQ8FkOuOzs392QbcX4qdWCy2ku2TRuFwOBz2u25+oum6642MbdvbtsA6Ao2aOtsT8rqFBfzL7coptjGd7WurK0tsPXvJF/fckq2MADTkSlzeVWRb2T1zmtzWgu7FEMljKsALKqyJp+QLKVayFYf8cdeonihKLj6uXTOMlvItDYKkRm//cF/YLtADPajbPz3wodhOvpDKaT1hR4FOL9nKCEHT9Wfd9iGl/UnYvg66L04io67ctQnALkBR+Q+HQHVvvSVfoOXOPKoWNA9N058CTq+IMSudFunUVi2b6pGWhZR2o0RMMKz0oWlY2ivQvXESGfVlI87Z0BROF3gKMLHEorzg6VNUuCWkUd+CCk0vvrGxLSkySVPLpDqllbWw7TopCEmh10nBFAtA0+jt1NrZ7EzY3oLuOYDZKblXPhmctPZtOIPlsTitbf0gxYsDb/n9YEcZo76lcwuje+tLKS2YQsrgR96UEpFNdotMuo1sOiVsK2DbcqzU9HFSExMsmIAQoA/eObWtzG4wtED7I0D/v6JdOMLtztmdBOyM85PwMpGQ6+fpFAoviP/PKZb2vYfW2KmevUUmtVXLphPSziJtu1EiQlLXGyU0AqDpJYU/kNnsFACDqNlMr7HyaAL6C2GyETVBpBhhaOmedXaiYz8JzRaA0ECvXCASiR2s+/6D0zVgd5frMoWPJuhVKIY1msDdpRZA2vZeGjDVg/qo8adiRCE0/U23fUgpd9f46CSQGyiBKkYUQjdecduHtLO7aoC74QQd1nngQ6HwDKEbr7rtQ1r2Lhru5//sIBJKuF0ZhcJLeq46vlMIrcdVJ7Y1ScPZNeQmaoJIMSIRum66aV/CeA0Y53I93nfZvqJ/yt1/qxgCoRmby7cyMNK2xnghUPVF8Qe3nrvrEQWGC0JoXW7al7bV6IVA2122r+iffIGWu382//PqB7cXTXS6al/KRg1nL62btLtsX9E/bglUtaA5hNDa3bQvbbveiyRJagbXH1bm/bvcMCWqBe0HIYTr320NZw+tm7S7XQlFv6xlu5jmlhkyMz9Y2MqSrYw0hHA1ALuUdnD7ATTFiCIXgyhfTCWFzMwFvt4WujMWi5UbPmUEIWX5NgZFU+Ic2eQH+1pUYiuaH5V+VEeW9wMvBJryu5KjlVgsdj/bJ3XGA/cV8/lc+ofe8WsbSqA7ommuf7e9EGiwfBOKMsiPJj83HA4/WkhL2ic3C5Qe+HrkYtuuf7ddTaWm8J9YLLYyHA4vZntXdS5g5q6tzB9ThsPh6TgTQgvYceb2/konEVYUhgF04O5a6Bi/KznaicViS3IB4fPHk9cBFBAovuCES6MOKce5aV4IrceLLm4lQ3cqSiTXAh5LcRsNzlHiHBTX9WPgROlzk5DblRhpuJVDNJdnZUZe1rP+Mm4vAdpUl3ZopJRuR07MGoBJ6bFuC0GFf6wycrO7sGP+T0WRSNsa56Z9oetxDfe3bqkWVDEikVK6uo9dCK3TC4GqFlQxMrFtdydANUegrp4Kx4k6r1CMOKRtuRqNRKB1eNGCTiFqqvVWxYii4eoVNbaVdbV3KHStXcP9mEE63oT2VCg8wzZqZ+DyMovQtE0a3sQM2s0DHwqFZ0hpz3TbhxDauxrwrgf1cb0yCoWXSNv6ePlWBkdo2lqvWlDXK6NQeImdzeznuhMj8JaGkzLQbT7mgQ+FwjOklZ3htg/Ntls0IqEk8IHLvvZ1uzIKhZfY2YyrAhVCa0t896RE7yxUJVOpWcA7wPPAM8DrwASiptuxjxQKT6j/7v0TSHSm9GT3Oi2VeF9kUp3YlY1+ogUC78H286CvAUeVYGcLsAnowTmYPRloxsk52jfv6KHAKs+eokLhEjKZOM7uie+4AUdatibpEJoWR9dtqdcEbaNmPIFgDaJ4H5pRs8Ziu0DfGOL+BM5kUgfO2s94nKWTSblXIRyCEqhiBGBn0rM/clHomi0YD4zHssFKQjoJ0kprQusQiB4MQ0g9UG8HguMxAoOuoQpdfwN2bEEBbBwhtgJZoBEnPWETsFeZ9TrU7werUFQCO506qOCbhV5j9zZi2azzSiYQ0u4WmtYpND0j9YAhjcAYGahtQnOaW03TX4TtAn0dZ0fRFJyW0Y2NBbPLN6FQ+E82mShv2VAIpNAbJTRi22CnIJOCRKetGTXtoqa2y5qy51NAXu84ar6H+xvb9yESGqo7PeLIBelayvYA0G04QbiW5N3zHB+NAL8sFoudU4D9RcD8WCx2SD/vhXEOZs/NXVqb870s9/58Bo72d3Lv2dGcj/wD3otH46Hu+ivu/WRiw7on3fShGYEO+87Lx8GOewmf9aB+pUxEDWty4nwOZ9weisViAifS3qLclz6f+2OxmMjdMwMnCt91BbhZQD8RGHK+H8WJkNBrdwmwNBwO9w1kfUjvPXmvXnHel/NxSM7GIcCCcDi81O/n6zV2KvFFt33otfXbVlXyBfqcB/U7xgMf1cYCHHGe3Bu2MvfFv59Bor3HYrG1OJHh5w5mPBf5fXxvi9iHuTnfS/LsLsvZnU8B5OzPx2kxYzkbMZwfhIJsjCSsVNL1oZpWU/t877/zj4F50YLOJWoGiITcjoNUTYRxvtM7HOsbquuaC4E5Fxgq1cJ8dowgn8/avHu22YnFYscWWf62vDApvdzPKDuM33D1iprEhnUHuO1HCwT+1fvvfIE+B0goZdWmYMYCRwKPuV3JKqPQM7fzw+Fw/or3ygKi6s0H+r0nFovFwuHwyWzv0i7LXe5P0M/1CcHZG25z/AC21wKLvX+U/mGnkxFpWbUuu5GabjzS+8d2gUZCbUTNfwMHulyAExl9At1GbjyX3zUM5bWuO8SgzUWBv28gkeZE15aL1tcvvd3p3L3T2S7Wk/u06oeoxEiDYyV7XO/S67V1H8SvOe3D3r/7Lpb+zYN6nuiBj2pjWysUi8VOzk20FNL6LMFpVQdKwDufAsNzxmKxZbFYbDHO5NNAITf7o9/WPxwOTy9wAmvEkI13ftptH3ptw9P5f/cV6GoP6jmdqHlI+WaGDTGc1Y5yxmsfEWjeGHWg8SfhcPi6cDj89g6FcVrNGIUn9Y0B43PLMfn0pogYFdQv/vV/2NmM6xEq9WDto/l/9xXo4zi7idzmKx74qApya4Vrgft6W8KcWOfi5NscbHy6KHdPf13Y+Thj1MEixa8EdmjpckKbS4GJeHO+7weuy62p9q6tLmCQH4eRRran+2wP3EhNNx7Iv7CjQCOhDrxZbvkKUXM05SbtTbnwdm4SyMTpOvadTZ0fDodl7yt3baBJoiEFkhPXITia6rW5FGejQt8u9nP5vnOvRTk7J+N0pZ/L2XiUAjdRjAQarl6hZ+Kdc8u3NDh6bf36/PEn9DdjGzWvBr7vQb3nEgl5MeYdceRawaWxWEwFBfeAukX3nN7z4Xv3uO0nGNppeerms7+Zf62/VuyPHtV7VPz6ukEsFrtfidM7svEuT8baerDu3r7X+l/z9GZfbgbYhUhosxeVVyhKoeHKe6fFN7zzFtJ2NUufZgTa7Tsv/8hE4kDjwIc9qHsA+IYHfhSKksn2JC53W5wARv2Yf/Z3fSCB/sGj+p9L1FT5QxVVScPVK/RMV7sn+VH12rrf9Xd9IIH+DSecidvsAXzBiwegUBSLlUxcaGdSruZfARBGIG5Y6V/3917/Ao2ELGCFR8/hMo/8KBRFkelqX+iFn0DDmFVdN53d7wGSwdYi/9ej53A4UfMIj3wpFAVRt/ie+VYysYcXvvS6huUDvTeYQJ/Em7QQAFd75EehKIhMV7sn30ktENxau/W9hwZ8f8BPRkIS+IVHz+M4ouYsj3wpFINSt/hXJ2bjXft74SvQOObPbXdcMeD22qG22y3HOSPqBf/tkR+FYlAyneb1HrmSem39oL4GF2gk9C7wCN5wHFFz1MUsUlQXdYt+eWo20b23F76MhqaXEtd+9eXB7ilkw3pB5w0rxE2jbBO9oopouHqFnm7feq1X/gINTT8f6p5CxPAwsMGjMh8EnO6RL4ViB6yexJVWqseTZNNaINBhWNm7hrxvSEuRUBa43YtC57iIqNngoT+FgsDPnm9KtW+51DN/Y0L3DbT2mU+h3cmlQNzlMsdxMqIdBHzXZV8KxQ6IN59ZqiU64kgP4hVoetqoqS3oO16YQCOhNtxdcumNA3pw7r/fJmq6n8FYoQAaLll2TOadN0+xehJTtI7WuJaMb3XTX03TuEfi1351YyH3FjMh8yMqv+TSgpM8+GAgv1trAD8naroZAlShoOHqFXpqy/vLZU4LttAa7FRigt7ZulVkkpXvNQpso6Gp4B5i4QKNhNYycA6PYtkCvIqTMW3nAe45AvhWxR+QQpGH1bbppmx3V5+JIYElmSDjHfV699aNWFa2Uv4CjeOeSlxz2ouF3l/sksb3Ka8VTeFEiQsB+xZw/xKi5sxKPRyFIp+GS+84PLXpvYEbAaELy5JTRFerrfd0fogsuwMpA03jFhXzgeK7kFHzf4FTSijcv4FpONHli+EJ4DNEQl5EG1SMEpouvSPQs/mDNdnujt0L/YyO7JS1DbYdrB9Xis9A07gnM7efX9TBkFI2BRTbiq4H1gEHULw4wUkVcXkpD0ShGIhMT/fSYsQJYCHG2Mn4OL1zyxaRTSeLdFl06wml5mGJmvcydGzbduAdHGGWiwXMJhL6V9mWFKOehm8v/VJi/Rv3ydIaKAAE0tYM40OrrmkymjGkncCYcX/P3Hb+nGL9lFrAK4H0AO9ZOOPMOiojTgAdWEHUVJHsFGXR+J3lu/RsWLu8HHECSIRmZa0pWpeZ0pJdg0cfEZoVaBr/7VL8lFbISGgd/e8uehUnKHMYCJbzAPphV+ButfSiKJXxZ1+jpVo3/NlOp8dUyqaNVmenUpP0js3tWrqnq797asaG/pD44akvlGK/nF+RH+CIEZy1zDU4M7OTKlX5fjgBdSxNUSI9Ru2vMu1bXdkAY6GNs3u6mvSu1g+FndnWuxSGkQjUN15Qqt3SBRoJteNMGD2Ps5bp1XLIfxE1VaAxRVHULfzpd5Kb3jvVXS8als1kuto0I9G+CduWwbETfto3nUNxFsvjJ0CNu5Xul18RNV3PdKwYGTRccse85Ia113jlT6IZ2Uy22QgE1tq77nNFObbKE6hz0uU8ryqeRwPwJ6Lmrj74VgwjGi+9c9+eDW9HpWUZ5VsrjppxE69IX3zEkCdWBqP8w9GR0BM4oVG8ZirwCFGzlLVVxSig6dI7pvRsXL/KTqWavPZdM2Hy3xM3nVX21thKRS9YBLh6AmAAPg48RNSs98G3ooppuOI3Y3q2fPCk1RPfyWvfWiDQUzN+p69WxFZFShQJteJfAOpPA1Giph9jYUUV0nTZnfXpjWufyHZ1TPPDf3CnXW/ovuHM9ythq7JrilHzAeDzPjwTcNImfjE3LlaMUpouu7M+uXXjU5l205OwmX0JjA29mrn76oot5VQ6QNe5bF8b9ZoTgF+rlnT00nTpHYGU+eHjfolTGIFkcKddK7oEWFmBRkKbgJIXZSvAKTjd3UrvYlJUOU2X3VmfNDc/l25rPbh8a6VRO2X3/+6+/htrKmnTnW1zUfO3wJe9eCgD8BhwPJFQwscyKDyicdHdE5Ib1z+V7erY068yBMZPfjazfPFhlbbrVgzac4G17j6SQTkKWEXUdHPboaIKaLzszt2TG9a96Kc4tWBtR3Di5JNcse1KiSOhDpwWtKxF2jI5HHiKqLmXj2VQuEjDZXd+queDtc9nuzt38asMQhN23dTpX+++/gxXYke7F8U9EnoWWOya/cKYDjxJ1Gz2uRyKClP/7Z9/JfFOy9+sZI+vRxCDzbsvjd989oNu2Xc7zcKPANcKXyC/zU1eKUYQ6daN18lsptbPMtSMm/jvunTc1UlRdwXqpDA8HXjdVT8D8xfgYp98K1ykdtLOx2k1QbeDqQ+IXle/NThh8nGDpQ6sBO4nKoqEuoCTcEKgeMkbwClEQpbHfhUe0H3zgjfqdp7+FaEJzzemCMNI1U7d48SuG88q+RhZoXiTSSwSagFOBbyKzGfiLLN0eORP4QPxmxf8sXbnGVd57FbWT91jYfzGs570wpl3qf4ioT8D3/HAUwb4ApHQ257VTeEbPbedv6S2ebdfe+Wvduq0ZfFbv+VZSk5vc3FGQjcDt7ns5TwioX94Wi+Fr9Rle74eGD/5Wbf9BCdOeTT5k4vO9bJufiTLvQT4vUu2byISGjLnomJk0XbHFXZw4uRjjMYx77nlo2bchBdqxoz/nNd1816gzqTN6UCl+/AP45xLVYxCuq8/o6t2yh7HaDU1nZW2bTSOWR+YssecQvJ5Vhp/0s07e2RPAF6pkMVXgFPVjO3opvv6b6yp22XGKULXKyYkva5hc+2UaUfErzmt4sIvBH8EChAJmcDROMsh5bAZZ8a2q0w7ihFA/KYFj9TtPP07lTgGotfWtdXtPP2Tbm3jKwT/BAoQCW0BjgVKnXFN48zYvuNrPRRVReLWb91a27xbWQmn9dq6trqp02d333Cmn4c+XDpuVixRc3ecLGbFbnr+GpHQr/wuvqI6qTlryRPprZuKyiYGzumU+p1nHNF901mv+l0Hf1vQXpwW8JMU15Jeq8SpGIzguAnzjMYx64v5TK5be1w1iBOqRaAAkdD7wGwKmzh6ACeBk0IxIF03npWobd79GC1YW9COMr2uYXPd1Omz4zed/bTfZe+legQKEAltxJk4en6Qu14AvprbiK9QDEr3DWeurZs6/QvC0NOD3Wc0Nn1QN3XarGppOXupLoFC78TR0cDf+nl3E3AikZBvpxgUw4/4zWc/VrfzjEsYIPG00TR2bd3k3cLdN561zu+y9qX6BAq9ERk+C+SPMXuAz+e6wgpFUSRuOe8ndVN2X9r3es34Sc/U7bTL/l6cTCmF6hQoQCSUBr4O/DB35QwioWf8LpZi+NLz04vPq5nY/Fjv38HJO9/foPPJrhvPUsHlykJlMlNUiKbL7qwPfP37r9ddcPsP/C5LIfx/wmrrUqmp83sAAAAASUVORK5CYII=" width="232" height="212"/>
+</svg>
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 0000000..895fff8
--- /dev/null
+++ b/skyquake/framework/style/img/viewport-vnf-50.svg
@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid" width="232" height="212" viewBox="0 0 232 212">
+ <image xlink:href="data:img/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOgAAADUCAYAAACWEW+uAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABmJLR0QAAAAAAAD5Q7t/AAAACXBIWXMAAAsSAAALEgHS3X78AAArUElEQVR42u2deZwUxfn/P1XdM7OzB7CzIAuKXIIaNR7jgVEMKkK+XjFm0ASPqFE8QaMo+/P4mlPBK4rGCCoazyCdxOubmLBBEo3xGu8DF1lQFJBdes+5u7t+f3QPO7vMzM7V1bO79X695iXb01NPVTufqeup5wEEg5rGNWulxjVrZafrISgM8T9uANO4Zu1EAI8CqALgAlCd8l8ZQI1163QArzpdX0H+CIEObKq9Xu8xB+y3LwihkCQJhBLIkgRCCCRJQvCd99De0eF0PQUFIgQ6wJEoRU1NTfEFCcoS6nQFBFwgTldAUBhCoIMdU5rDnK6GoDDEELccUdTdAOyZ8hoDwAegDsBI69/VF3dERjxZtSVrUbIk4+fhCS9UvdAalyhiMmURiSIkUdYuUfa1RNEsU/axRPEBIfiw6ej6sNPNF/QgBOoUiuoBsC+AfQB8y/r3vgD2AuDJpQgjx5FrQgcJJajHKje1Nz0k9T5CgMrnW+MVLqa6ZfalS2IfuyT2qizh2aaj61WnH9lQRAiUB4oqAdgPwGHWyw/gIJTZ82cMCMepOxxHPYB6AIcDOJ8QPFz1Qmu00m1s9rjYu26JvSRLeLrp6Pqo03Ue7JTVF2TQYAryYAAzrNd0DOB5IGNAKEYrQjE6BcAUAGdIlK2o/WtLa6WHveORjedkCY+K4XHpEQItFYo6CsBJAE4BMBMcBKmBQDd0R5qrGwRtYWlkWxizAMySKbuv7qWWrZVu4+8VLnb3+un1HzhSsUGGEGgxKOpUAHNgCnMaOG9nxBgFY8zppwAA0AxCdnRLY3dAOh/A+dUvtkaGe/V/e11syefH1L/sdP0GKkKg+aKoewA4E8Bc9FlkEfTQHaXe7iidDWB2zYut4WFe/RWXhJs3zRj9htN1G0gIgeaColbA7ClPhClOsfGfB11RWtkVpbNHaNsPIg83NRGCh6kWX6nN218sMvWDEGg2FPVbAOYBOBdALYAdEOIsCA9N6B0hMpqBjWYM0w3JdQ9Z0bSSUHq/cd5e7ztdv3JFCLQvikpgzikXAvhun3frADQDmOR0NQcaNSTU0mpu3SQZzhibx3R9Hnm46V+E4B5j2MhnEfCVx6S6TBACTWIOY88BcA2AvbPcKY6GFEC4O+bN5FnKwL7LGL5LOlo2kBVN9xKwB/UL9hZbNhC+uICiVkNRGwB8AWA5sosTML19DKerPZColqKxcJwO7+8+Bkw2GLtbZ9hIVzRdJ634rNLpujvN0BWoonqhqFcB2ATgVgC75fjJCgCfOF39gYSXhXbk+ZHdDMaW6Axf0RVN/yv9YcOQPU839ASqqG4o6pUw55K/hTmvzJeh99wKhDCGji69kGcMALUGY7/QNW0jXfHZ1VDUITclG1pfNEU9DcBHAO5G7wWLfNkXgJgj5cBwV7g7rpGcnP+zUGcw3Ek6WtbRFU1nON0mngwNgSrqIVDUtQD+AtOXtFgIgHVON2sgIGnhzlKVZc1RV5KHm96gj6w/wum28WBwC1RRR0BRHwDwNnbdMimWQodtQwYJOmvvZGNKXS4DO9wwjNfIw03L5Qc/qXW6nXYyeAWqqHNh9nIXwx7ngvEAWpxuZjkzXA616YzY5dhBGdhFGpWa6Iqm863960HH4BOook6Eov4DwJMARtts7Qunm1vO6NFogoOZkQZjK0hH67+kR9ZPdLrNpWbwCFRRCRT1EgAfAjiBk9UJTje7XPHQhNEZInb/QO6EgU3XDeMDuqLp8sHUmw4OgSrq7gD+BuD3MIM482IkgI1ONz8blFJUEP7eczUktN0Bn71qg7H7SEfrmsHSmw58gSrqmTB7zdkO1aDd6UeQDUoJJPCXSjgU9zrVZgY2QzeM9+jDn811qg6lYuBu/Jq+s3fDXARykn1guv4N/B+7ElEtRWPdMdKva5/NDDOAJ8mKpuMlpl+h/XTfiNPPpRAG5pdKUacAeB3OixMAvAA+dboS5UQBrn22wRi7QAcNSo+s38/puhTCwBOoOaR9B8CBTleFMwmYq8ZvA/g7gKdriPaHXD5Y59VafFV6yzCv0VXpNhLUxiUUy7XP5/TDSoUB++qG8eZAHPIOnNUuRaUAfgOgwemqpIEBiAAo9vRFAube7SfW6zOYovwSwNa+ZyUb16wd53a7v5x+1JEZC/z4k0+x7ZvtZ808bsZTqdenvLJtH93AgbpBDtYMcnBcI5NjCVIfipNK3Sh873KEHO5u3xGqtvFZFwUl5HZj2MgGBHwD4kTSwJiDKuowmPuaJztdlQwQmGI6OI/PGAA+APAmzF7xDQCfIODT8iij0zAK+56tn16/DuaPwcrU61Nf3SbrBmYldHJyXCPTQjG6d3eMVuYam0zSQp0w0x+WJQZj15KO1gPJY5vOMM6dUPZne8tfoOZ881mY0dfLmVxczj4A8LL1WouAr+y+IE1H12sA/mq9AABTXtk2OqGTi6IJcnJ3lH67O0bTrtBKREd7B6sv94EZA5uFcOef5WUfXqJdfMB6p+uTjfIWqKIeBuBPMJPSljsTALTC3BtNEgOwBsCLAF5AwLfZ6UoWwvrp9d8A+LX1wl6vbDssliDzQzE6qz0sjTas3nW4FFZVRspq/pkO0t3+GdMTx+mE/Nd131vfT1xx2H+crlMmylegivo/ABSY87ptMOd4ju2t5cgmmImNVgN4CsCzCPhKdpqjXPh8ev1bMAOpYcor28bFNHJDV5T+UO+Ixp2uW79Ew18xLb4XCAFjrC4Rj62Wl75+rrZgmuJ01dJRnqu4inoegOfRs+hSD2Az4MCOe+40wZwnj0HA9z0EfI9xEGdC1/VwdyiU9s14PAG1rV2HOT+2hfXT6zd/eezoS9r+Z9SorpjrUErIjQT43OZ2F/i0Yh2Ido8AIVLKVa+m6Svlpa9f6XT10lF+kwVFvQ7Akgzvvgcz6VC5kIDZyy8H8C8nItI1rll7qsfj+fNh/oMlj6f3uej3P/wIra07bpl53IwbeNeLrlh/LGPsCgb2fQBS0QUWi2EkSGerykhm/2BZlq/XFky71emqplJeAjWDd/X3gN6D8yLthCnKexDwfeVwXdC4Zu3C6qqq2/2HHARZNmctX2/ZinWfNQUBfGfmcTMcG3pKj6yfYDB2JWPsAjiVQIoxkI7tGxihk/utryzfoi+Yxv0HLRPlI1BFvQHWIkQOrIPpYsebFgC3AVhebnPLxjVr76+tHXHpQd/+NqLRKN58OxjWdd0/87gZZRH5QfrDhhqm6z8zGLsKua14lwzS0fIpM8PU5FZXSf6tfuW0q3k/o7R1d7oCAABFXQxgUR6fiAJog5l5mgc7ANwB4D4EfN2cn05ONK5ZKwH4c/3o3U4NRyLo7OyaP/O4Gfc5Xa++8BYq7W77zNC1/kKp7lpPSb5Xv3LaAieeUSrOCzS/njOV7TAXkezcFI8AuB3A7eUqzFQa16ythLmtowI4aeZxM8p2UY0+tmk4tPgNBmMLkGNG8byJhr5ENLQHCCloMbQchrvOClRRLwdQzK/85zDTMNixGv0EgP9XDnPMfGhcs3YUAGnmcTO2OV2XXJBWNI03gFsYY6X1k03E2hDq8ICQotwvJVm+Tl8w7Xanno9zAlXUswE8XoKS3kdpHeffBXApAj6RJo8j0oqmYwyG3zOw4j3GDD1OOlraGZVyDUaeFdnlukKbf8TvnHguzghUUU+Cuc9Zqp7vXeTnB5uOEID/BbA0T39YQalQVJl2tCw0gBtQ6NSFMZCOb5oZkUqZ4MqQZflMJ5wZ+AtUUQ8B8AqKP/nRl0+Rx0pdH/4OYB4Cvi+5Pw/BLkiPrJ9oGOwPDGx6vp8lnS2fMlbw9yAbEZfLdXxi/hH/5fks+ApUUcfBPLVhx+prDOY2yB55fCYM4FoAvxdp78oMRSW0s/Uqg7FfI8cfc9Ldto7pmm3bb4SQVll2TUvMP3wDr8fAT6DmkbF/w96D1jsAuAHkkmznDQDnIOAr69MMQx3p4XVTDZBnWH/fm2j3JkTDexa6YpsrhJANLlk+LD7/iDYe7efji2setn4a9kdBqIO5/dLfIcm7AEwX4ix/9J/u0yQnokdQM2JjehKxHYiERtstTgBgjE3WdF3hFdqTl7P8zQBO5GRrMswESeloB3A6Ar5rEPDxCKosKAGJSw6MGT/d+zJKSACmm2UPhh4j3W0GKOV20skwjOOkr5vu4GHL/l8BRT0ZwAs8GtOHviu7nwA4BQFfswN1EZQIaUXTvgZjzzFgiuVju4kROsGJusiyPFdbMO1pO23YK1BF3QvAWwBG2GonMx8D2A/mgem5CPi6HKqHoITQxzYNZ4n4H9G+fQIjjvhkmxAScsmu7yTmH/6BbW21rfJm3No/wTlxAsBUmIHGThPiHDwY507oYMNHnkRl+RVHK8JYlaYlnpGXf1Rhlwk756C3Afi2jeX3hwHgagR8NyLg0x2sh8AOAj5Dv3LaPEmWb0T/i4K2wRjbm0W677erfHuGuKan0It2VToHEgDOQsC3ysE6CDghL33jXE1LPAwHQ/jILvePtPmHryy+pN6UXqCKOhpmrpRRHJ5LOiIATkbAt8Yh+wIHkO9982QtkVAAZs/JmH4ghHTILve3E1ccVlJvNDuGuI/COXF2AjhJiHPooc0//EXZ5fohQELFl5Y/jLHhupZbpP98KK1AzWBf3+P0TPrSCWAWAr6XHbIvcBht/uH/J7tcJzslUsPQZ8hL37iilGWWboirqPUwQ5E4kdUqAuB4BHxcHZkF5Yl831uztXj8OUeGu4R0u9ye/ROXH1qS7Oul7EEfgDPiTMDcRhHiFAAAtCsO+7ssyz8CwP/YIGPVeiLxaKmKK41AFXUOgO9zfxjm8vpcBHz/cMC2oIzRFhzxrOxyXwAHtmAMQ58h3/vmBaUoq/ghrqLWwBzajuX9IABcgYDPkZPugoGBtPT1G3RNKyTmVVEQkBaXRKbGr/xOezHllKIHvQnOiPMeIU5Bf+gLpv1GkqSHedtlYKN00MXFllOcQBV1HwBX8W48TOf7axywKxiAuCJdl1AqNfK2q+vahfLyD4s6YllsD3oP+Gce+wTmvFO47wlyItowS5MJm0MI4X2SSdLjsaKG14ULVFFPgZlqL8KxwV0AfjAQYtQKyov4ld9pl2X5NBA+e6QkFmkl6rav2PbNJ7sX/+Osgssp6FNmhIT3ABwAoBvAehQfVS8XTkfA9xcOdgSDFPneN+ZqicSTthnQtTjtbN1kEDIFoAQAKKUbvXp0SuhXZ+Y96iu0Bz0bpjgBMzziwTDTA9qZdu4OIU5BsWjzj3hKkuQHS14wY6AdrZ+TjlbdINLUpDgBwDCMifHKEQWlN8y/B1VUN8wec88Md3wEM2pfXQmb/y6AaQj4yj9BrKDskZd/VKGHOt9hzChJeE4S7tyCaMjFqJzRB51Q0urxVk2MLjohr+lZIT3oZcgsTgDYH6ZHURCml0+xhAH8WIhTUCq0eftHZVk6E8Wun8SjnVTduonFY2OziRMAmMFG6rqeT4IwAPn2oGbvuRG573u2wRz6FnNw+xIEfMuK+LxAkBZp6RuLdC2R/16loWu0c8dGg7FJIDTn5MRmL1o5LrpoVjTXz+Tbg16A/JwSamGKsxlAIc7Da2EmyhUISo4+dsptlEpv5vwBxkC71GbSvj1igEzJR5yA1YsaLK+8o7n3oIoqA2gCMLGIZ/IezGxkuWRajgDYX0ThE9iJa+kbeye0xLsAsobtJNHubxDuYozK9cXYI5S2VMh0XOTGU2K53J9PD3oWihMnYKau98Ccn/a35HyDEKfAbhILjvhMkuXfZL4hHqJt2zawaHh0seIEAGYYozTJfVmu9+cj0IUleiYeAH6YaRo+zXDPBwCWlsieQJAVydBvI4Q29brIDIN2tDShW3UbRJpcyqPTejx2ea735iZQRT0W5upsKdkNZjayJgBb+7x3hXDlE/AiftVRCUmWd0ZCIKH2L0jb9i6DYSqIVHJXVkPXJ7sX/+O0XO7NtQddYOPzmQqgHsA7MOedTyHgczbeqWDIoc0/fDV1e54gHdu/YonEeEYlW4MP6InEVbnc179AFXUCgFNtfj4EwCEwk+g22GxLIEgLdVXcyBgpSVbu/tAT8WM8t/2z32ziufSgl+Z4Xyl4CAHfZk62BIJeaBcf8IXsqXiUkzmiJ+L9brlkF565tfITThVuA7CEky2BIC2yy3UjobSz+JL6R0/ETnff/Z+sc9z+esYTAYzm9GxuRcDXzsmWQJCW6HUzW2S35/fFl9Q/zGC1iHbPzXZPfwI9j9Nz2QHAtvwWAkE+SJQs5teLaudnez+zQBV1FIBTOD2T3yLgcyTYsEDQl2jD7HbJ7eHiYqon4kdXLGkcl+n9bD3oj8AnGU0HABH8S1BWyJQuIZRL9AVJ17WfZnozm0DP4PQsHhBzT0G5EW2Y1Sq5PbZmz06ia4kfZHovvUAVdSyAo3jUDWLuKShTJNl9OzgEvjY07YCKJavT+rln6kF/CLtyh/bmOQR8JU3XJhCUith1xzdJLjePTHlE1/W025mZBHo6p2cgHOIFZY3k8dzNw46uJU5Ld33XXlJR6wBsh/3eQ58i4OvX1UkgcBryi+e/ZLo+rviSshkhzFMzvD628NjtqZfTifAE8HHte5SDDYGgaGTZZf9iEWOEaYldRq7phMgjAa8B4HEOdgSCoqEuz+/AZ7HoxL7Xeg9xFZUA2ALz+Jed/A0B34nFFzNw8fv9iwAUm1ynORgMTi7A9jyY8aLS2V9ilStiQaUg/er/XtUTcVt3NgglbWz/Y+oQ8LHktb496IGwX5wAwGV/qcyp5W3Q7/fP8fv9GwAsQ+Yfh0UAlvn9ftUSsgCA5HKtstsGM1itZ0Nweuq1vgI9nkNb4wCe52Cn3OEqUKvHfgZm0LZc67fM7/eLkKcAKPAkCLE9Y7eh67NS/+7rysfDOWENAr4ODnbKnZ1CCQaDtu45pxlOJ4/2rQoGg80p900CMAfAvJT6zfP7/QgGgxc7/cCcJNowq1X61Yuv64nE0XbaMXStlwb7CtRW4xZ/4mBjIJAUQJudRvx+/0z0FueqYDCY1o3TEusSAEv8fv9imMNdwBRpcKjPS6kkP8dBoL2SkPUMcRV1bwCj8i2wAP7KwcZAIDnEtTu0aOoQNaM4+xIMBhvQ+wD9Yr/fz33eXE5IkmR758IMNtxzW+Mhyb9T56A8es8PEfBt4WCnrLG+6Mkvu209qN/vn4OenroZQF7DVEukQevPWphD3yFLdNEJG6kk2R6r2dD1nWtBqQI9nEMbX+JgYyCQ2hPZ+T98Tsq/lwSDwUJ+DFKDuM0p4PODCiq7XrbbBtO1nVpMFehBHNr3Dw42BgKpK6m2CNTqpWdaf7YBKGibIBgMNqbU0e/3+/28HlI5QiXJ9k7GMIydLrCmQBVVQk9CXrvQAfzX7sYNEFIFatcQdxJ6eurGAnvPJKninllwKYMACtYIm72KDF3by9Lkzh50KvpJHlMC3hVhTXaSOsS1S6CpQgoWXIpJai8/pBeKog2z26ksNxVfUhYY3FL7N/sBPQI9iEPb/sPBxkCBxxy0lD8CqXXM1dFh0EIl6S27bTBmHAn07IPuBzORUZd1rQ7AHgDyyn/YD2J42wOPIW4pfwRED5oCpdLrAM4pSWF6PE7isR1Ui0WYroMxo5qB1Ml6/LA4sCwp0L1hJjLqy1aYZ0NjMIfAYwCMLLAq73J9iuXNzi950pPHWnyZmfJKJQhzHrg8j7mkXUIa8j0ooVL+nY2hM5KIqjQR62S6psMwvIzAx4jkZQRjdACgFMlBraFpU4CeHnSvDMWOsV6pJGCmtW+DOVkeDrO3rcxSvRCAz51+sGXEzr1Ja7V1GbJvYfit12K/398QDAZzicA/5Hs6u5C7d3wQo54YGPPs8iZjIFq0myTibdDiMWLoLsNgwxmVRjBK6nSgDoQAUvbBqaEn9gT6F2g6XEj/K9oFU7jdVrmjAOwO8yfhQwR8tp+nG0CkPr8NyE9Mi/1+/8xgMHiC040YqoR+daZOf/H8eiMW2ZskYjuoFg8zQwMzjGoG4mOSVM2AagAAlQoKf8A0bQwAyFDUeiQLK44aAOlCmGyFWCDaSR93uVShpj2Had0/D70d2Gf6/f5ncnXbE5QeGo9sNMId+zOgXgcAQgGpdIFIGAyP95fPTaIAxtvcljHYNUHvUKbv6KMRwORgMNiQzhk9GAy2BYPBJdbB7NT354jzms5BCezdagHADGMqBTCWQ3vE/LOH1B40COCM1CNf2bCOfKWKdMg7sDsFodJndttgjI2n2HURyA6EQHtIXYVtKMDDpwE92x61EP6xjkAk+SO7bTBDGycDsDecoMlGDjYGBMFgMIgigoIHg8E2v9+/HD1nPGeid68q4ACR5I/ttsF0Yw8K+/N/diDgC9vdmCFGY8q/h7RvrFNEbjy5kxAasdWIoY+iML2G7EQsEJUYqxdODo1rxTzUGYgkqXaWz4BaCmCEze34yubyhyqpDvDpBGprKBUBQKi8vfhSMsMMfRgPgYoviv2kcxyx67nbHlFgoEAI7bKzfGbo1TwE2m5z+YL0Yky9Vqz/LA/n/oEHJZ22ls9YNYXpS2sn7TaXP1Tp7ziZXQIVPagFIbTdzvKZYVTySHEvVnBT8Pv9q2GuvBaUtiGF1Li66USTutJbbJgS0YOmgRBi+3ebwvShtZN2uxsxQCm4V7OOpiV70EzREprRI6aZRa70pjpDNBZcymCDEFsDsDNmeHoOoAl4kRrJvdCeLXXvM61gLA+l1PcK8tu1Al/vPB5nbfEIAACMFV9GVqgQJ39Sh6N5i8bqCRelXMommNRgX4sK7EVTo9ILjyXO8BBozOlGlhmpopln9VD5sAgpUemDwWDGcJrWe6l+u8/kY8hK/5Ds5dsgBNobSm3/bvMQqKf4IgYP1oJO6hd9tRUBvl/65EsBegeVzkRqNPmZfr9/dS49aRpbhQa+HrwYhu3fbR6ruIJdaYA5j0zO7Z7x+/2NMOPX9gpnkiHjGGDGJ+o3GHUwGGz0+/0N6O1cr1rXGlPnlFlsrcoxzIqgxBAoajvs3Qu9GQHfL51uaLlhiWE1ClvNbcw35EkRGb1zTrg01HDf8Mdl8VCnbYfmCaERHkPcUobuHDQEg8HkPmi+PVNDIfGIrB7wBOTnaHCxEGdWbNePDDNKn5347G7EQMbKINZg9XCTkH5ldwmAtmKHmVaelckpWc/S9aglsTUUYIzZfYpIkwGoKDzWbS6Io1A5kCII2zNZp8xdhQiLgBn6CDvLJ5IUorDfdUv0oIJBCWPMVj92QmgnD4GKHlQwODGMYbaWT02B2noqHGbUeYFg0MEM3dZoJAS0g0cPOgaKKvZbBYOKqptWug1ds3V0SCTaTmF/zCAJfEJ7CgTcMOSKybB5m4VQuo2CT8ygPTnYEAi4wZgxxW4bhNAvKYAvObTH9sYIBDxhhv6t4kvJDqG0mVcPantjBAKeGFpif9uNyK7PKcyUgXazDwcbAgE3mK4VE64mJ6hhNFEEfFEAX9tsaz+7GyMQ8MTQErYKlBDaFr75tHByFaqUqdR0AF8AeAfAmwA+BVAHRbU79pFAwIXKm1fVIdwZk6LdG2ks/BVJxDphlDb6CXW5NgM950E/AXBsAeW0ANgGIALzYPZoAPUwc472zTt6GIA13J6iQGATLBqebURCvR1wmG5Qhg5CaQiSZDDJ7TFkdy1cHnchqbKo7F6vo0eg6/q5PwxzMakD5t5PLcytk1HWKxcOhRCoYBBgJOLTd7lIJGoQ1AKohW4AehSIRwGmxymhHQQkAlkmTHJVGi5PLWRX1j1UIknrgN49KAAYMIXYCkADUA0zPWENgKlFtuswpx+sQFAKjHjs4JxvJpLbSHZimma+omEQZnQTSjsJlRJMcslMdg1jrooaULO7pVR6D+gR6KcwPYrGwOwZ7XAsmF58EQKB82jRcHHbhoSAEamaAdUwDMCIAYkYEO40qOxuJ+6KLn3MXq8DqYlkFXUz7Hds3xcBX3/D6UGHFaRrGXoCQLfBDMK1JOWet7FrBPjlVtr7/spfBGBOMBg8NM17fpgHs5PRA5st28ut9+cgc7S/M5JnR9OETGkYioe6K69/6sjwlo2v2WmDyq4O46HrRgC9fQnf4tC+QhaiBjSWON+GOW/3BYNBAvNQ9iLrS5/KqmAwSKx7JsOMwpdLHKF5SBMS07K9GmaEhGS5SwAs8/v9fSM3HJq8J+WVFOczlo1DrTIOhRkydJnTz5c3Rix8ut02pIrKnbsqqQJ9m0P7judgo9yYB1OcZyTDVlpf/FXIErjaCs/ZiH4yaFtxdWuTPWIfZlq2l6SUu9wqN9dQnzOtexuSEQCt/y7PtYzBhB6L2j5Vo+6Kd5L/Tj0GxqMHnQlFdSHgszsOUjnhh/md7nWsr7+hqxX1byayR44HTJFkCr/ZnHLPznLyDDrmh9kD97WxCkPsMH7VTSvd4S0bD7TbDnW5/pv8d6pA3wbAgEJ2bXJmOICjAbxsdyPLjFzP3M7x+/2pO96NOUTVmwMg7T3BYDDo9/vPQM+Qdrl1OZ2g3+6TKiYZbrM2Q9nNyC1w9qDBiEcDTNcrbDbDqCS/lPyjR6ABXxsU9X0AB9lcgVMx9AS6E2s+lzo09KX0rr1i0FpR4J/JJFJLdG1WtL60JIfT1r2T0CPWM/r06oeKxEjZ0aMR24f0UoX369AtZ32T/LvvZuk/ObTzVA42yo2dvVAwGDzDWmjJpfdZArNXzRTceg5yzJcSDAaXWyE+JyNzyM10pO39/X7/pBwXsAYNWqjzGLttSBVVb6T+3Vegazm0cxIU9dDiixkwBGHudhQzX9tFoClz1IzpH/x+/2K/37+hV2XMXjOI3JP6BgHUpskfk0wRMSSobHjie4aWsD1CpeSpWJ36d1+BvgLTm8hufszBRllg7RU2w8y/MgnYuf2RzLKdbX66yLon3RB2Dsw5arZI8Y0AevV0ltBmIsdEvJbtVQAWJ/OZWv+dhyw/DoMNLdJ9EQczjErys6kXegs04OsAn+2WH0NRh1Ju0mTKhQ3WIpAKc+jYdzV1jt/vZ8mXdS3TIlG/ArHEdShMTSXLXAbTUaHvEPvtVNvWa5FVzhkwh9JvW2WsRo5OFIOBqptWSolQZ75pIvNGqqjclDr/BNKt2CrqTQB4JDuaiYCPx5x30GH1gsuCwaAICs4B76LHzol8s/kxu+14fLutiN110U9Tr6XrxV7g1O4h8etrB8FgcJUQJz+0UBeXubbk8T7V91r6PU8+frkJAHsg4NvOo/ECQSFU3fDUhNCWLz4HM2zN0kdlV7vx0HW7LCRmmge+yKHtLgDncbAjEBSMFglfZ7c4AUCuHPafdNczCfQvnNp/CRRV5A8VlCVVN62UEl3tXPKjShXeP6W7nkmg/4QZzsRuJgL4AY8HIBDkix4NzzcSMVvzrwAAkV0hWY8/ke699AIN+HQAKzk9h4Wc7AgEeZHoal/Aw46ratiarjsvSnuAJNte5B85PYcjoKhHcbIlEOSEt+GxOXo0PJGHLclbtSLTe9kE+hr4pIUAgJs42REIciLR1c7lO0ldnh0VOzY/n/H9jJ8M+BiARzg9j9lQ1GmcbAkEWfE2PH6qFuo6gIctV/Wwv7U9eH1G99r+3O1WwDwjyoOfc7IjEGQl0anexskUkyoqs9rKLtCA70sAL4EPs6GoQy5mkaC88C76w1wt3L03D1tyVc0H4VvP/jDbPbk4rOd03rBE3DnEnOgFZUTVTSulePuOW3nZc1XVPNDfPbmI4UUAWzjV+WAA53CyJRD0Qo+Eb9BjES7JpqnL1SHr2sP93tdvSQGfBuBeHpW2uBKKWsXRnkAA1+/fqYm1t1zDzd4w3zOZ9j5TyXU4uQxAyOY6h2BmRDsYwM022xIIekE+e3MZDXeEwDjEK6BSXHZX5PQdz02gAV8b7N1yScYBPcT678+gqPZnMBYIAFRdvfz4xBefnalHwmNoR2uIRkM77LTnrhnxUujWs7fmcm8+CzK/Rem3XJpgJg8+BEDqsFYG8AAU1c4QoAIBqm5aKcVavlrBLC0YhFYZsXCd1Nm6gySipR81EhhyVU3OI8TcBRrwNSNzDo98aQHwMcyMabtnuOcoAJeV/AEJBCnobdvu1Lq7+iwMEegMdSzUUSl179gKXddKZc9VPeL18C1nvZfr/fluafwSxfWiMZhR4nwA9svh/iVQ1CmlejgCQSpV1zx4RGzb5sydAJGIrrMxpKvVkCKd34AVPYBkrpoRi/L5QP5DSEX9I4AzC6jc+wAmwIwunw+vAvguAj4e0QYFQ4Saax50RbZ/vV7r7hif62cksE5WUWUYnsoRhdh01Yx4LXHv5XkdDCnEKSDfXnQTgI0ADkT+4gTMVBHXFfJABIJMJCLdy/IRJwDoIMOMaGiE1NnSQrR4NE+TefeeQKF5WBT1KfQf27YdwBcwhVksOoDpCPj+W3RJgiFP1c+W/TC8ad0zrLAOCgBAwAwqy9/o3prRoHK/5biGjfhXYunlM/K1U2gFbwAQz/CeDnOe6UVpxAkAEoCVUFQRyU5QFNXXrtgjsqV5RTHiBAAGQnVNH0O71BiNdmWPPkKo7qqp/VkhdgqrZMC3Eem9iz6GGZTZD8BTzANIwzgAj4qtF0Gh1F50C421bvmbEY8PK1WZBqjXiMVGSR3b22k80pXuHvdw31/Cv5n7biHlF/Mr8muYYgTMvcz1MFdmR5Wq8Wk4BeJYmqBAInLF44n2HbY4wOigI4xIV43U1foNMRI7R5dElsOuyuorCi23cIEGfO0wF4zegbmXyWs75H+hqCLQmCAvvAvuvza6bfNce61Q6AZGo6uNyuH2bTAM5hled3/fdA75lVgcvwPgtrfRaXkcimp7pmPB4KDq6gdnRbc038LLHgOVtYRWL7tczca4fa8vpqziBGqedLmUV8NTqALwVyjqOAdsCwYQ1dc8tF9kywaF6bpcfGn54R4x8vr4VUf1e2IlG8Ufjg74XoUZGoU3YwG8BEUtZG9VMASouebBMZGtm9YYsVgNb9vuutH/Ct95YdGusaWKXrAIgK0nADLwLQDPQ1ErHbAtKGOqrn9yWKTl69f0SGg33rapyxVx1+52dknKKkmNAr5WOBeA+hgAChTVibmwoAypWfhQZXxr86taV8cEJ+x7dht3e/ftF3xVirJKu6eoqM8C+L4DzwQw0yaebs2LBUOUmoUPVUZ3bH090a5yCZvZF9dw38eJR28q2VZOqQN0XYKevVHenALgCdGTDl1qrnnQFVO/ecUpcRLZFfXsNq6kW4ClFWjAtw1AwZuyJeBMmMPdUnsxCcqcmoUPVUbV7W/H21oPKb60wqgYM/7n3bedt76UZdrjNqeoTwP4EY+HkoGXAZyMgC/sYB0EnKhe9GhddOum17Wujr2cqoOrdvRbiRUNh5e6XLti0F4CoNneR5KVYwGsgaLa6XYoKAOqFz40Prpl43tOipN6Kjo8I0efZkvZttQ44OuA2YMWtUlbJEcAeB2KOtXBOghspGrhQ9+JfN38jtbduYdTdSCUGN6xk37Sfdv5tsSOti+Ke8D3FoAG28rPjUkAXoOi1jtcD0GJqfzZAz8Of9H0Tz0acfQIoqd+/LLQXRc9Z1f5dqdZ+C0A2yqfI09bi1eCQUS8detipiUqnKyDe8TI973xkK2LovYK1ExheA6AT221k5m/A7jKIdsCG6kYtfts6vbYHUw9I5K3coenbvTsbKkDS4H9iYoCvi4Ap8EMgcKTdQDORMCnc7Yr4ED3XfPWeXef9GNCCXfHFCLLsYqxE0/tuuPCgo+R5QqfTGIBXxOAuQB4ReZTYW6zdHCyJ3CA0F3zXqjYffKNnM2yyrETF4TuuPA1Hsb4pfoL+P4G4FoOlhIAfoCAbwO3tgkcI7L08iUV9Xs+wctexdgJy0P3XMYtJSffXJwB310Altps5VIEfP/m2i6Bo3i1yE9ctaPfstuOZ+SY1dHfXXkJz7Y5kSz3agB/tqnsOxHw9ZtzUTC4aHvwesMzcvTxcvWwzXbZcI+oe9c9rPYk3m3jL1Bz0eYcAKUew78I81yqYAjSfdv5XRVjJh5P3e7OUpctVw/b5BozcUYu+TxLjTPp5k0f2VMAfFSiEj8CMFes2A5tum87b713j8lnEkkqmZAkb9X2ijETjgrdclbJhZ8LzggUAAI+FcBxMLdDimE7zBXbriLLEQwCQnfOe8m7+6RrS3EMRKrwtnl3n3SkXW58ueCcQAEg4GsBcAKAQldc4zBXbL9wtB2CsiJ8z2X3VNTvWVTCaanC2+YdO2l69+0XOHnow6bjZvmiqONhZjHL1+n5XAR8jztdfUF54r5wyavxHdvyyiYGmKdTKneffFT3nRd+7HQbnO1Bk5g94JHIrye9VYhTkA3PiLpZcvWwTfl8xhrWzi4HcQLlIlAACPi+AjAduS0cPQszgZNAkJGuOy4MV9SPP556KnLyKJO8Vdu9YydND9150RtO1z1J+QgUAAK+rTAXjt7Jcte7AM62HPEFgqx0335Bs3fspB8QWYpnu0+urvnaO3bCtHLpOZOUl0CB5MLRcQD+mebdbQBORcDn2CkGwcAjdNdFL3t3n3w1MiSelmuGN3tH7+nvvuPCjU7XtS/lJ1AgGZHhRACpc8wIgO9bQ2GBIC/Cd1/6O++Y8cv6XnfXjnrTu9seB/A4mVII5SlQAAj44gB+AuA31pXzEfC96XS1BAOXyP1XXeoeWf9y8m/P6N1XVUk4suuOC0VwuaIQmcwEJaJm4UOVrp/88lPvFff+2um65ML/B0uPe20x0+ZGAAAAAElFTkSuQmCC" width="232" height="212"/>
+</svg>
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 0000000..2debb1b
--- /dev/null
+++ b/skyquake/framework/style/img/viewport-vnf-temp.png
Binary files 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 0000000..3fdea44
--- /dev/null
+++ b/skyquake/framework/style/img/vim-icon-corners.png
Binary files 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 0000000..76c2cd3
--- /dev/null
+++ b/skyquake/framework/style/img/vim-icon-diamond.png
Binary files 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 0000000..4440a9a
--- /dev/null
+++ b/skyquake/framework/style/img/vim-icon-halfcircle-bottom.png
Binary files 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 0000000..25d8e1d
--- /dev/null
+++ b/skyquake/framework/style/img/vim-icon-halfcircle-top.png
Binary files 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 0000000..88c7f83
--- /dev/null
+++ b/skyquake/framework/style/img/vim-icon-plus.png
Binary files 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 0000000..7d6ca71
--- /dev/null
+++ b/skyquake/framework/style/img/vim-icon-soliddiamond.png
Binary files 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 0000000..03466e4
--- /dev/null
+++ b/skyquake/framework/style/img/vim-icon-solidsquare.png
Binary files 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 0000000..a756b55
--- /dev/null
+++ b/skyquake/framework/style/img/vim-icon-solidstar.png
Binary files 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 0000000..fa53ce4
--- /dev/null
+++ b/skyquake/framework/style/img/vim-icon-triangle.png
Binary files differ
diff --git a/skyquake/framework/style/layout.scss b/skyquake/framework/style/layout.scss
new file mode 100644
index 0000000..358564c
--- /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 0000000..a1d6cbd
--- /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 0000000..ba39032
--- /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 0000000..b266c37
--- /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 0000000..1cca8f2
--- /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 0000000..d08fe5f
--- /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 0000000..2f8c0a8
--- /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 (
+ <div className="JSONViewer">
+ <style dangerouslySetInnerHTML={{__html: cssString}}></style>
+ <label className="descriptor">
+ <pre className="language-js">
+ <code dangerouslySetInnerHTML={{__html: result}} />
+ </pre>
+ </label>
+ </div>
+ );
+ }
+});
+
+export default JSONViewer;
diff --git a/skyquake/framework/widgets/JSONViewer/JSONViewer.scss b/skyquake/framework/widgets/JSONViewer/JSONViewer.scss
new file mode 100644
index 0000000..08fcefd
--- /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 0000000..1411f0f
--- /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 0000000..c972e14
--- /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 0000000..41730eb
--- /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: '<use xlink:href="#' + this.props.icon[i] + '" />'} //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 0000000..ae93128
--- /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 = <svg className="svg-24px SqButton-icon">
+ <use xlinkHref={icons[icon]}></use></svg>;
+ }
+ 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 (
+ <div style={{display: 'flex'}}>
+ <div className={Class} tabIndex="0">
+ {svgHTML}
+ <div className="SqButton-content">{label}</div>
+ </div>
+ </div>
+ )
+ }
+}
+
+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 0000000..1fe49bc
--- /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-<div class="tooltip"></div>')
+// 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("<div class='tooltip' style='position:relative;right:20px'>" + val + "</div>");
+// } else {
+// $($element).find('.noUi-handle').append("<div class='tooltip' style='position:relative;left:-20px'>" + val + "</div>");
+// }
+// $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: '<div></div>',
+// controller : controller,
+// replace: true,
+// scope: {
+// min : '@',
+// max : '@',
+// width: '@',
+// height: '@',
+// step : '@',
+// orientation : '@',
+// tooltipInvert: '@',
+// percent: '@',
+// kvalue: '@?',
+// onSet:'&?',
+// direction: '@?',
+// value:'=?'
+// }
+// };
+// })
+// .directive('rwGauge', function() {
+// return {
+// restrict: 'AE',
+// template: '<canvas class="rwgauge" style="width:100%;height:100%;max-width:{{width}}px;max-height:240px;"></canvas>',
+// 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 0000000..e44c4cc
--- /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 0000000..4904dd2
--- /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 (
+ <header className={cardClassHeader}>
+ <h3>
+ {this.props.title}
+ </h3>
+ </header>
+ )
+ }
+});
+ 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 = <CardHeader className={this.props.className} title={this.props.title}/>;
+ };
+ if (this.props.closeCard) {
+ closeCard = this.props.closeCard;
+ }
+ return (
+ <div className={cardClassWrapper} style={{display: this.props.isHidden ? 'none':'inherit'}}>
+ {closeCard}
+ <i className="corner-accent top left"></i>
+ <i className="corner-accent top right"></i>
+ {hasHeader}
+ <div className={cardClassContent}>
+ <div className={cardClassContentBody}>
+ {this.props.children}
+ </div>
+ </div>
+ <i className="corner-accent bottom left"></i>
+ <i className="corner-accent bottom right"></i>
+ </div>
+ )
+ }
+})
+
+
+// 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 0000000..9a33dc5
--- /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 0000000..0ee7af5
--- /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(<div key={i} className={"component"}>{this.props.component_list[i]}</div>);
+ }
+ }
+ return (
+ <div>
+ <Slider {...settings}>
+ {list}
+ </Slider>
+ </div>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 0000000..4a88435
--- /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 0000000..41a8b13
--- /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 <option key={i} value={JSON.stringify(op.value)}>{op.label}</option>
+ });
+ if (this.props.initial) {
+ options.unshift(<option key='blank' value={JSON.stringify(this.props.defaultValue)}></option>);
+ }
+ html = (
+ <label>
+ {this.props.label}
+ <select className={this.props.className} onChange={this.handleOnChange} defaultValue={JSON.stringify(defaultValue)} >
+ {
+ options
+ }
+ </select>
+ </label>
+ );
+ 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 0000000..03dfa9c
--- /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 = <span className="required">*</span>
+ }
+ if (defaultValue) {
+ inputProperties.defaultValue = defaultValue;
+ }
+ if (props.pattern) {
+ inputProperties.pattern = props.pattern;
+ }
+ if (value == undefined) {
+ value = defaultValue;
+ }
+ switch(props.type) {
+ case 'textarea':
+ inputType = <textarea {...inputProperties} value={value}/>
+
+ break;
+ default:
+ inputType = <input type={props.type} {...inputProperties} placeholder={props.placeholder}/>;
+ }
+ let html = (
+ <label className={"sqTextInput " + props.className} style={props.style}>
+ <span> { label } {isRequired}</span>
+ {
+ !props.readonly ? inputType : <div className="readonly">{value}</div>
+ }
+
+ </label>
+ );
+ return html;
+ }
+}
+
+TextInput.defaultProps = {
+ onChange: function(e) {
+ console.log(e.target.value);
+ },
+ label: '',
+ defaultValue: undefined,
+ type: 'text',
+ readonly: false,
+ style:{}
+
+}
+
diff --git a/skyquake/framework/widgets/gauge/gauge.js b/skyquake/framework/widgets/gauge/gauge.js
new file mode 100644
index 0000000..dc1a083
--- /dev/null
+++ b/skyquake/framework/widgets/gauge/gauge.js
@@ -0,0 +1,275 @@
+
+/*
+ *
+ * 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 ReactDOM = require('react-dom');
+var MIXINS = require("../mixins/ButtonEventListener.js");
+var Gauge = require("../../js/gauge-modified.js");
+var GUID = require("utils/guid");
+import _ from 'underscore'
+
+
+
+
+/**
+ * Gauge Component
+ * It's props values and a brief description below
+ *
+ * min: minimum value expected
+ * max: maximum value expected
+ * width: width of gauge in px
+ * height: height of gauge in px
+ * value: the number displayed on the gauge
+ * resize: should the gauge resize with container
+ * unit: the units displayed on the gauge
+ * valueFormat: An object with an 'int' and 'dec' property. The 'int' is the min number of integer digits displayed
+ * and the 'dec' object is the min number of fractional digits displayed.
+ *
+ *
+ **/
+module.exports = React.createClass({
+ displayName: 'Gauge',
+ mixins:MIXINS,
+ propTypes: {
+ min: React.PropTypes.number,
+ max: React.PropTypes.number,
+ width: React.PropTypes.number,
+ height: React.PropTypes.string,
+ value: React.PropTypes.number,
+ resize: React.PropTypes.bool,
+ isAggregate: React.PropTypes.bool,
+ units: React.PropTypes.string,
+ valueFormat: React.PropTypes.shape({
+ 'int': React.PropTypes.number,
+ 'dec': React.PropTypes.number
+ })
+ },
+ clone: function(obj) {
+ if (null == obj || "object" != typeof obj) return obj;
+ var copy = obj.constructor();
+ for (var attr in obj) {
+ if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr];
+ }
+ return copy;
+ },
+
+ /**
+ * Defines default state.
+ *
+ * min: minimum value expected
+ * max: maximum value expected
+ * nSteps: fixed number for now. The number of ticks in the gauge.
+ * width: width of gauge in px
+ * height: height of gauge in px
+ * value: the number displayed on the gauge
+ * resize: should the gauge resize with container
+ * unit: the units displayed on the gauge
+ * valueFormat: An object with an 'int' and 'dec' property. The 'int' is the min number of integer digits displayed
+ * and the 'dec' object is the min number of fractional digits displayed.
+ */
+ getInitialState: function() {
+ var valueFormatState = null
+ this.gauge = null;
+ this.gaugeID = GUID();
+ if (!this.props.valueFormat) {
+ if ((this.props.max && this.props.max > 1000) || this.props.value) {
+ valueFormatState = {
+ "int": 1,
+ "dec": 0
+ };
+ } else {
+ valueFormatState = {
+ "int": 1,
+ "dec": 2
+ };
+ }
+ } else {
+ valueFormatState = this.props.valueFormat;
+ }
+ return {
+ //sizeOfButton: this.props.size || '', //There is no Medium value in CSS, default size is the absence of a value
+ min: this.props.min || 0,
+ max: this.props.max || 0,
+ nSteps: 14,
+ height: this.props.height || 200,
+ width: this.props.width || 200,
+ color: this.props.color || 'hsla(212, 57%, 50%, 1)',
+ value: this.props.value || 0,
+ valueFormat: valueFormatState,
+ isAggregate: this.props.isAggregate || false,
+ units: this.props.units || '',
+ resize:this.props.resize || false
+
+ }
+ },
+
+
+ /**
+ * Called when props are changed. Syncs props with state.
+ */
+ componentWillReceiveProps: function(nextProps) {
+ this.setState({
+ max:nextProps.max || this.state.max,
+ value:nextProps.value || 0,
+ valueFormat:nextProps.valueFormat || this.state.valueFormat
+ });
+ },
+
+ /**
+ * Calls the render on the gauge object once the component first mounts
+ */
+ componentDidMount: function() {
+ this.canvasRender(this.state);
+ },
+
+ /**
+ * If any of the state variables have changed, the component should update.
+ * Note, this is where the render step occures for the gauge object.
+ */
+ shouldComponentUpdate: function(nextProps, nextState) {
+ var currentStateString = String(this.state.max) + String(this.state.valueFormat.int) + String(this.state.valueFormat.dec) + String(this.state.value);
+ var nextStateString = String(nextState.max) + String(nextState.valueFormat.int) + String(nextState.valueFormat.dec) + String(nextState.value);
+ if (currentStateString == nextStateString) {
+ return false;
+ }
+ this.state.valueFormat = this.determineValueFormat(nextState.value);
+ this.canvasRender(nextState);
+ return true;
+ },
+
+ /**
+ * Default value format based on units.
+ */
+ determineValueFormat: function(value) {
+ if (value > 999 || this.state.units == "%") {
+ return {
+ "int": 1,
+ "dec": 0
+ }
+ }
+
+ return {
+ "int": 1,
+ "dec": 2
+ }
+ },
+
+
+ /**
+ * Render step for the gauge object. Sets some defaults, passes some of the component's state down.
+ */
+ canvasRender: function(state) {
+ if (state.max == state.min) {
+ state.max = 14;
+ }
+ var range = state.max - state.min;
+ var step = Math.round(range / state.nSteps);
+ var majorTicks = [];
+ for (var i = 0; i <= state.nSteps; i++) {
+ majorTicks.push(state.min + (i * step));
+ }
+ var redLine = state.min + (range * 0.9);
+ var config = {
+ isAggregate: state.isAggregate,
+ renderTo: ReactDOM.findDOMNode(document.getElementById(this.gaugeID)),
+ width: state.width,
+ height: state.height,
+ glow: false,
+ units: state.units,
+ title: false,
+ minValue: state.min,
+ maxValue: state.max,
+ majorTicks: majorTicks,
+ valueFormat: this.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 = state.max;
+ }, 500);
+ if (state.resize) $(window).resize(updateSize)
+
+ if (this.gauge) {
+ this.gauge.setValue(Math.ceil(state.value* 100) / 100)
+ this.gauge.updateConfig(config);
+ } else {
+ this.gauge = new Gauge(config);
+ this.gauge.setValue(Math.ceil(state.value* 100) / 100)
+ this.gauge.draw();
+ }
+ },
+
+ /**
+ * Renders the Gauge Component
+ * Returns the canvas element the gauge will be housed in.
+ * @returns {*}
+ */
+ render: function() {
+ var gaugeDOM = React.createElement("div", null,
+ React.createElement("canvas",
+ {className: "rwgauge", style:
+ {width:'100%','maxWidth':this.state.width + 'px','maxHeight':this.state.width},
+ id:this.gaugeID
+ }
+ )
+ )
+
+
+
+ return gaugeDOM;
+ }
+});
diff --git a/skyquake/framework/widgets/header/header.jsx b/skyquake/framework/widgets/header/header.jsx
new file mode 100644
index 0000000..21f4c10
--- /dev/null
+++ b/skyquake/framework/widgets/header/header.jsx
@@ -0,0 +1,152 @@
+/*
+ *
+ * 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 './header.scss';
+import Crouton from 'react-crouton';
+import HeaderStore from './headerStore.js';
+import ScreenLoader from '../screen-loader/screenLoader.jsx';
+export default class AppHeader extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {};
+ this.state.validateErrorEvent = 0;
+ this.state.validateErrorMsg = '';
+ }
+ componentDidMount() {
+ HeaderStore.listen(this.storeListener);
+ }
+ componentWillUnmount(){
+ HeaderStore.unlisten(this.storeListener);
+ }
+ closeError() {
+ LaunchpadFleetActions.validateReset()
+ }
+ storeListener = (state) => {
+ this.setState(state);
+ }
+ render() {
+ let html;
+ let navItems = this.props.nav.map(function(nav, i) {
+ if (nav.href || nav.onClick) {
+ return <li key={i}><a {...nav}>{nav.name}</a></li>
+ } else {
+ return <li key={i}><span className="current"> {nav.name} </span></li>
+ }
+ });
+ let errorMessage = <Crouton
+ id={Date.now()}
+ message={this.state.validateErrorMsg}
+ type={"error"}
+ hidden={!(this.state.validateErrorEvent && this.state.validateErrorMsg)}
+ onDismiss={HeaderStore.validateReset}
+ />
+
+ // html = (
+ // <header className="header-app-component">
+ // {errorMessage}
+ // <ScreenLoader show={this.props.isLoading}/>
+ // <div className="header-app-main">
+ // <h1>{this.props.title}</h1>
+ // <CommonLinks></CommonLinks>
+ // </div>
+ // <div className="header-app-nav">
+ // <ul>
+ // {navItems}
+ // </ul>
+ // </div>
+ // </header>
+ // );
+
+ html = (
+ <header className="header-app-component">
+ {errorMessage}
+ <ScreenLoader show={this.props.isLoading}/>
+ <div className="header-app-nav">
+ <ul>
+ {navItems}
+ </ul>
+ </div>
+ </header>
+ );
+
+ return html;
+ }
+}
+export class CommonLinks extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+ openAbout() {
+ generateRefPage();
+ navigateTo('about')
+ }
+ openDebug() {
+ generateRefPage();
+ navigateTo('debug');
+ }
+ render() {
+ let links = [];
+ setContext();
+ links.push(<li key={'about'} onClick={this.openAbout}><a>About</a></li>);
+ links.push(<li key={'debug'} onClick={this.openDebug}><a>Debug</a></li>);
+ let html;
+ html = (
+ <nav>
+ <ul>
+ {links}
+ </ul>
+ </nav>
+ );
+ return html;
+ }
+}
+AppHeader.defaultProps = {
+ nav: [],
+ isLoading: false
+}
+function generateRefPage() {
+ let applicationContext = window.sessionStorage.getItem('applicationContext') || 'launchpad';
+ let hash = window.location.hash.split('/');
+ let pageTitle = 'Dashboard';
+ if (applicationContext === 'launchpad') {
+ hash = '#/launchpad/' + hash[2];
+ } else {
+ hash = "#/"
+ }
+ let refPage = {
+ 'hash': hash,
+ 'title': pageTitle
+ };
+ window.sessionStorage.setItem('refPage', JSON.stringify(refPage));
+ return refPage;
+}
+function navigateTo(loc) {
+ window.location.hash = '#/' + loc;
+}
+function setContext() {
+ let applicationContext;
+ let hashOne = window.location.hash.split('/')[1];
+ if(hashOne == "launchpad") {
+ applicationContext = "launchpad";
+ } else {
+ applicationContext = "missioncontrol";
+ }
+ if(hashOne != 'about' && hashOne != 'debug'){
+ window.sessionStorage.setItem('applicationContext', applicationContext);}
+}
diff --git a/skyquake/framework/widgets/header/header.scss b/skyquake/framework/widgets/header/header.scss
new file mode 100644
index 0000000..9238c22
--- /dev/null
+++ b/skyquake/framework/widgets/header/header.scss
@@ -0,0 +1,87 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+header.header-app-component {
+ padding: 20px 0px;
+ display:flex;
+ flex-direction:column;
+ .header-app-main {
+ display:flex;
+ flex-direction:row;
+ justify-content:space-between;
+ align-items:center;
+ }
+ h1 {
+ background: url('../../style/img/header-logo.png') no-repeat;
+ background-size:contain;
+ height: 51px;
+ line-height: 51px;
+ margin-left: 20px;
+ padding-left: 100px;
+ left: 0;
+ text-transform: uppercase;
+ font-size: 1.625rem;
+ font-weight: 400;
+ position:relative;
+ flex: 1 0 auto;
+
+ }
+ ul {
+ display:flex;
+ }
+ li {
+ display:flex;
+ flex:1 1 auto;
+ border-right:1px solid #e5e5e5;
+ padding: 0 1rem;
+ &:last-child {
+ border:none;
+ }
+ a {
+ cursor:pointer;
+ // padding: 0.125rem;
+ // border-bottom:1px solid black;
+ text-decoration:underline;
+ }
+ }
+ .header-app-nav {
+ display:flex;
+ margin-left: 0.25rem;
+ a,span {
+ text-transform:uppercase;
+ }
+ a {
+ cursor: pointer;
+ margin-left: 0.5rem;
+ font-weight: normal;
+ color: black;
+ }
+
+ .spacer {
+
+ }
+ .current {
+ font-weight: bold;
+ }
+ }
+ nav {
+ display:flex;
+ flex:0 1 auto;
+ align-items:center;
+ }
+}
diff --git a/skyquake/framework/widgets/header/headerActions.js b/skyquake/framework/widgets/header/headerActions.js
new file mode 100644
index 0000000..e71e79f
--- /dev/null
+++ b/skyquake/framework/widgets/header/headerActions.js
@@ -0,0 +1,20 @@
+/*
+ *
+ * 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 Alt from '../skyquake_container/skyquakeAltInstance';
+export default Alt.generateActions('showError', 'resetError');
diff --git a/skyquake/framework/widgets/header/headerStore.js b/skyquake/framework/widgets/header/headerStore.js
new file mode 100644
index 0000000..4ee86ff
--- /dev/null
+++ b/skyquake/framework/widgets/header/headerStore.js
@@ -0,0 +1,46 @@
+/*
+ *
+ * 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 HeaderActions from './headerActions.js';
+import Alt from '../skyquake_container/skyquakeAltInstance';
+
+class HeaderStoreConstructor {
+ constructor() {
+ var self = this;
+ this.validateErrorEvent = 0;
+ this.validateErrorMsg = '';
+ this.bindActions(HeaderActions);
+ this.exportPublicMethods({
+ validateReset: self.validateReset
+ })
+ }
+ showError = (msg) => {
+ console.log('message received');
+ this.setState({
+ validateErrorEvent: true,
+ validateErrorMsg: msg
+ });
+ }
+ validateReset = () => {
+ this.setState({
+ validateErrorEvent: false
+ });
+ }
+}
+
+export default Alt.createStore(HeaderStoreConstructor)
diff --git a/skyquake/framework/widgets/input-range-slider/input-range-slider.jsx b/skyquake/framework/widgets/input-range-slider/input-range-slider.jsx
new file mode 100644
index 0000000..d9e0544
--- /dev/null
+++ b/skyquake/framework/widgets/input-range-slider/input-range-slider.jsx
@@ -0,0 +1,72 @@
+
+/*
+ *
+ * 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 Slider from 'react-rangeslider';
+// import Slider from './react-rangeslider.jsx';
+import './input-range-slider.scss';
+
+
+class RWslider extends React.Component {
+ constructor(props, context) {
+ super(props, context);
+ this.state = {...props}
+ }
+
+ handleChange = (value) => {
+ this.props.handleInputUpdate(value);
+ this.setState({
+ value: value
+ });
+ };
+
+ render() {
+ let state = this.state;
+ var className = "input-range-slider_" + this.props.orientation;
+ return (
+ <div className={className}>
+ <div className={className + '-info'}>
+ {state["min-value"]}
+ </div>
+ <Slider
+ displayValue={true}
+ value={state.value}
+ max={state["max-value"]}
+ min={state["min-value"]}
+ step={state["step-value"]}
+ onChange={this.handleChange}
+ className={className + '-info'} />
+ <div className={className + '-info'}>
+ {state["max-value"]}
+ </div>
+ </div>
+ );
+ }
+}
+
+RWslider.defaultProps = {
+ value: 10,
+ "min-value": 0,
+ "max-value":100,
+ "step-value":1,
+ "units": "%",
+ orientation: "horizontal"
+}
+
+export default RWslider
+
diff --git a/skyquake/framework/widgets/input-range-slider/input-range-slider.scss b/skyquake/framework/widgets/input-range-slider/input-range-slider.scss
new file mode 100644
index 0000000..803bd8b
--- /dev/null
+++ b/skyquake/framework/widgets/input-range-slider/input-range-slider.scss
@@ -0,0 +1,104 @@
+
+/*
+ *
+ * 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';
+
+ .input-range-slider_horizontal{
+ display:flex;
+ align-items:baseline;
+ &-info {
+ margin:0 0.5rem;
+ }
+ }
+
+
+.rangeslider {
+ margin: 20px 0;
+ position: relative;
+ background: #e6e6e6;
+ display:flex;
+ flex:1;
+ .rangeslider__fill, .rangeslider__handle {
+ position: absolute;
+ }
+ &, .rangeslider__fill {
+ display: block;
+ box-shadow: inset 0px 1px 3px rgba(0, 0, 0, 0.3);
+ }
+ .rangeslider__handle {
+ background: #fff;
+ border: 1px solid #ccc;
+ cursor: pointer;
+ display: inline-block;
+ position: absolute;
+ &:active {
+ background: #999;
+ }
+ }
+}
+
+/**
+ * Rangeslider - Horizontal slider
+ */
+.rangeslider-horizontal {
+ height: 1rem;
+ margin-bottom: 2.5rem;
+ .rangeslider__fill {
+ height: 100%;
+ background: $light-blue;
+ top: 0;
+ }
+ .rangeslider__handle {
+ width: 0.5rem;
+ height: 2rem;
+ top: -0.5rem;
+ left:-10px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ >div{
+ width: 5rem;
+ margin-top: 2rem;
+ text-align: center;
+ }
+ }
+}
+
+/**
+ * Rangeslider - Vertical slider
+ */
+.rangeslider-vertical {
+ margin: 20px auto;
+ height: 150px;
+ max-width: 10px;
+ background: none;
+ .rangeslider__fill {
+ width: 100%;
+ background: $light-blue;
+ box-shadow: none;
+ bottom: 0;
+ }
+ .rangeslider__handle {
+ width: 30px;
+ height: 10px;
+ left: -10px;
+ &:active {
+ box-shadow: none;
+ }
+ }
+}
diff --git a/skyquake/framework/widgets/input-range-slider/react-rangeslider.jsx b/skyquake/framework/widgets/input-range-slider/react-rangeslider.jsx
new file mode 100644
index 0000000..feff1eb
--- /dev/null
+++ b/skyquake/framework/widgets/input-range-slider/react-rangeslider.jsx
@@ -0,0 +1,251 @@
+
+/*
+ *
+ * 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.
+ *
+ */
+/**
+ * https://github.com/larsmaultsby/react-rangeslider
+ *
+ *
+ * Forked from: https://github.com/whoisandie/react-rangeslider
+ *
+ *
+ The MIT License (MIT)
+
+ Copyright (c) 2015 Bhargav Anand
+
+ 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.
+ *
+ */
+
+import React, { PropTypes, Component, findDOMNode } from 'react';
+import joinClasses from 'react/lib/joinClasses';
+
+function capitalize(str) {
+ return str.charAt(0).toUpperCase() + str.substr(1);
+}
+
+function maxmin(pos, min, max) {
+ if (pos < min) { return min; }
+ if (pos > max) { return max; }
+ return pos;
+}
+
+const constants = {
+ orientation: {
+ horizontal: {
+ dimension: 'width',
+ direction: 'left',
+ coordinate: 'x',
+ },
+ vertical: {
+ dimension: 'height',
+ direction: 'top',
+ coordinate: 'y',
+ }
+ }
+};
+
+class Slider extends Component {
+ static propTypes = {
+ min: PropTypes.number,
+ max: PropTypes.number,
+ step: PropTypes.number,
+ value: PropTypes.number,
+ orientation: PropTypes.string,
+ onChange: PropTypes.func,
+ className: PropTypes.string,
+ }
+
+ static defaultProps = {
+ min: 0,
+ max: 100,
+ step: 1,
+ value: 0,
+ orientation: 'horizontal',
+ }
+
+ state = {
+ limit: 0,
+ grab: 0
+ }
+
+ // Add window resize event listener here
+ componentDidMount() {
+ this.calculateDimensions();
+ window.addEventListener('resize', this.calculateDimensions);
+ }
+
+ componentWillUnmount() {
+ window.removeEventListener('resize', this.calculateDimensions);
+ }
+
+ handleSliderMouseDown = (e) => {
+ let value, { onChange } = this.props;
+ if (!onChange) return;
+ value = this.position(e);
+ onChange && onChange(value);
+ }
+
+ handleKnobMouseDown = () => {
+ document.addEventListener('mousemove', this.handleDrag);
+ document.addEventListener('mouseup', this.handleDragEnd);
+ }
+
+ handleDrag = (e) => {
+ let value, { onChange } = this.props;
+ if (!onChange) return;
+ value = this.position(e);
+ onChange && onChange(value);
+ }
+
+ handleDragEnd = () => {
+ document.removeEventListener('mousemove', this.handleDrag);
+ document.removeEventListener('mouseup', this.handleDragEnd);
+ }
+
+ handleNoop = (e) => {
+ e.stopPropagation();
+ e.preventDefault();
+ }
+
+ calculateDimensions = () => {
+ let { orientation } = this.props;
+ let dimension = capitalize(constants.orientation[orientation].dimension);
+ const sliderPos = findDOMNode(this.refs.slider)['offset' + dimension];
+ const handlePos = findDOMNode(this.refs.handle)['offset' + dimension]
+ this.setState({
+ limit: sliderPos - handlePos,
+ grab: handlePos / 2,
+ });
+ }
+ getPositionFromValue = (value) => {
+ let percentage, pos;
+ let { limit } = this.state;
+ let { min, max } = this.props;
+ percentage = (value - min) / (max - min);
+ pos = Math.round(percentage * limit);
+
+ return pos;
+ }
+
+ getValueFromPosition = (pos) => {
+ let percentage, value;
+ let { limit } = this.state;
+ let { orientation, min, max, step } = this.props;
+ percentage = (maxmin(pos, 0, limit) / (limit || 1));
+
+ if (orientation === 'horizontal') {
+ value = step * Math.round(percentage * (max - min) / step) + min;
+ } else {
+ value = max - (step * Math.round(percentage * (max - min) / step) + min);
+ }
+
+ return value;
+ }
+
+ position = (e) => {
+ let pos, value, { grab } = this.state;
+ let { orientation } = this.props;
+ const node = findDOMNode(this.refs.slider);
+ const coordinateStyle = constants.orientation[orientation].coordinate;
+ const directionStyle = constants.orientation[orientation].direction;
+ const coordinate = e['client' + capitalize(coordinateStyle)];
+ const direction = node.getBoundingClientRect()[directionStyle];
+
+ pos = coordinate - direction - grab;
+ value = this.getValueFromPosition(pos);
+
+ return value;
+ }
+
+ coordinates = (pos) => {
+ let value, fillPos, handlePos;
+ let { limit, grab } = this.state;
+ let { orientation } = this.props;
+ value = this.getValueFromPosition(pos);
+ handlePos = this.getPositionFromValue(value);
+
+ if (orientation === 'horizontal') {
+ fillPos = handlePos + grab;
+ } else {
+ fillPos = limit - handlePos + grab;
+ }
+
+ return {
+ fill: fillPos,
+ handle: handlePos,
+ };
+ }
+
+ render() {
+ let dimension, direction, position, coords, fillStyle, handleStyle, displayValue;
+ let { value, orientation, className } = this.props;
+
+ dimension = constants.orientation[orientation].dimension;
+ direction = constants.orientation[orientation].direction;
+
+ position = this.getPositionFromValue(value);
+ coords = this.coordinates(position);
+
+ fillStyle = {[dimension]: `${coords.fill}px`};
+ handleStyle = {[direction]: `${coords.handle}px`};
+
+ if(this.props.displayValue) {
+ displayValue = <div>{this.props.value}</div>;
+ }
+
+ return (
+ <div
+ ref="slider"
+ className={joinClasses('rangeslider ', 'rangeslider-' + orientation, className)}
+ onMouseDown={this.handleSliderMouseDown}
+ onClick={this.handleNoop}
+ style={{display:'flex'}}>
+ <div
+ ref="fill"
+ className="rangeslider__fill"
+ style={fillStyle} />
+ <div
+ ref="handle"
+ className="rangeslider__handle"
+ onMouseDown={this.handleKnobMouseDown}
+ onClick={this.handleNoop}
+ style={handleStyle}>
+ {displayValue}
+ </div>
+ </div>
+ );
+ }
+}
+
+export default Slider;
diff --git a/skyquake/framework/widgets/listy/listy.js b/skyquake/framework/widgets/listy/listy.js
new file mode 100644
index 0000000..ac0e58a
--- /dev/null
+++ b/skyquake/framework/widgets/listy/listy.js
@@ -0,0 +1,152 @@
+/*
+ *
+ * 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 _ from 'lodash';
+
+/**
+ *
+ *
+ */
+export default class Listy extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+
+ /**
+
+ */
+ createList(data, iter=0) {
+ var groupTag = this.props.groupTag.tag;
+ var groupClass = this.props.groupTag.className;
+ var itemTag = this.props.itemTag.tag;
+ var itemClass = this.props.itemTag.className;
+ var listHeaderTag = this.props.listHeaderTag.tag;
+ var listHeaderClass = this.props.listHeaderTag.className;
+
+ var listNode = null;
+ var self = this;
+ if (_.isArrayLike(data) && _.isObjectLike(data)) {
+ var children = [];
+ data.forEach(function(element, index, array) {
+ if ( _.isArrayLike(element) || _.isObjectLike(element)) {
+ children.push(self.createList(element, iter+1));
+ } else {
+ children.push(React.createElement(itemTag, {
+ key: index,
+ className: itemClass
+ }, element));
+ }
+ });
+
+ listNode = React.createElement(groupTag, {
+ key: iter,
+ className: groupClass }, children);
+ }
+ else if (_.isObjectLike(data)) {
+ var children = [];
+ for (var key in data) {
+ if ( _.isArrayLike(data[key]) || _.isObjectLike(data[key])) {
+ children.push(
+ React.createElement(listHeaderTag, {
+ key: key + '_header',
+ className: listHeaderClass }, key + ":")
+ );
+ children.push(
+ React.createElement(groupTag, {
+ key: key + '_list',
+ className: groupClass },
+ [this.createList(data[key], iter + 1)])
+ );
+
+ } else {
+ // TODO: Add span to line-wrap the data part (hanging)
+ children.push(React.createElement(itemTag, {
+ key: key,
+ className: itemClass},
+ key + ": " + data[key]));
+ }
+ }
+ listNode = React.createElement(groupTag, {
+ key: iter,
+ className: groupClass }, children);
+ } else {
+ listNode = React.createElement(itemTag, {
+ className: itemClass}, data);
+ }
+
+ return listNode;
+ }
+
+ noDataMessage() {
+ return React.createElement("div", {
+ className: this.props.noDataMessageClass},
+ this.props.noDataMessage);
+ }
+
+ render () {
+ var data = this.props.data;
+
+ return React.createElement("div", {
+ className: "listy" },
+ _.isEmpty(data) ?
+ this.noDataMessage() :
+ this.createList(data)
+ )
+ }
+}
+
+Listy.validateTagDefinition = function(props, propName, componentName) {
+ let obj = props[propName];
+ let fullAttr = componentName + "." + propName;
+
+ if (!obj)
+ return new Error('Validation failed. "%" is undefined', fullAttr);
+ if (!obj.hasOwnProperty("tag") || _.isEmpty(obj.tag))
+ return new Error('Validation failed. "%s" missing attribute "tag"', fullAttr);
+ if (!obj.hasOwnProperty("className") || obj.className == undefined)
+ return new Error('Validation failed. "%s" missing attribute "className"', fullAttr);
+}
+
+Listy.propTypes = {
+ data: React.PropTypes.object,
+ groupTag: Listy.validateTagDefinition,
+ itemTag: Listy.validateTagDefinition,
+ listHeaderTag: Listy.validateTagDefinition,
+ debugMode: React.PropTypes.bool
+}
+
+Listy.defaultProps = {
+ data: {},
+
+ // Visual Rules
+ groupTag: {
+ tag: "ul",
+ className: "listyGroup"
+ },
+ itemTag: {
+ tag: "li",
+ className: "listyItem"
+ },
+ listHeaderTag: {
+ tag: "h2",
+ className: "listyGroupHeader"
+ },
+ noDataMessage: "No data",
+ noDataMessageClass: "listyNoDataMessage",
+ debugMode: false
+}
diff --git a/skyquake/framework/widgets/loading-indicator/loading-indicator-animations.scss b/skyquake/framework/widgets/loading-indicator/loading-indicator-animations.scss
new file mode 100644
index 0000000..44222de
--- /dev/null
+++ b/skyquake/framework/widgets/loading-indicator/loading-indicator-animations.scss
@@ -0,0 +1,53 @@
+
+/*
+ *
+ * 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-scale-pulse-out-rapid > div {
+ background-color: #fff;
+ width: 4px;
+ height: 2rem;
+ border-radius: 2px;
+ margin: 2px;
+ animation-fill-mode: both;
+ display: inline-block;
+ animation: line-scale-pulse-out-rapid 0.9s -0.5s infinite cubic-bezier(0.11, 0.49, 0.38, 0.78); }
+ .line-scale-pulse-out-rapid > div:nth-child(2), .line-scale-pulse-out-rapid > div:nth-child(4) {
+ animation-delay: -0.25s !important; }
+ .line-scale-pulse-out-rapid > div:nth-child(1), .line-scale-pulse-out-rapid > div:nth-child(5) {
+ animation-delay: 0s !important; }
+
+
+.loader-animation-enter, .loader-animation-appear {
+ opacity: 0.01;
+}
+
+.loader-animation-enter.loader-animation-enter-active, .loader-animation-appear.loader-animation-appear-active {
+ opacity: 1.0;
+ transition: opacity 300ms ease-in;
+}
+
+.loader-animation-leave {
+ visibility:hidden;
+ height:0px;
+ width:0px;
+ opacity:1.0;
+}
+
+.loader-animation-leave.loader-animation-leave-active {
+ opacity:0;
+ transition: opacity 300ms ease-in;
+}
diff --git a/skyquake/framework/widgets/loading-indicator/loadingIndicator.jsx b/skyquake/framework/widgets/loading-indicator/loadingIndicator.jsx
new file mode 100644
index 0000000..9e77fdd
--- /dev/null
+++ b/skyquake/framework/widgets/loading-indicator/loadingIndicator.jsx
@@ -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 React from 'react';
+import '../../../node_modules/loaders.css/src/animations/line-scale-pulse-out-rapid.scss';
+import './loading-indicator-animations.scss';
+let ReactCSSTransitionGroup = require('react-addons-css-transition-group');
+export default class Loader extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+ render() {
+ let loader = '';
+ var style = {
+ height: this.props.size + 'rem',
+ width: this.props.size * 0.15 + 'rem',
+ backgroundColor: this.props.color || 'white'
+ }
+ if (this.props.show) {
+ loader = (
+ <div
+ transitionName="loader-animation"
+ transitionAppear={true}
+ component="div"
+ className={"line-scale-pulse-out-rapid"}>
+ <div style={style}></div>
+ <div style={style}></div>
+ <div style={style}></div>
+ <div style={style}></div>
+ <div style={style}></div>
+ </div>
+ );
+ }else {
+ loader = <span></span>
+ }
+ return loader;
+
+ }
+}
+
+Loader.defaultProps = {
+ show: true,
+ size: '5'
+}
+
diff --git a/skyquake/framework/widgets/login/login.js b/skyquake/framework/widgets/login/login.js
new file mode 100644
index 0000000..f6ce188
--- /dev/null
+++ b/skyquake/framework/widgets/login/login.js
@@ -0,0 +1,51 @@
+
+/*
+ *
+ * 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 LoginScreen = require('./login.jsx');
+angular.module('login', ['ui.router'])
+ .config(function($stateProvider) {
+
+ $rw.nav.push({
+ module: 'login',
+ name: "Login"
+ });
+
+ $stateProvider.state('login', {
+ url: '/login',
+ replace: true,
+ template: '<login-screen></login-screen>'
+ });
+
+})
+.directive('loginScreen', function() {
+ return {
+ restrict: 'AE',
+ controller: function($element) {
+ function reactRender() {
+ React.render(
+ React.createElement(LoginScreen, null)
+ ,
+ $element[0]
+ );
+ }
+ reactRender();
+ }
+ };
+})
+ ;
diff --git a/skyquake/framework/widgets/login/login.jsx b/skyquake/framework/widgets/login/login.jsx
new file mode 100644
index 0000000..1506809
--- /dev/null
+++ b/skyquake/framework/widgets/login/login.jsx
@@ -0,0 +1,91 @@
+/*
+ *
+ * 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 Utils from 'utils/utils.js';
+import Button from 'widgets/button/rw.button.js';
+import './login.scss'
+let rw = require('utils/rw.js');
+class LoginScreen extends React.Component{
+ constructor(props) {
+ super(props);
+ var API_SERVER = rw.getSearchParams(window.location).api_server;
+ if (!API_SERVER) {
+ window.location.href = "//" + window.location.host + '/index.html?api_server=' + window.location.protocol + '//localhost';
+ }
+ this.state = {
+ username: '',
+ password: ''
+ };
+
+ }
+ updateValue = (e) => {
+ let state = {};
+ state[e.target.name] = e.target.value;
+ this.setState(state);
+ }
+ validate = (e) => {
+ let self = this;
+ let state = this.state;
+ e.preventDefault();
+ if (state.username == '' || state.password == '') {
+ console.log('false');
+ return false;
+ } else {
+ Utils.setAuthentication(state.username, state.password, function() {
+ //Returning to previous location disabled post port
+ // let hash = window.sessionStorage.getItem("locationRefHash") || '#/';
+ // if (hash == '#/login') {
+ // hash = '#/'
+ // }
+ // window.location.hash = hash;
+ self.context.router.push('/');
+ });
+
+ }
+ }
+ submitForm = (e) => {
+ if(e.keyCode == 13){
+ this.validate(e);
+ }
+ }
+ render() {
+ let html;
+ html = (
+ <form className="login-cntnr" autoComplete="on" onKeyUp={this.submitForm}>
+ <div className="logo"> </div>
+ <h1 className="riftio">Launchpad Login</h1>
+ <p>
+ <input type="text" placeholder="Username" name="username" value={this.state.username} onChange={this.updateValue} autoComplete="username"></input>
+ </p>
+ <p>
+ <input type="password" placeholder="Password" name="password" onChange={this.updateValue} value={this.state.password} autoComplete="password"></input>
+ </p>
+ <p>
+ <Button className="sign-in" onClick={this.validate} style={{cursor: 'pointer'}} type="submit" label="Sign In"/>
+ </p>
+ </form>
+ )
+ return html;
+ }
+}
+LoginScreen.contextTypes = {
+ router: React.PropTypes.object
+ };
+
+
+export default LoginScreen;
diff --git a/skyquake/framework/widgets/login/login.scss b/skyquake/framework/widgets/login/login.scss
new file mode 100644
index 0000000..cb14e9b
--- /dev/null
+++ b/skyquake/framework/widgets/login/login.scss
@@ -0,0 +1,21 @@
+/*
+ *
+ * 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-cntnr .logo {
+ background-image: url(../../style/img/header-logo.png);
+
+}
diff --git a/skyquake/framework/widgets/login/loginAuthActions.js b/skyquake/framework/widgets/login/loginAuthActions.js
new file mode 100644
index 0000000..c2c805f
--- /dev/null
+++ b/skyquake/framework/widgets/login/loginAuthActions.js
@@ -0,0 +1,21 @@
+/*
+ *
+ * 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 Alt from 'widgets/skyquake_container/skyquakeAltInstance';
+module.exports = Alt.generateActions(
+ 'notAuthenticated'
+ );
diff --git a/skyquake/framework/widgets/login/main.js b/skyquake/framework/widgets/login/main.js
new file mode 100644
index 0000000..f69a91e
--- /dev/null
+++ b/skyquake/framework/widgets/login/main.js
@@ -0,0 +1,11 @@
+import { render } from 'react-dom';
+import SkyquakeRouter from 'widgets/skyquake_container/skyquakeRouter.jsx';
+const config = require('json!../config.json');
+
+let context = require.context('./', true, /^\.\/.*\.jsx$/);
+let router = SkyquakeRouter(config, context);
+let element = document.querySelector('#content');
+
+render(router, element);
+
+
diff --git a/skyquake/framework/widgets/metric-bars/metricBarGroup.jsx b/skyquake/framework/widgets/metric-bars/metricBarGroup.jsx
new file mode 100644
index 0000000..d4f92d6
--- /dev/null
+++ b/skyquake/framework/widgets/metric-bars/metricBarGroup.jsx
@@ -0,0 +1,141 @@
+
+/*
+ *
+ * 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 ReactDOM from 'react-dom';
+import d3 from 'd3';
+import './metricBarGroup.scss';
+
+class MetricBarGroup extends React.Component {
+ constructor(props) {
+ super(props);
+
+ }
+ componentWillMount() {
+ const {...props} = this.props;
+ this.margin = {top: 20, right: 50, bottom: 700, left: 100};
+ this.width = 1220 - this.margin.left - this.margin.right;
+ this.height = 1220 - this.margin.top - this.margin.bottom;
+ // this.width = 800;
+ // this.height = 600;
+ this.x = d3.scale.ordinal()
+ .rangeRoundBands([0, this.width], .1);
+
+ this.y = d3.scale.linear()
+ .range([this.height, 0]);
+
+ this.xAxis = d3.svg.axis()
+ .scale(this.x)
+ .orient("bottom");
+
+ this.yAxis = d3.svg.axis()
+ .scale(this.y)
+ .orient("left")
+ .ticks(props.ticks.number, props.ticks.type);
+ }
+ componentDidMount() {
+ let el = document.querySelector('#' + this.props.title + this.props.lp_id);
+ this.svg = d3.select(el)
+ .append('svg');
+ let self = this;
+
+ let totalWidth = this.width + this.margin.left + this.margin.right;
+ let totalHeight = this.height + this.margin.top + this.margin.bottom;
+ this.svg = this.svg
+ .attr("viewBox", "-10 0 " + totalWidth + " " + totalHeight + "")
+ .attr("preserveAspectRatio", "xMidYMid meet")
+ .style("overflow","visible")
+ .append("g")
+ .attr("transform", "translate(" + this.margin.left + "," + this.margin.top + ")");
+
+ this.svg.append("g")
+ .attr("class", "y axis")
+ .call(this.yAxis);
+ this.svg.append("g")
+ .attr("class", "x axis")
+ .attr("transform", "translate(-1," + this.height + ")")
+ .call(this.xAxis)
+ this.drawBars(this.props);
+
+
+ }
+ componentWillReceiveProps(props) {
+ this.drawBars(props);
+ }
+ drawBars = (props) => {
+ let DATA = props.data.sort(function(a,b){
+ return (a.id > b.id) ? -1 : 1;
+ });
+ this.x.domain(DATA.map(function(d, i) { return d.id }));
+ let self = this;
+ let barGroup = this.svg.selectAll(".barGroup").data(DATA, function(d, i) { return d.id});;
+ let barGroupExit = barGroup.exit().remove();
+ let barGroupEnter = barGroup.enter().append('g')
+ .attr('class', 'barGroup');
+ barGroupEnter
+ .append("rect")
+ .attr("class", "bar")
+ .attr("x", function(d) { return self.x(d.id); })
+ .attr("width", self.x.rangeBand())
+ .attr("y", function(d) { return self.y(d[props.valueName]); })
+ .attr("height", function(d) {return self.height - self.y(d[props.valueName]); });
+ barGroupEnter.append("g")
+ .attr("transform", function(d){
+ return "translate("+ (parseInt(self.x(d.id)) + (self.x.rangeBand() / 2) + 10) +"," + (parseInt(self.height) + 10) + ")"
+ })
+ .append('text')
+ .classed('metriclabel', true)
+ .style("text-anchor", "end")
+ .attr('transform', 'rotate(-75)')
+ .text(function(d) { return d.name;} )
+
+ let barGroupUpdate = barGroup.transition();
+ barGroupUpdate.select('rect')
+ .attr("class", "bar")
+ .attr("x", function(d) { return self.x(d.id) })
+ .attr("width", self.x.rangeBand())
+ .attr("y", function(d) { return self.y(d[props.valueName]); })
+ .attr("height", function(d) {return self.height - self.y(d[props.valueName]); });
+ barGroupUpdate
+ .select('g')
+ .attr("transform", function(d){
+ return "translate("+ (parseInt(self.x(d.id)) + (self.x.rangeBand() / 2) + 10) +"," + (parseInt(self.height) + 10) + ")"
+ })
+ .select('text')
+ .style("text-anchor", "end")
+ .attr('transform', 'rotate(-75)')
+ .text(function(d) { return d.name;} )
+ }
+ render() {
+ let html = <div></div>;
+ let self = this;
+ return <div className="metricBarGroup"><h3>{this.props.title}</h3><div id={this.props.title + this.props.lp_id}></div></div>;
+ }
+}
+MetricBarGroup.defaultProps = {
+ ticks: {
+ number: 2,
+ type: '%'
+ },
+ valueName: 'utilization',
+ title: '',
+ data: []
+}
+
+
+export default MetricBarGroup;
diff --git a/skyquake/framework/widgets/metric-bars/metricBarGroup.scss b/skyquake/framework/widgets/metric-bars/metricBarGroup.scss
new file mode 100644
index 0000000..6517612
--- /dev/null
+++ b/skyquake/framework/widgets/metric-bars/metricBarGroup.scss
@@ -0,0 +1,49 @@
+
+/*
+ *
+ * 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 "../../../framework/style/_colors.scss";
+ .metricBarGroup {
+ display:flex;
+ flex-direction:column;
+ padding: 1rem;
+ justify-content: center;
+ h3 {
+ text-align:center;
+ font-weight:bold;
+ }
+ .bar {
+ fill: $brand-blue-light;
+ stroke: black;
+ }
+ .y.axis {
+ .domain {
+ transform:scaleX(0.25);
+ }
+ }
+ .x.axis {
+ .domain {
+ transform:scaleY(0.25);
+ }
+ }
+ .metriclabel, .y.axis .tick text{
+ font-size:3rem;
+ }
+ &>div {
+ flex: 1 1 %33;
+ }
+ }
diff --git a/skyquake/framework/widgets/mixins/ButtonEventListener.js b/skyquake/framework/widgets/mixins/ButtonEventListener.js
new file mode 100644
index 0000000..7a8e0cc
--- /dev/null
+++ b/skyquake/framework/widgets/mixins/ButtonEventListener.js
@@ -0,0 +1,207 @@
+
+/*
+ *
+ * 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');
+
+
+/**
+ * Event listener Mixins. A vast majority of components are going to expose these events to the user so we're making
+ * a central location to house all of them.
+ */
+module.exports = {
+ propTypes: {
+ onClick: React.PropTypes.func,
+ onMouseUp: React.PropTypes.func,
+ onMouseDown: React.PropTypes.func,
+ onMouseOver: React.PropTypes.func,
+ onMouseEnter: React.PropTypes.func,
+ onMouseLeave: React.PropTypes.func,
+ onMouseOut: React.PropTypes.func,
+
+ onTouchCancel: React.PropTypes.func,
+ onTouchEnd: React.PropTypes.func,
+ onTouchMove: React.PropTypes.func,
+ onTouchStart: React.PropTypes.func,
+
+ onKeyDown: React.PropTypes.func,
+ onKeyPress: React.PropTypes.func,
+ onKeyUp: React.PropTypes.func,
+
+ onFocus: React.PropTypes.func,
+ onBlur: React.PropTypes.func
+ },
+
+ /**
+ * A vast majority of these functions just check to see if the event function is defined and then passes the function
+ * both the event and the local props.
+ * @param e
+ */
+ onClick: function(e) {
+ if (Boolean(this.props.onClick) && !this.state.isDisabled && !this.state.isReadOnly) {
+ //this.props.isActive = true;
+ this.props.onClick(e, this);
+ } else {
+ e.preventDefault();
+ }
+ },
+ onMouseUp: function(e) {
+ if (!this.state.isDisabled && !this.state.isReadOnly) {
+ this.setState({isActive:false});
+ if (Boolean(this.props.onMouseUp)) {
+ this.props.onMouseUp(e, this);
+ }
+ } else {
+ e.preventDefault();
+ }
+ },
+ onMouseDown: function(e) {
+ if (!this.state.isDisabled && !this.state.isReadOnly) {
+ this.setState({isActive:true});
+ if (Boolean(this.props.onMouseDown)) {
+ this.props.onMouseDown(e, this);
+ }
+ }
+ },
+ onMouseOver: function(e) {
+ if (Boolean(this.props.onMouseOver) && !this.state.isDisabled && !this.state.isReadOnly) {
+ this.props.onMouseOver(e, this);
+ } else {
+ e.preventDefault();
+ }
+ },
+ onMouseEnter: function(e) {
+ if (!this.state.isDisabled && !this.state.isReadOnly) {
+ this.setState({isHovered:true});
+ if (this.props.onMouseEnter) {
+ this.props.onMouseEnter(e, this);
+ }
+ } else {
+ e.preventDefault();
+ }
+ },
+ onMouseLeave: function(e) {
+ if (!this.state.isDisabled && !this.state.isReadOnly) {
+ this.setState({isHovered:false, isActive:false});
+ if (Boolean(this.props.onMouseLeave)) {
+ this.props.onMouseLeave(e, this);
+ }
+ }
+ },
+ onMouseOut: function(e) {
+ if (!this.state.isDisabled && !this.state.isReadOnly) {
+ if (Boolean(this.props.onMouseOut)) {
+ this.props.onMouseOut(e, this);
+ }
+ } else {
+ e.preventDefault();
+ }
+ },
+ onTouchCancel: function(e) {
+ if (!this.state.isDisabled && !this.state.isReadOnly) {
+ this.setState({isActive: false});
+ if (Boolean(this.props.onTouchCancel)) {
+ this.props.onTouchCancel(e, this);
+ }
+ } else {
+ e.preventDefault();
+ }
+ },
+ onTouchEnd: function(e) {
+ if (!this.state.isDisabled && !this.state.isReadOnly) {
+ this.setState({isActive: false});
+ if (Boolean(this.props.onTouchEnd)) {
+ this.props.onTouchEnd(e, this);
+ }
+ } else {
+ e.preventDefault();
+ }
+ },
+ onTouchMove: function(e) {
+ if (Boolean(this.props.onTouchMove) && !this.state.isDisabled && !this.state.isReadOnly) {
+ this.props.onTouchMove(e, this);
+ } else {
+ e.preventDefault();
+ }
+ },
+ onTouchStart: function(e) {
+ if (!this.state.isDisabled && !this.state.isReadOnly) {
+ this.setState({isActive: true});
+ if (Boolean(this.props.onTouchStart)) {
+ this.props.onTouchStart(e, this);
+ }
+ } else {
+ e.preventDefault();
+ }
+ },
+ onKeyDown: function(e) {
+ if (Boolean(this.props.onKeyDown) && !this.state.isDisabled && !this.state.isReadOnly) {
+ this.props.onKeyDown(e, this);
+ } else {
+ e.preventDefault();
+ }
+ },
+ onKeyPress: function(e) {
+ if (Boolean(this.props.onKeyPress) && !this.state.isDisabled && !this.state.isReadOnly) {
+ this.props.onKeyPress(e, this);
+ } else {
+ e.preventDefault();
+ }
+ },
+ onKeyUp: function(e) {
+ if (Boolean(this.props.onKeyUp) && !this.state.isDisabled && !this.state.isReadOnly) {
+ this.props.onKeyUp(e, this);
+ } else {
+ e.preventDefault();
+ }
+ },
+ onFocus: function(e) {
+ if (!this.state.isDisabled && !this.state.isReadOnly) {
+ this.setState({isFocused: true});
+ if (Boolean(this.props.onFocus)) {
+ this.props.onFocus(e, this);
+ }
+ } else {
+ e.preventDefault();
+ }
+ },
+ onBlur: function(e) {
+ if (!this.state.isDisabled && !this.state.isReadOnly) {
+ this.setState({isFocused: false});
+ if (Boolean(this.props.onBlur)) {
+ this.props.onBlur(e, this);
+ }
+ } else {
+ e.preventDefault();
+ }
+ },
+
+ /**
+ * Generic clone function that takes an object and returns an independent clone of it.
+ * Needed to give the user a clone of the props instead of the props themselves to prevent direct access to the props.
+ * @param obj
+ * @returns {*}
+ **/
+ clone: function(obj) {
+ if (null == obj || "object" != typeof obj) return obj;
+ var copy = obj.constructor();
+ for (var attr in obj) {
+ if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr];
+ }
+ return copy;
+ }
+};
diff --git a/skyquake/framework/widgets/multicomponent/multicomponent.js b/skyquake/framework/widgets/multicomponent/multicomponent.js
new file mode 100644
index 0000000..4c2f9a5
--- /dev/null
+++ b/skyquake/framework/widgets/multicomponent/multicomponent.js
@@ -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.
+ *
+ */
+var React = require('react');
+var mixin = require('../mixins/ButtonEventListener.js')
+/**
+ * Contains a set of components. Takes a list of components and renders them in lists.
+ * It's props values and a brief description below
+ * component_list: Takes a list of React components.
+ */
+module.exports = React.createClass({
+ displayName: 'Multicomponent',
+ mixins:mixin.MIXINS,
+ propTypes: {
+ component_list: React.PropTypes.array.isRequired
+ },
+
+ /**
+ * Defines default state.
+ * component_list: Takes a list of React components.
+ */
+ getInitialState: function() {
+ return {
+ component_list: this.props.component_list
+
+ }
+ },
+
+
+ /**
+ * Renders the multicomponent Component
+ * Returns a list React components
+ * @returns {*}
+ */
+ render: function() {
+
+ var componentDOM = React.createElement("div", {className:this.props.className},
+ this.props.component_list
+ )
+ return componentDOM;
+ }
+});
\ No newline at end of file
diff --git a/skyquake/framework/widgets/nfvi-metric-bars/nfviMetricBars.jsx b/skyquake/framework/widgets/nfvi-metric-bars/nfviMetricBars.jsx
new file mode 100644
index 0000000..019c4d7
--- /dev/null
+++ b/skyquake/framework/widgets/nfvi-metric-bars/nfviMetricBars.jsx
@@ -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.
+ *
+ */
+import React from 'react';
+import './nfviMetricBars.scss';
+class MetricChart extends React.Component {
+ constructor(props) {
+ super(props)
+ }
+ render() {
+ let html;
+ let byteSize = 1e+9;
+ let utilization = this.props.utilization;
+ // let utilization = (1 > this.props.utilization) ? Math.round(this.props.utilization * 100) : this.props.utilization;
+ let isVCPU = (this.props.label == "VCPU");
+ let label;
+ if (isVCPU) {
+ label = this.props.total;
+ } else {
+ var num = this.props.used / byteSize;
+ label = Math.round(num * 100) / 100 + ' / ' + Math.round(this.props.total / byteSize) + 'GB';
+ }
+ html = (
+ <div className="nvfi-metric-container">
+ <div className="nfvi-metric-chart">
+ <div className="nfvi-metric-chart-value">{utilization}%</div>
+ <div className="nfvi-metric-chart-bar" style={{height: utilization + '%'}}></div>
+ </div>
+ <div className="nfvi-metric-value">{label}</div>
+ <div className="nfvi-metric-label">{this.props.label}</div>
+ </div>
+ );
+ return html;
+ }
+}
+
+class nfviMetrics extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+ render() {
+ let html;
+ let nfviMetrics = this.props.metrics.map(function(metric, k) {
+ //Do not display network metrics
+ if("outgoing" in metric || "incoming" in metric){
+ return
+ } else {
+ return (<MetricChart key={k} utilization={metric.utilization} label={metric.label} total={metric.total} used={metric.used}/>)
+ }
+ });
+ html = (
+ <div className="nfvi-metrics-tray">
+ {nfviMetrics}
+ </div>
+ )
+ return html;
+ }
+}
+nfviMetrics.defaultProps = {
+ metrics: []
+}
+
+
+
+export default nfviMetrics;
diff --git a/skyquake/framework/widgets/nfvi-metric-bars/nfviMetricBars.scss b/skyquake/framework/widgets/nfvi-metric-bars/nfviMetricBars.scss
new file mode 100644
index 0000000..fcd77bb
--- /dev/null
+++ b/skyquake/framework/widgets/nfvi-metric-bars/nfviMetricBars.scss
@@ -0,0 +1,102 @@
+
+/*
+ *
+ * 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 "../../../framework/style/_colors.scss";
+.nfvi-metrics-tray {
+ flex: 1 0;
+ display: flex;
+ flex-direction: row;
+ justify-content: space-around;
+ margin: 10px 0;
+ padding: 0 15px 0 10px;
+ height: 165px;
+ .nvfi-metric-container {
+ flex: 1 0;
+ display: flex;
+ flex-direction: column;
+ align-items: stretch;
+ margin: 0 10px 0 0;
+ }
+
+ .nfvi-metric-chart {
+ position: relative;
+ flex: 1 0;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ background: #fff;
+ }
+
+ .nfvi-metric-chart-value {
+ font-size: 2rem;
+ font-weight: 700;
+ z-index: 2;
+ }
+
+ .nfvi-metric-chart-bar {
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ height: 0%;
+ background: #00ACEE;
+ transition: all 300ms cubic-bezier(0.230, 1.000, 0.320, 1.000);
+ }
+
+ .nfvi-metric-value {
+ padding: 5px 10px;
+ text-align: center;
+ font-size: 16px;
+ }
+
+ .nfvi-metric-label {
+ text-align: center;
+ font-size: 12px;
+ text-transform: uppercase;
+ }
+
+ }
+ .metricBars {
+ display:flex;
+ flex-direction:row;
+ flex-wrap:wrap;
+ padding: 1rem;
+ justify-content: space-around;
+ h3 {
+ text-align:center;
+ font-weight:bold;
+ }
+ .bar {
+ fill: $brand-blue-light;
+ stroke: black;
+ }
+ .y.axis {
+ .domain {
+ transform:scaleX(0.25);
+ }
+ }
+ .x.axis {
+ .domain {
+ transform:scaleY(0.25);
+ }
+ }
+ &>div {
+ flex: 1 1;
+ max-width:400px;
+ }
+ }
diff --git a/skyquake/framework/widgets/operational-status/launchpadOperationalStatus.jsx b/skyquake/framework/widgets/operational-status/launchpadOperationalStatus.jsx
new file mode 100644
index 0000000..ba77a79
--- /dev/null
+++ b/skyquake/framework/widgets/operational-status/launchpadOperationalStatus.jsx
@@ -0,0 +1,201 @@
+
+/*
+ *
+ * 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 Loader from 'widgets/loading-indicator/loadingIndicator.jsx';
+
+//Currently displays all buffer state messages. Should consider showing only the n most recent.
+//TODO remove loader when current state is running
+//TODO need to look at refactoring this
+class ThrottledMessage extends React.Component {
+
+ constructor(props) {
+ super(props);
+ let self = this;
+ this.displayBuffer = [];
+ this.bufferInterval;
+ this.last_id = null;
+ this.state = {};
+ this.state.loading = props.loading;
+ this.state.buffer = {};
+ this.state.displayMessage = 'Loading...'
+ }
+
+ componentWillReceiveProps(nextProps) {
+ let buffer = nextProps.buffer;
+ if(buffer.length) {
+ this.buildBufferObject(nextProps);
+ this.bufferIt(this.props);
+ }
+ }
+
+ componentDidMount() {
+ if(this.props.buffer.length) {
+ let buffer = this.props.buffer;
+ this.buildBufferObject(this.props);
+ this.bufferIt(this.props);
+ }
+ }
+ componentWillUnmount() {
+ clearInterval(this.bufferInterval);
+ }
+ buildBufferObject(props) {
+ let self = this;
+ let buffer = self.state.buffer;
+ this.last_id = props.buffer[props.buffer.length -1].id;
+ props.buffer.map(function(item) {
+ if(!buffer[item.id]) {
+ buffer[item.id] = {
+ displayed: false,
+ data: item
+ }
+ self.displayBuffer.push(buffer[item.id]);
+ }
+ });
+ }
+
+ bufferIt(props) {
+ let self = this
+ clearInterval(self.bufferInterval);
+ self.bufferInterval = setInterval(function() {
+ let currentStatus = self.props.currentStatus;
+ let failed = currentStatus == 'failed';
+ for (let i = 0; i < self.displayBuffer.length; i++) {
+ if(!self.displayBuffer[i].displayed) {
+ self.displayBuffer[i].displayed = true;
+ let displaymsg;
+ if(failed) {
+ displaymsg = self.displayBuffer[self.displayBuffer.length-1].data.description;
+ clearInterval(self.bufferInterval);
+ self.props.onEnd(failed);
+ } else {
+ displaymsg = self.displayBuffer[i].data.description;
+ }
+ self.setState({
+ displayMessage: displaymsg
+ });
+ break;
+ }
+ }
+
+ if((currentStatus == 'running' || currentStatus == 'started' || currentStatus == 'stopped' || failed) && self.displayBuffer[self.displayBuffer.length - 1].displayed ) {
+ clearInterval(self.bufferInterval);
+ self.props.onEnd(failed);
+ }
+ }, 600)
+ }
+ render() {
+ if(!this.props.hasFailed) {
+ return (<span className='throttledMessageText'>{this.state.displayMessage}</span>)
+ } else {
+ return (<span> </span>)
+ }
+ }
+}
+
+ThrottledMessage.defaultProps = {
+ buffer: []
+}
+
+export default class operationalStatus extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {};
+ this.state.message = 'Loading...';
+ this.state.messageHistory = {};
+ }
+ componentWillReceiveProps(nextProps) {
+
+ }
+ finishedMessages(){
+ this.setState({
+ loading: false
+ })
+ }
+ statusMessage(currentStatus, currentStatusDetails) {
+ var message = currentStatus;
+ if (currentStatusDetails) {
+ message += ' - ' + currentStatusDetails;
+ }
+ return message;
+ }
+ render() {
+ let html;
+ let isDisplayed = this.props.display;
+ let isFailed = (this.props.currentStatus == 'failed') || false;
+ let title = (!this.props.loading || isFailed) ? <h2>History</h2> : '';
+ let status = this.props.status.map(function(status, index) {
+ return (
+ <li key={index}>
+ {status.description}
+ {status.details ? (
+ <ul>
+ <li>{status.details}
+ </li>
+ </ul>) : null}
+ </li>
+ )
+ }).reverse();
+ if(this.props.loading) {
+ if (!isFailed) {
+ isDisplayed = true;
+ //If there is no collection of status event message, just display currentStatus
+ if(status.length) {
+ html = (
+ <div className={this.props.className + '_loading'}>
+ <Loader show={!isFailed}/>
+ <ThrottledMessage currentStatus={this.props.currentStatus} buffer={this.props.status} onEnd={this.props.doneLoading}/>
+ </div>
+ )
+ } else {
+ html = (
+ <div className={this.props.className + '_loading'}>
+ <Loader show={!isFailed}/>
+ {this.statusMessage(this.props.currentStatus,this.props.currentStatusDetails)}
+ </div>
+ )
+ }
+ } else {
+ isDisplayed = true;
+ html = (
+
+ <ul>
+ <ThrottledMessage currentStatus={this.props.currentStatus} buffer={this.props.status} onEnd={this.props.doneLoading} hasFailed={isFailed}/>
+ {status}
+ </ul>
+ )
+ }
+ } else {
+ html = (
+ <ul>
+ {status}
+ </ul>
+ )
+ }
+ return (<div className={this.props.className + (isDisplayed ? '_open':'_close')}>{title} {html}</div>);
+ }
+}
+
+operationalStatus.defaultProps = {
+ status: [],
+ loading: true,
+ display: false
+}
+
+
+
diff --git a/skyquake/framework/widgets/panel/panel.jsx b/skyquake/framework/widgets/panel/panel.jsx
new file mode 100644
index 0000000..e8a3118
--- /dev/null
+++ b/skyquake/framework/widgets/panel/panel.jsx
@@ -0,0 +1,66 @@
+/*
+ *
+ * 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, {Component} from 'react';
+import 'style/core.css';
+import './panel.scss';
+export class Panel extends Component {
+ constructor(props) {
+ super(props)
+ }
+ render() {
+ let self = this;
+ let {children, className, title, ...props} = self.props;
+ let classRoot = className ? ' ' + className : ' '
+ let titleTag = title ? <header className="skyquakePanel-title">{title}</header> : '';
+ return (
+ <section className={'skyquakePanel' + classRoot} style={props.style}>
+ <i className="corner-accent top left"></i>
+ <i className="corner-accent top right"></i>
+ {titleTag}
+ <div className="skyquakePanel-wrapper">
+ <div className={(classRoot ? 'skyquakePanel-body ' + decorateClassNames(classRoot, '-body') : 'skyquakePanel-body')}>
+ {children}
+ </div>
+ </div>
+ <i className="corner-accent bottom left"></i>
+ <i className="corner-accent bottom right"></i>
+ </section>
+ )
+ }
+}
+
+Panel.defaultProps = {
+
+}
+
+export class PanelWrapper extends Component {
+ render() {
+ return (<div className={'skyquakePanelWrapper'}>
+ {this.props.children}
+ </div>)
+ }
+}
+
+export default Panel;
+
+
+function decorateClassNames(className, addendum) {
+ return className.split(' ').map(function(c) {
+ return c + addendum
+ }).join(' ');
+}
diff --git a/skyquake/framework/widgets/panel/panel.scss b/skyquake/framework/widgets/panel/panel.scss
new file mode 100644
index 0000000..ff36bbf
--- /dev/null
+++ b/skyquake/framework/widgets/panel/panel.scss
@@ -0,0 +1,67 @@
+/*
+ *
+ * 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';
+.skyquakePanel {
+ position:relative;
+ display:-ms-flexbox;
+ display:flex;
+ -ms-flex-direction:column;
+ flex-direction:column;
+ -ms-flex:1 1;
+ flex:1 1;
+ background-color: $body-color;
+ margin:0.5rem;
+ &-title{
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex-align: center;
+ -ms-grid-row-align: center;
+ align-items: center;
+ padding: 1rem;
+ background-color: white;
+ text-transform: uppercase;
+ }
+ &-wrapper {
+ overflow:auto;
+ height:100vh;
+ }
+ &-body {
+ display:-ms-flexbox;
+ display:flex;
+ -ms-flex-direction:column;
+ flex-direction:column;
+ padding:1rem;
+ }
+ &Wrapper {
+ display:-ms-flexbox;
+ display:flex;
+ width:100%;
+ height:100%;
+ }
+}
+
+/* Style for storybook */
+body{
+ height:100%;
+>#root{
+ height:100vh;
+ width:100vw;
+ display:-ms-flexbox;
+ display:flex;
+}
+}
diff --git a/skyquake/framework/widgets/radio-button/rw.radio-button.js b/skyquake/framework/widgets/radio-button/rw.radio-button.js
new file mode 100644
index 0000000..706d39e
--- /dev/null
+++ b/skyquake/framework/widgets/radio-button/rw.radio-button.js
@@ -0,0 +1,267 @@
+
+/*
+ *
+ * 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 ButtonEventListenerMixin = require('../mixins/ButtonEventListener.js');
+
+
+/**
+ * A radio-button component.
+ * It's props values and a brief description below
+ *
+ * label: The label for the radio button group.
+ * radiobuttons: The object that creates each radio button. Each object has a property "label" and "checked".
+ * label: The label for the individual radio button.
+ * checked: If set to true, the individual radio button is initialized with a check.
+ * requiredText: The text content of the "if required" message.
+ * errorText: The text content of the error message.
+ * ClassName: Css Classes applied to the element.
+ * size: The size of the element.
+ * isRequired: A boolean indicating whether or not the input is required.
+ * isDisabled: A boolean indicating the disabled state of the element.
+ * isReadOnly: A boolean indicating whether or not the input is read only.
+ * instructions: The text content of the instructions
+ **/
+module.exports = React.createClass({
+ displayName: "RadioButton",
+ mixins:[ButtonEventListenerMixin],
+ propTypes: {
+ name: React.PropTypes.string,
+ label: React.PropTypes.string,
+ radiobuttons: React.PropTypes.arrayOf(
+ React.PropTypes.shape(
+ {
+ label: React.PropTypes.string,
+ checked: React.PropTypes.bool
+ }
+ )),
+ requiredText: React.PropTypes.string,
+ errorText: React.PropTypes.string,
+ placeholder: React.PropTypes.string,
+ className: React.PropTypes.string,
+ size: React.PropTypes.string,
+ isRequired: React.PropTypes.bool,
+ isDisabled: React.PropTypes.bool,
+ isReadOnly: React.PropTypes.bool,
+ instructions: React.PropTypes.string
+ },
+
+
+ /**
+ * Sets the default input state.
+ * If there is no description for the variable, assume it's the same as it's props counterpart.
+ *
+ * isActive: Boolean to indicate if input is active.
+ * isHovered: Boolean to indicate if the input is being hovered over.
+ * isFocused: Boolean to indicate if the input has been focused.
+ * isDisabled: Boolean to indicate if input has been disabled.
+ *
+ * @returns {{sizeOfButton: (*|string), isActive: boolean, isHovered: boolean, isFocused: boolean, isDisabled: (*|boolean),
+ * isReadOnly: (*|.textInput.isReadOnly|.exports.propTypes.isReadOnly|.exports.getInitialState.isReadOnly|boolean),
+ * isRequired: (*|.textInput.isRequired|.exports.propTypes.isRequired|.exports.getInitialState.isRequired|isRequired|null),
+ * isValid: null, isSuccess: null}}
+ */
+ getInitialState: function() {
+ return {
+ label: this.props.label || "",
+ requiredText: this.props.requiredText || "Required",
+ instructionsText: this.props.instructions || "",
+ errorText: this.props.errorText || "",
+ size: this.props.size || '',
+ isActive: false,
+ isHovered: false,
+ isFocused: false,
+ isDisabled: this.props.disabled || false,
+ isReadOnly: this.props.isReadOnly || false,
+ isRequired: this.props.isRequired || null,
+ isValid: null, // Three way bool. Valid: true. Invalid: false. Not acted on: null.
+ isSuccess: null // Three way bool. Success: true. Error: false. Not acted on: null.
+ }
+ },
+
+ /**
+ * 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.isReadOnly + this.state.isDisabled + this.state.isActive + this.state.isFocused +
+ this.state.isHovered + this.state.isValid + this.state.isSuccess;
+ var nextStateString = nextState.isReadOnly + nextState.isDisabled + nextState.isActive + nextState.isFocused +
+ nextState.isHovered + nextState.isValid + nextState.isSuccess;
+ if (currentStateString == nextStateString) {
+ return false;
+ }
+ return true;
+ },
+
+
+
+ /**
+ * Returns a string reflecting the current state of the input.
+ * If the component state "isDisabled" is true, returns a string "disabled".
+ * If the component state "isReadOnly" is true, returns a string "readonly".
+ * 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}
+ */
+ setComponentState: function() {
+ var ret = "";
+ if (this.state.isDisabled) {
+ return "disabled";
+ }
+ if (this.state.isReadOnly) {
+ return "readonly";
+ }
+ if (this.state.isActive) {
+ ret += "active ";
+ }
+ if (this.state.isHovered) {
+ ret += "hovered ";
+ }
+ if (this.state.isFocused) {
+ ret += "focused ";
+ }
+ return ret;
+ },
+
+ /**
+ * Renders the Text Input component.
+ *
+ **/
+ render: function() {
+ var input = [];
+ var label = [];
+ var input_style = {};
+ var input_state = this.setComponentState();
+
+ // The following if statements translates the min/max width from a number into a string.
+ if (this.props.minWidth) {
+ input_style['min-width'] = String(this.props.minWidth) + 'px';
+ }
+ if (this.props.maxWidth) {
+ input_style['max-width'] = String(this.props.maxWidth) + 'px';
+ }
+
+ //// The input element.
+ //input = React.createElement("input", {
+ // ref: "inputRef",
+ // "data-state": input_state,
+ // "type": "checkbox",
+ // placeholder: this.props.placeholder,
+ // pattern: this.props.pattern,
+ // maxLength: this.props.maxLength,
+ // required: this.state.isRequired,
+ // disabled: this.state.isDisabled,
+ // readOnly: this.state.isReadOnly,
+ // onChange: this.handleChange,
+ // onClick: this.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,
+ // className: (this.props.className || "rw-textinput"),
+ // tabIndex: 0
+ // },
+ // null
+ //);
+
+ label = React.createElement("label", null, this.props.label);
+
+ for (var i = 0; i < this.props.radiobuttons.length; i++) {
+
+ // Label for the whole radio button group
+ input[i] = React.createElement("label",
+ {
+ key:i*2 + 1,
+ readOnly:this.props.readonly,
+ disabled:this.props.disabled
+ },
+
+ // Creates each radio button input element.
+ React.createElement("input", {
+ key: i*2,
+ defaultChecked: this.props.radiobuttons[i].checked,
+ type: "radio",
+ name: this.props.name,
+ readOnly: this.props.readonly,
+ disabled: this.props.disabled,
+ onClick: this.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
+ },
+ null
+ ),
+ this.props.radiobuttons[i].label);
+ }
+
+ // The "if required" element. It displays a label if the element is required.
+ if(this.props.isRequired == true){
+ var requiredEle = React.createElement("small", {className: "rw-form-requiredLabel"}, this.state.requiredText);
+ }
+
+ // The "error" element. It pops up as a message if there is an error with the input.
+ if(this.state.errorText != "") {
+ var error = React.createElement("p", {className: "rw-form-errorMsg"}, this.state.errorText);
+ }
+
+ // The "instruction" element.
+ if(this.state.instructionsText != ""){
+ var instructions = React.createElement("p", {className: "rw-form-instructions"}, this.state.instructionsText)
+ }
+
+ // The parent element for all.
+ var ret = React.createElement("div", {
+ "data-state": input_state,
+ required: this.state.isRequired,
+ disabled: this.state.isDisabled,
+ readOnly: this.state.isReadOnly
+ }, requiredEle, label, input, error, instructions);
+
+ return ret;
+ }
+});
diff --git a/skyquake/framework/widgets/screen-loader/screenLoader.jsx b/skyquake/framework/widgets/screen-loader/screenLoader.jsx
new file mode 100644
index 0000000..eac33a9
--- /dev/null
+++ b/skyquake/framework/widgets/screen-loader/screenLoader.jsx
@@ -0,0 +1,46 @@
+
+/*
+ *
+ * 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 Loader from '../loading-indicator/loadingIndicator.jsx';
+import React from 'react';
+
+export default class ScreenLoader extends React.Component {
+ constructor(props) {
+ super(props)
+ }
+ render() {
+ let overlayStyle = {
+ position: 'fixed',
+ zIndex: 999,
+ width: '100%',
+ height: '100%',
+ top: 0,
+ // right: 0,
+ // bottom: 0,
+ left: 0,
+ display: this.props.show ? 'flex' : 'none',
+ justifyContent: 'center',
+ alignItems: 'center',
+ scroll: 'none',
+ backgroundColor: 'rgba(0,0,0,0.5)'
+ };
+ return (
+ <div style={overlayStyle}><Loader size="10"/></div>
+ );
+ }
+}
diff --git a/skyquake/framework/widgets/skyquake_container/eventCenter.jsx b/skyquake/framework/widgets/skyquake_container/eventCenter.jsx
new file mode 100644
index 0000000..75d2d52
--- /dev/null
+++ b/skyquake/framework/widgets/skyquake_container/eventCenter.jsx
@@ -0,0 +1,198 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+ /**
+ * EventCenter module to display a list of events from the system
+ * @module framework/widgets/skyquake_container/EventCenter
+ * @author Kiran Kashalkar <kiran.kashalkar@riftio.com>
+ *
+ */
+
+import React from 'react';
+import { Link } from 'react-router';
+import Utils from 'utils/utils.js';
+import Crouton from 'react-crouton';
+import TreeView from 'react-treeview';
+import _ from 'lodash';
+import '../../../node_modules/react-treeview/react-treeview.css';
+import './eventCenter.scss';
+
+export default class EventCenter extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {};
+ this.state.isOpen = false;
+ }
+
+ componentWillReceiveProps(props) {
+ let stateObject = {};
+
+ let notificationList = sessionStorage.getItem('notificationList');
+ let latestNotification = sessionStorage.getItem('latestNotification');
+
+ if (props.newNotificationEvent && props.newNotificationMsg) {
+ if (latestNotification) {
+ latestNotification = JSON.parse(latestNotification);
+ if (!_.isEqual(props.newNotificationMsg, latestNotification)) {
+ stateObject.newNotificationEvent = props.newNotificationEvent;
+ stateObject.newNotificationMsg = props.newNotificationMsg;
+ sessionStorage.setItem('latestNotification', JSON.stringify(stateObject.newNotificationMsg));
+ } else {
+ stateObject.newNotificationEvent = false;
+ stateObject.newNotificationMsg = null;
+ }
+ } else {
+ stateObject.newNotificationEvent = true;
+ stateObject.newNotificationMsg = props.newNotificationMsg;
+ sessionStorage.setItem('latestNotification', JSON.stringify(stateObject.newNotificationMsg));
+ }
+ } else {
+ stateObject.newNotificationEvent = false;
+ stateObject.newNotificationMsg = null;
+ }
+
+ if (notificationList) {
+ stateObject.notifications = _.merge(notificationList, props.notifications);
+ } else {
+ stateObject.notifications = props.notifications;
+ }
+ sessionStorage.setItem('notifications', JSON.stringify(stateObject.notifications));
+ this.setState(stateObject);
+ }
+
+ newNotificationReset = () => {
+ this.setState({
+ newNotificationEvent: false
+ });
+ }
+
+ onClickToggleOpenClose(event) {
+ this.props.onToggle();
+ this.setState({
+ isOpen: !this.state.isOpen
+ });
+ }
+ constructTree(details) {
+ let markup = [];
+ for (let key in details) {
+ if (typeof(details[key]) == "object") {
+ let html = (
+ <TreeView key={key} nodeLabel={key}>
+ {this.constructTree(details[key])}
+ </TreeView>
+ );
+ markup.push(html);
+ } else {
+ markup.push((<div key={key} className="info">{key} = {details[key].toString()}</div>));
+ }
+ }
+ return markup;
+ }
+
+ getNotificationFields(notification) {
+ let notificationFields = {};
+ if (notification) {
+
+ notificationFields.source = notification.source;
+ notificationFields.eventTime = notification.eventTime;
+
+ Object.keys(notification).map((notificationKey) => {
+ if (_.indexOf(['source', 'eventTime'], notificationKey) == -1) {
+ notificationFields.eventKey = notificationKey;
+ notificationFields.details = notification[notificationFields.eventKey];
+ }
+ });
+
+ }
+ return notificationFields;
+ }
+
+ render() {
+ let html;
+ let displayNotifications = [];
+ let displayNotificationsTableHead = null;
+
+ if (this.state.notifications && this.state.notifications.length > 0) {
+ displayNotificationsTableHead = (
+ <thead>
+ <tr key='header' className='header'>
+ <th className='source column'> Source Event Stream </th>
+ <th className='timestamp column'> Timestamp </th>
+ <th className='event column'> Event </th>
+ <th className='details column'> Details </th>
+ </tr>
+ </thead>
+ );
+ }
+
+ this.state.notifications && this.state.notifications.map((notification, notifIndex) => {
+ let notificationFields = {};
+
+ notificationFields = this.getNotificationFields(notification);
+
+ displayNotifications.push(
+ <tr key={notifIndex} className='notificationItem'>
+ <td className='source column'> {notificationFields.source} </td>
+ <td className='timestamp column'> {notificationFields.eventTime} </td>
+ <td className='event column'> {notificationFields.eventKey} </td>
+ <td className='details column'>
+ <TreeView key={notifIndex + '-detail'} nodeLabel='Event Details'>
+ {this.constructTree(notificationFields.details)}
+ </TreeView>
+ </td>
+ </tr>
+ );
+ });
+
+ let openedClassName = this.state.isOpen ? 'open' : 'closed';
+ html = (
+ <div className={'eventCenter ' + openedClassName}>
+
+ <div className='notification'>
+ <Crouton
+ id={Date.now()}
+ message={this.getNotificationFields(this.state.newNotificationMsg).eventKey +
+ ' notification received. Check Event Center for more details.'}
+ type={'info'}
+ hidden={!(this.state.newNotificationEvent && this.state.newNotificationMsg)}
+ onDismiss={this.newNotificationReset}
+ timeout={this.props.dismissTimeout}
+ />
+ </div>
+ <h1 className='title' data-open-close-icon={this.state.isOpen ? 'open' : 'closed'}
+ onClick={this.onClickToggleOpenClose.bind(this)}>
+ EVENT CENTER
+ </h1>
+ <div className='notificationListBody'>
+ <table className='notificationList'>
+ {displayNotificationsTableHead}
+ <tbody>
+ {displayNotifications}
+ </tbody>
+ </table>
+ </div>
+ </div>
+ );
+
+ return html;
+ }
+}
+
+EventCenter.defaultProps = {
+ dismissTimeout: 3000
+}
diff --git a/skyquake/framework/widgets/skyquake_container/eventCenter.scss b/skyquake/framework/widgets/skyquake_container/eventCenter.scss
new file mode 100644
index 0000000..103d416
--- /dev/null
+++ b/skyquake/framework/widgets/skyquake_container/eventCenter.scss
@@ -0,0 +1,152 @@
+
+/*
+ *
+ * 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';
+
+.eventCenter {
+ background-color: $white;
+ z-index: 9999;
+ position: relative;
+
+ [data-open-close-icon] {
+ &:after {
+ content: " ";
+ display: block;
+ float: right;
+ margin-right: 8px;
+ height: 51px;
+ width: 51px;
+ opacity: 0.25;
+ background-size: 20px 20px;
+ background-repeat: no-repeat;
+ background-position: center center;
+ /* background-image required in order for background-size to work*/
+ background-image: url(../../../node_modules/open-iconic/svg/caret-bottom.svg);
+ vertical-align: middle;
+ }
+ &:hover:after {
+ opacity: 1;
+ }
+ }
+ [data-open-close-icon="closed"]:after {
+ transform: rotate(180deg);
+ transition: all 300ms cubic-bezier(0.77, 0, 0.175, 1);
+ }
+ [data-open-close-icon="open"]:after {
+ transform: rotate(0deg);
+ transition: all 300ms cubic-bezier(0.77, 0, 0.175, 1);
+ }
+
+
+ .notification {
+ .crouton {
+ right: 4px;
+ top: 34px;
+ width: 30%;
+ left: auto;
+ .info {
+ border-radius: 25px;
+ }
+ }
+ }
+ &.-with-transitions {
+ transition: height 300ms cubic-bezier(0.230, 1.000, 0.320, 1.000);
+ }
+ &.open {
+ .notificationListBody {
+ height: 300px;
+ }
+ }
+ &.closed {
+ .notificationListBody {
+ height: 0px;
+ }
+ }
+ .title {
+ border-top: 1px solid $gray-dark;
+ border-bottom: 1px solid $gray-dark;
+ cursor: pointer;
+ color: $gray-darker;
+ font-style: normal;
+ font-size: 1.625rem;
+ font-weight: 400;
+ height: 51px;
+ line-height: 51px;
+ margin: 0;
+ padding-left: 0;
+ padding-right: 0;
+ text-align: center;
+ text-transform: uppercase;
+ background-color: $gray-lighter;
+ }
+ .notificationListBody {
+ overflow: auto;
+ .notificationList {
+ width: 100%;
+ &:hover {
+ overflow: auto;
+ }
+
+ .notificationItem {
+
+ }
+
+ th {
+ text-align: left;
+ padding: 0.5rem;
+ background: $gray-darker;
+ border: 1px solid black;
+ color: $white;
+ &.source {
+ width: 20%;
+ }
+ &.timestamp {
+ width: 20%;
+ }
+ &.event {
+ width: 20%;
+ }
+ &.details {
+ width: 40%;
+ }
+ }
+
+ tr {
+ padding: 0.5rem;
+ border: 1px solid black;
+
+ &:nth-child(odd) {
+ background: $white;
+ td {
+ border: 1px solid black;
+ padding: 0.5rem;
+ }
+ }
+
+ &:nth-child(even) {
+ background: $gray-lighter;
+ td {
+ border: 1px solid black;
+ padding: 0.5rem;
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/skyquake/framework/widgets/skyquake_container/skyquakeAltInstance.js b/skyquake/framework/widgets/skyquake_container/skyquakeAltInstance.js
new file mode 100644
index 0000000..0b7876e
--- /dev/null
+++ b/skyquake/framework/widgets/skyquake_container/skyquakeAltInstance.js
@@ -0,0 +1,27 @@
+/*
+ *
+ * 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.
+ *
+ */
+//Alt Instance.
+import Alt from 'alt';
+var path = require('path');
+if (!window.A) {
+ window.A = new Alt();
+ Alt.debug('alt', window.A);
+ console.log('new alt', path.resolve(__dirname));
+}
+
+module.exports = window.A;
diff --git a/skyquake/framework/widgets/skyquake_container/skyquakeApp.scss b/skyquake/framework/widgets/skyquake_container/skyquakeApp.scss
new file mode 100644
index 0000000..4f77d71
--- /dev/null
+++ b/skyquake/framework/widgets/skyquake_container/skyquakeApp.scss
@@ -0,0 +1,143 @@
+//import a reset
+@import '../../style/_colors.scss';
+html, body {
+ height:100%;
+ width:100%;
+}
+
+
+.skyquakeApp {
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+ background: $gray-lightest;
+ h1 {
+ text-transform: uppercase;
+ }
+ .active {
+ background-color: $brand-blue!important;
+ border-color: $brand-blue!important;
+ color: #fff!important
+ }
+ .skyquakeNav {
+ display: flex;
+ color:white;
+ background:black;
+ position:relative;
+ z-index: 10;
+ font-size:0.75rem;
+ .secondaryNav {
+ flex: 1 1 auto;
+ display: flex;
+ justify-content: flex-end;
+ }
+ .app {
+ position:relative;
+ h2 {
+ font-size:0.75rem;
+ border-right: 1px solid black;
+ display: flex;
+ align-items: center;
+ .oi {
+ padding-right: 0.5rem;
+ }
+ }
+ .menu {
+ position:absolute;
+ display:none;
+ z-index:2;
+ width: 100%;
+ }
+ &:first-child{
+ h2 {
+ border-left: 1px solid black;
+ }
+ }
+ &:hover {
+ a {
+ color:$brand-blue-light;
+ cursor:pointer;
+ }
+ .menu {
+ display:block;
+ background:black;
+ a {
+ color:white;
+ }
+ li:hover {
+ a {
+ color:$brand-blue-light;
+ }
+ }
+ }
+ }
+ &.active {
+ color:white;
+ background:black;
+ a {
+ color:white;
+ }
+ }
+ }
+ a{
+ display:block;
+ padding:0.5rem 1rem;
+ text-decoration:none;
+ text-transform:uppercase;
+ color:white;
+ }
+ &:before {
+ content: '';
+ height:1.85rem;
+ width:2.5rem;
+ margin:0 1rem;
+ padding:0 0.125rem;
+ // background: url('../../style/img/header-logo.png') no-repeat center center white;
+ background: url('../../style/img/svg/riftio_logo_white.svg') no-repeat center center;
+ background-size:65%;
+ }
+ }
+ .skyquakeContainerWrapper {
+ }
+ .titleBar {
+ padding: 1rem 0 0;
+ h1 {
+ // background: url('../../style/img/header-logo.png') no-repeat;
+ background-size:contain;
+ height: 51px;
+ line-height: 51px;
+ margin-left: 20px;
+ // padding-left: 100px;
+ left: 0;
+ font-size: 1.625rem;
+ font-weight: 400;
+ text-align:left;
+ position:relative;
+ flex: 1 0 auto;
+ }
+ }
+ .corner-accent {
+ border: 1px solid #000;
+ display: block;
+ height: 4px;
+ position: absolute;
+ width: 4px;
+
+ &.top {
+ border-bottom: 0;
+ top: -1px;
+ }
+ &.right {
+ border-left: 0;
+ right: -1px;
+ }
+ &.bottom {
+ border-top: 0;
+ bottom: -1px;
+ }
+ &.left {
+ border-right: 0;
+ left: -1px;
+ }
+ }
+}
diff --git a/skyquake/framework/widgets/skyquake_container/skyquakeComponent.jsx b/skyquake/framework/widgets/skyquake_container/skyquakeComponent.jsx
new file mode 100644
index 0000000..893a4c1
--- /dev/null
+++ b/skyquake/framework/widgets/skyquake_container/skyquakeComponent.jsx
@@ -0,0 +1,18 @@
+import React from 'react';
+export default function(Component) {
+ class SkyquakeComponent extends React.Component {
+ constructor(props, context) {
+ super(props, context);
+ this.router = context.router;
+ this.actions = context.flux.actions.global;
+ }
+ render(props) {
+ return <Component {...this.props} router={this.router} actions={this.actions} flux={this.context.flux} />
+ }
+ }
+ SkyquakeComponent.contextTypes = {
+ router: React.PropTypes.object,
+ flux: React.PropTypes.object
+ };
+ return SkyquakeComponent;
+}
diff --git a/skyquake/framework/widgets/skyquake_container/skyquakeContainer.jsx b/skyquake/framework/widgets/skyquake_container/skyquakeContainer.jsx
new file mode 100644
index 0000000..eca9413
--- /dev/null
+++ b/skyquake/framework/widgets/skyquake_container/skyquakeContainer.jsx
@@ -0,0 +1,143 @@
+/*
+ *
+ * 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 AltContainer from 'alt-container';
+import Alt from './skyquakeAltInstance.js';
+import SkyquakeNav from './skyquakeNav.jsx';
+import EventCenter from './eventCenter.jsx';
+import SkyquakeContainerActions from './skyquakeContainerActions.js'
+import SkyquakeContainerStore from './skyquakeContainerStore.js';
+import Breadcrumbs from 'react-breadcrumbs';
+import Utils from 'utils/utils.js';
+import _ from 'lodash';
+import Crouton from 'react-crouton';
+import ScreenLoader from 'widgets/screen-loader/screenLoader.jsx';
+import './skyquakeApp.scss';
+// import 'style/reset.css';
+import 'style/core.css';
+export default class skyquakeContainer extends React.Component {
+ constructor(props) {
+ super(props);
+ let self = this;
+ this.state = SkyquakeContainerStore.getState();
+ //This will be populated via a store/source
+ this.state.nav = this.props.nav || [];
+ this.state.eventCenterIsOpen = false;
+ this.state.currentPlugin = SkyquakeContainerStore.currentPlugin;
+ }
+
+ componentWillMount() {
+ let self = this;
+
+ Utils.bootstrapApplication().then(function() {
+ SkyquakeContainerStore.listen(self.listener);
+ SkyquakeContainerStore.getNav();
+ SkyquakeContainerStore.getEventStreams();
+ });
+
+ // Load multiplex-client
+ const script = document.createElement("script");
+
+ script.src = "/multiplex-client";
+ script.async = true;
+
+ document.body.appendChild(script);
+
+ Utils.setupMultiplexClient();
+ }
+
+ componentWillUnmount() {
+ SkyquakeContainerStore.unlisten(this.listener);
+ }
+ listener = (state) => {
+ this.setState(state);
+ }
+ matchesLoginUrl() {
+ //console.log("window.location.hash=", window.location.hash);
+ // First element in the results of match will be the part of the string
+ // that matched the expression. this string should be true against
+ // (window.location.hash.match(re)[0]).startsWith(Utils.loginHash)
+ var re = /#\/login?(\?|\/|$)/i;
+ //console.log("res=", window.location.hash.match(re));
+ return (window.location.hash.match(re)) ? true : false;
+ }
+ onToggle = (isOpen) => {
+ this.setState({
+ eventCenterIsOpen: isOpen
+ });
+ }
+
+ render() {
+ const {displayNotification, notificationMessage, displayScreenLoader, ...state} = this.state;
+ var html;
+
+ if (this.matchesLoginUrl()) {
+ html = (
+ <AltContainer>
+ <div className="skyquakeApp">
+ {this.props.children}
+ </div>
+ </AltContainer>
+ );
+ } else {
+ let tag = this.props.routes[this.props.routes.length-1].name ? ': '
+ + this.props.routes[this.props.routes.length-1].name : '';
+ let routeName = this.props.location.pathname.split('/')[1];
+ html = (
+ <AltContainer flux={Alt}>
+ <div className="skyquakeApp wrap">
+ <Crouton
+ id={Date.now()}
+ message={notificationMessage}
+ type={"error"}
+ hidden={!(displayNotification && notificationMessage)}
+ onDismiss={SkyquakeContainerActions.hideNotification}
+ />
+ <ScreenLoader show={displayScreenLoader}/>
+ <SkyquakeNav nav={this.state.nav}
+ currentPlugin={this.state.currentPlugin}
+ store={SkyquakeContainerStore} />
+ <div className="titleBar">
+ <h1>{this.state.currentPlugin + tag}</h1>
+ </div>
+ <div className={"application " + routeName}>
+ {this.props.children}
+ </div>
+ <EventCenter className="eventCenter"
+ notifications={this.state.notifications}
+ newNotificationEvent={this.state.newNotificationEvent}
+ newNotificationMsg={this.state.newNotificationMsg}
+ onToggle={this.onToggle} />
+ </div>
+ </AltContainer>
+ );
+ }
+ return html;
+ }
+}
+skyquakeContainer.contextTypes = {
+ router: React.PropTypes.object
+ };
+
+/*
+<Breadcrumbs
+ routes={this.props.routes}
+ params={this.props.params}
+ separator=" | "
+ />
+ */
diff --git a/skyquake/framework/widgets/skyquake_container/skyquakeContainerActions.js b/skyquake/framework/widgets/skyquake_container/skyquakeContainerActions.js
new file mode 100644
index 0000000..773978b
--- /dev/null
+++ b/skyquake/framework/widgets/skyquake_container/skyquakeContainerActions.js
@@ -0,0 +1,33 @@
+/*
+ *
+ * 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 Alt from './skyquakeAltInstance.js';
+export default Alt.generateActions(
+ 'getSkyquakeNavSuccess',
+ 'openNotificationsSocketLoading',
+ 'openNotificationsSocketSuccess',
+ 'openNotificationsSocketError',
+ 'getEventStreamsLoading',
+ 'getEventStreamsSuccess',
+ 'getEventStreamsError',
+ //Notifications
+ 'showNotification',
+ 'hideNotification',
+ //Screen Loader
+ 'showScreenLoader',
+ 'hideScreenLoader'
+);
diff --git a/skyquake/framework/widgets/skyquake_container/skyquakeContainerSource.js b/skyquake/framework/widgets/skyquake_container/skyquakeContainerSource.js
new file mode 100644
index 0000000..2ee50c0
--- /dev/null
+++ b/skyquake/framework/widgets/skyquake_container/skyquakeContainerSource.js
@@ -0,0 +1,117 @@
+/*
+ *
+ * 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 Alt from './skyquakeAltInstance.js';
+import $ from 'jquery';
+import SkyquakeContainerActions from './skyquakeContainerActions'
+
+let Utils = require('utils/utils.js');
+let API_SERVER = require('utils/rw.js').getSearchParams(window.location).api_server;
+let HOST = API_SERVER;
+let NODE_PORT = require('utils/rw.js').getSearchParams(window.location).api_port || ((window.location.protocol == 'https:') ? 8443 : 8000);
+let DEV_MODE = require('utils/rw.js').getSearchParams(window.location).dev_mode || false;
+let RW_REST_API_PORT = require('utils/rw.js').getSearchParams(window.location).rw_rest_api_port || 8008;
+
+if (DEV_MODE) {
+ HOST = window.location.protocol + '//' + window.location.hostname;
+}
+
+export default {
+ getNav() {
+ return {
+ remote: function() {
+ return new Promise(function(resolve, reject) {
+ $.ajax({
+ url: '/nav',
+ type: 'GET',
+ // beforeSend: Utils.addAuthorizationStub,
+ success: function(data) {
+ resolve(data);
+ }
+ })
+ })
+ },
+ success: SkyquakeContainerActions.getSkyquakeNavSuccess
+ }
+ },
+
+ getEventStreams() {
+ return {
+ remote: function(state, recordID) {
+ return new Promise(function(resolve, reject) {
+ $.ajax({
+ url: '//' + window.location.hostname + ':' + NODE_PORT + '/api/operational/restconf-state/streams?api_server=' + API_SERVER,
+ type: 'GET',
+ beforeSend: Utils.addAuthorizationStub,
+ success: function(data) {
+ resolve(data);
+ }
+ }).fail(function(xhr) {
+ //Authentication and the handling of fail states should be wrapped up into a connection class.
+ Utils.checkAuthentication(xhr.status);
+ });;
+ });
+ },
+ loading: SkyquakeContainerActions.getEventStreamsLoading,
+ success: SkyquakeContainerActions.getEventStreamsSuccess,
+ error: SkyquakeContainerActions.getEventStreamsError
+ }
+ },
+
+ openNotificationsSocket() {
+ return {
+ remote: function(state, location, streamSource) {
+ return new Promise((resolve, reject) => {
+ $.ajax({
+ url: '//' + window.location.hostname + ':' + NODE_PORT + '/socket-polling?api_server=' + API_SERVER,
+ type: 'POST',
+ beforeSend: Utils.addAuthorizationStub,
+ data: {
+ url: location
+ },
+ success: (data) => {
+ // var url = Utils.webSocketProtocol() + '//' + window.location.hostname + ':' + data.port + data.socketPath;
+ // var ws = new WebSocket(url);
+ // resolve({
+ // ws: ws,
+ // streamSource: streamSource
+ // });
+ const checker = () => {
+ if (!Utils.isMultiplexerLoaded()) {
+ setTimeout(() => {
+ checker();
+ }, 500);
+ } else {
+ resolve({
+ connection: data.id,
+ streamSource: streamSource
+ });
+ }
+ };
+
+ checker();
+ }
+ });
+ });
+ },
+ loading: SkyquakeContainerActions.openNotificationsSocketLoading,
+ success: SkyquakeContainerActions.openNotificationsSocketSuccess,
+ error: SkyquakeContainerActions.openNotificationsSocketError
+ }
+ }
+}
+
diff --git a/skyquake/framework/widgets/skyquake_container/skyquakeContainerStore.js b/skyquake/framework/widgets/skyquake_container/skyquakeContainerStore.js
new file mode 100644
index 0000000..fe4a7b0
--- /dev/null
+++ b/skyquake/framework/widgets/skyquake_container/skyquakeContainerStore.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.
+ *
+ */
+//This will reach out to the global routes endpoint
+
+import Alt from './skyquakeAltInstance.js';
+import SkyquakeContainerSource from './skyquakeContainerSource.js';
+import SkyquakeContainerActions from './skyquakeContainerActions';
+import _ from 'lodash';
+//Temporary, until api server is on same port as webserver
+var rw = require('utils/rw.js');
+var API_SERVER = rw.getSearchParams(window.location).api_server;
+var UPLOAD_SERVER = rw.getSearchParams(window.location).upload_server;
+
+class SkyquakeContainerStore {
+ constructor() {
+ this.currentPlugin = getCurrentPlugin();
+ this.nav = {};
+ this.notifications = [];
+ this.socket = null;
+ //Notification defaults
+ this.notificationMessage = '';
+ this.displayNotification = false;
+ //Screen Loader default
+ this.displayScreenLoader = false;
+ this.bindActions(SkyquakeContainerActions);
+ this.exportAsync(SkyquakeContainerSource);
+
+
+ this.exportPublicMethods({
+ // getNav: this.getNav
+ });
+
+ }
+ getSkyquakeNavSuccess = (data) => {
+ var self = this;
+ this.setState({
+ nav: decorateAndTransformNav(data, self.currentPlugin)
+ })
+ }
+
+ closeSocket = () => {
+ if (this.socket) {
+ window.multiplexer.channel(this.channelId).close();
+ }
+ this.setState({
+ socket: null
+ });
+ }
+ //Remove once logging plugin is implemented
+ getSysLogViewerURLSuccess(data){
+ window.open(data.url);
+ }
+ getSysLogViewerURLError(data){
+ console.log('failed', data)
+ }
+
+ openNotificationsSocketLoading = () => {
+ this.setState({
+ isLoading: true
+ })
+ }
+
+ openNotificationsSocketSuccess = (data) => {
+ var self = this;
+
+ let connection = data.connection;
+ let streamSource = data.streamSource;
+ console.log('Success opening notification socket for stream ', streamSource);
+
+ let ws = window.multiplexer.channel(connection);
+
+ if (!connection) return;
+ self.setState({
+ socket: ws.ws,
+ isLoading: false,
+ channelId: connection
+ });
+
+ ws.onmessage = (socket) => {
+ try {
+ var data = JSON.parse(socket.data);
+ if (!data.notification) {
+ console.warn('No notification in the received payload: ', data);
+ } else {
+ // Temp to test before adding multi-sources
+ data.notification.source = streamSource;
+ if (_.indexOf(self.notifications, data.notification) == -1) {
+ // newly appreared event.
+ // Add to the notifications list and setState
+ self.notifications.unshift(data.notification);
+ self.setState({
+ newNotificationEvent: true,
+ newNotificationMsg: data.notification,
+ notifications: self.notifications,
+ isLoading: false
+ });
+ }
+ }
+ } catch(e) {
+ console.log('Error in parsing data on notification socket');
+ }
+ };
+
+ ws.onclose = () => {
+ self.closeSocket();
+ };
+ }
+
+ openNotificationsSocketError = (data) => {
+ console.log('Error opening notification socket', data);
+ }
+
+ getEventStreamsLoading = () => {
+ this.setState({
+ isLoading: true
+ });
+ }
+
+ getEventStreamsSuccess = (streams) => {
+ console.log('Found streams: ', streams);
+ let self = this;
+
+ streams['ietf-restconf-monitoring:streams'] &&
+ streams['ietf-restconf-monitoring:streams']['stream'] &&
+ streams['ietf-restconf-monitoring:streams']['stream'].map((stream) => {
+ stream['access'] && stream['access'].map((streamLocation) => {
+ if (streamLocation['encoding'] == 'ws_json') {
+ setTimeout(() => {
+ self.getInstance().openNotificationsSocket(streamLocation['location'], stream['name']);
+ }, 0);
+ }
+ })
+ })
+
+ this.setState({
+ isLoading: true,
+ streams: streams
+ })
+ }
+
+ getEventStreamsError = (error) => {
+ console.log('Failed to get streams object');
+ this.setState({
+ isLoading: false
+ })
+ }
+
+ //Notifications
+ showNotification = (data) => {
+ if(typeof(data) == 'string') {
+ this.setState({
+ displayNotification: true,
+ notificationMessage: data
+ });
+ } else {
+ if(data.type == 'error') {
+ this.setState({
+ displayNotification: true,
+ notificationMessage: data.msg,
+ displayScreenLoader: false
+ });
+ }
+ }
+ }
+ hideNotification = () => {
+ this.setState({
+ displayNotification: false
+ })
+ }
+ //ScreenLoader
+ showScreenLoader = () => {
+ this.setState({
+ displayScreenLoader: true
+ });
+ }
+ hideScreenLoader = () => {
+ this.setState({
+ displayScreenLoader: false
+ })
+ }
+
+}
+
+/**
+ * Receives nav data from routes rest endpoint and decorates the data with internal/external linking information
+ * @param {object} nav Nav item from /nav endoingpoint
+ * @param {string} currentPlugin Current plugin name taken from url path.
+ * @return {array} Returns list of constructed nav items.
+ */
+function decorateAndTransformNav(nav, currentPlugin) {
+ for ( let k in nav) {
+ nav[k].pluginName = k;
+ if (k != currentPlugin) {
+ nav[k].routes.map(function(route, i) {
+ if (API_SERVER) {
+ route.route = '/' + k + '/index.html?api_server=' + API_SERVER + '&upload_server=' + UPLOAD_SERVER + '#' + route.route;
+ } else {
+ route.route = '/' + k + '/#' + route.route;
+ }
+ route.isExternal = true;
+ })
+ }
+ }
+ return nav;
+}
+
+function getCurrentPlugin() {
+ var paths = window.location.pathname.split('/');
+ var currentPath = null;
+ if (paths[0] != "") {
+ currentPath = paths[0]
+ } else {
+ currentPath = paths[1];
+ }
+ if (currentPath != null) {
+ return currentPath;
+ } else {
+ console.error('Well, something went horribly wrong with discovering the current plugin name - perhaps you should consider moving this logic to the server?')
+ }
+}
+
+export default Alt.createStore(SkyquakeContainerStore);
diff --git a/skyquake/framework/widgets/skyquake_container/skyquakeNav.jsx b/skyquake/framework/widgets/skyquake_container/skyquakeNav.jsx
new file mode 100644
index 0000000..73a2f02
--- /dev/null
+++ b/skyquake/framework/widgets/skyquake_container/skyquakeNav.jsx
@@ -0,0 +1,226 @@
+/*
+ *
+ * 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 { Link } from 'react-router';
+import Utils from 'utils/utils.js';
+import Crouton from 'react-crouton';
+import 'style/common.scss';
+
+//Temporary, until api server is on same port as webserver
+var rw = require('utils/rw.js');
+var API_SERVER = rw.getSearchParams(window.location).api_server;
+var UPLOAD_SERVER = rw.getSearchParams(window.location).upload_server;
+
+//
+// Internal classes/functions
+//
+
+class LogoutAppMenuItem extends React.Component {
+ handleLogout() {
+ Utils.clearAuthentication();
+ }
+ render() {
+ return (
+ <div className="app">
+ <h2>
+ <a onClick={this.handleLogout}>
+ Logout
+ </a>
+ </h2>
+ </div>
+ );
+ }
+}
+
+
+//
+// Exported classes and functions
+//
+
+//
+/**
+ * Skyquake Nav Component. Provides navigation functionality between all plugins
+ */
+export default class skyquakeNav extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {};
+ this.state.validateErrorEvent = 0;
+ this.state.validateErrorMsg = '';
+ }
+ validateError = (msg) => {
+ this.setState({
+ validateErrorEvent: true,
+ validateErrorMsg: msg
+ });
+ }
+ validateReset = () => {
+ this.setState({
+ validateErrorEvent: false
+ });
+ }
+ returnCrouton = () => {
+ return <Crouton
+ id={Date.now()}
+ message={this.state.validateErrorMsg}
+ type={"error"}
+ hidden={!(this.state.validateErrorEvent && this.state.validateErrorMsg)}
+ onDismiss={this.validateReset}
+ />;
+ }
+ render() {
+ let html;
+ html = (
+ <div>
+ {this.returnCrouton()}
+ <nav className="skyquakeNav">
+ {buildNav.call(this, this.props.nav, this.props.currentPlugin)}
+ </nav>
+
+ </div>
+ )
+ return html;
+ }
+}
+skyquakeNav.defaultProps = {
+ nav: {}
+}
+/**
+ * Returns a React Component
+ * @param {object} link Information about the nav link
+ * @param {string} link.route Hash route that the SPA should resolve
+ * @param {string} link.name Link name to be displayed
+ * @param {number} index index of current array item
+ * @return {object} component A React LI Component
+ */
+//This should be extended to also make use of internal/external links and determine if the link should refer to an outside plugin or itself.
+export function buildNavListItem (k, link, index) {
+ let html = false;
+ if (link.type == 'external') {
+ this.hasSubNav[k] = true;
+ html = (
+ <li key={index}>
+ {returnLinkItem(link)}
+ </li>
+ );
+ }
+ return html;
+}
+
+/**
+ * Builds a link to a React Router route or a new plugin route.
+ * @param {object} link Routing information from nav object.
+ * @return {object} component returns a react component that links to a new route.
+ */
+export function returnLinkItem(link) {
+ let ref;
+ let route = link.route;
+ if(link.isExternal) {
+ ref = (
+ <a href={route}>{link.label}</a>
+ )
+ } else {
+ if(link.path && link.path.replace(' ', '') != '') {
+ route = link.path;
+ }
+ if(link.query) {
+ let query = {};
+ query[link.query] = '';
+ route = {
+ pathname: route,
+ query: query
+ }
+ }
+ ref = (
+ <Link to={route}>
+ {link.label}
+ </Link>
+ )
+ }
+ return ref;
+}
+
+/**
+ * Constructs nav for each plugin, along with available subnavs
+ * @param {array} nav List returned from /nav endpoint.
+ * @return {array} List of constructed nav element for each plugin
+ */
+export function buildNav(nav, currentPlugin) {
+ let navList = [];
+ let navListHTML = [];
+ let secondaryNav = [];
+ let self = this;
+ self.hasSubNav = {};
+ let secondaryNavHTML = (
+ <div className="secondaryNav" key="secondaryNav">
+ {secondaryNav}
+ <LogoutAppMenuItem />
+ </div>
+ )
+ for (let k in nav) {
+ if (nav.hasOwnProperty(k)) {
+ self.hasSubNav[k] = false;
+ let header = null;
+ let navClass = "app";
+ let routes = nav[k].routes;
+ let navItem = {};
+ //Primary plugin title and link to dashboard.
+ let route;
+ let NavList;
+ if (API_SERVER) {
+ route = routes[0].isExternal ? '/' + k + '/index.html?api_server=' + API_SERVER + '' + '&upload_server=' + UPLOAD_SERVER + '' : '';
+ } else {
+ route = routes[0].isExternal ? '/' + k + '/' : '';
+ }
+ let dashboardLink = returnLinkItem({
+ isExternal: routes[0].isExternal,
+ pluginName: nav[k].pluginName,
+ label: nav[k].label || k,
+ route: route
+ });
+ if (nav[k].pluginName == currentPlugin) {
+ navClass += " active";
+ }
+ NavList = nav[k].routes.map(buildNavListItem.bind(self, k));
+ navItem.priority = nav[k].priority;
+ navItem.order = nav[k].order;
+ navItem.html = (
+ <div key={k} className={navClass}>
+ <h2>{dashboardLink} {self.hasSubNav[k] ? <span className="oi" data-glyph="caret-bottom"></span> : ''}</h2>
+ <ul className="menu">
+ {
+ NavList
+ }
+ </ul>
+ </div>
+ );
+ navList.push(navItem)
+ }
+ }
+ //Sorts nav items by order and returns only the markup
+ navListHTML = navList.sort((a,b) => a.order - b.order).map(function(n) {
+ if((n.priority < 2)){
+ return n.html;
+ } else {
+ secondaryNav.push(n.html);
+ }
+ });
+ navListHTML.push(secondaryNavHTML);
+ return navListHTML;
+}
diff --git a/skyquake/framework/widgets/skyquake_container/skyquakeRouter.jsx b/skyquake/framework/widgets/skyquake_container/skyquakeRouter.jsx
new file mode 100644
index 0000000..fc3231d
--- /dev/null
+++ b/skyquake/framework/widgets/skyquake_container/skyquakeRouter.jsx
@@ -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.
+ *
+ */
+import React from 'react';
+import {
+ Router, Route, hashHistory, IndexRoute
+}
+from 'react-router';
+import SkyquakeContainer from 'widgets/skyquake_container/skyquakeContainer.jsx';
+
+export default function(config, context) {
+ let routes = [];
+ let index = null;
+ let components = null;
+ if (config && config.routes) {
+ routes = buildRoutes(config.routes)
+ function buildRoutes(routes) {
+ return routes.map(function(route, index) {
+ let routeConfig = {};
+ if (route.route && route.component) {
+ // ES6 modules need to specify default
+ let path = route.route;
+ if(route.path && route.path.replace(' ', '') != '') {
+ path = route.path
+ }
+ routeConfig = {
+ path: path,
+ name: route.label,
+ getComponent: function(location, cb) {
+ require.ensure([], (require) => {
+ cb(null, context(route.component).default)
+ })
+ }
+ }
+ if(route.routes && (route.routes.length > 0)){
+ routeConfig.childRoutes = buildRoutes(route.routes)
+ }
+ } else {
+ console.error('Route not properly configured. Check that both path and component are specified');
+ }
+ return routeConfig;
+ });
+ }
+ routes.push({
+ path: '/login',
+ name: 'Login',
+ component: require('../login/login.jsx').default
+ });
+ routes.push({
+ path:'*',
+ name: 'Dashboard',
+ getComponent: function(loc, cb) {
+ cb(null, context(config.dashboard).default);
+ }
+ });
+ if (config.dashboard) {
+ // ES6 modules need to specify default
+ index = <IndexRoute component={context(config.dashboard).default} />;
+ } else {
+ index = DefaultDashboard
+ }
+
+ const rootRoute = {
+ component: SkyquakeContainer,
+ path: '/',
+ name: 'Dashboard',
+ indexRoute: {
+ component: (config.dashboard) ? context(config.dashboard).default : DefaultDashboard
+ },
+ childRoutes: routes
+ }
+
+ return((
+ <Router history={hashHistory} routes={rootRoute}>
+ </Router>
+ ))
+ } else {
+ console.error('There are no routes configured in the config.json file');
+ }
+}
+
+//When no default dashboard is specified in the plugin_config.json, use this component.
+class DefaultDashboard extends React.Component {
+ constructor(props) {
+ super(props)
+ }
+ render() {
+ let html;
+ html = <div> This is a default dashboard page component </div>;
+ return html;
+ }
+}
diff --git a/skyquake/framework/widgets/sq-input-range-slider/sq-input-range-slider.jsx b/skyquake/framework/widgets/sq-input-range-slider/sq-input-range-slider.jsx
new file mode 100644
index 0000000..6a0bb97
--- /dev/null
+++ b/skyquake/framework/widgets/sq-input-range-slider/sq-input-range-slider.jsx
@@ -0,0 +1,84 @@
+import React from 'react';
+import './sq-input-range-slider.scss';
+
+export default class SqInputRangeSlider extends React.Component {
+ constructor(props){
+ super(props);
+ this.state = {};
+ this.state.position = {
+ left: 50
+ };
+ this.className = "SqRangeInput";
+ }
+
+ updateHandle = (e) => {
+ this.props.onChange(e.target.valueAsNumber);
+ }
+
+ render() {
+ const {vertical, className, min, max, step, width, disabled, ...props} = this.props;
+ let class_name = this.className;
+ let dataListHTML = null;
+ let dataListOptions = null;
+ let dataListID = null;
+ let inputStyle = {};
+ let inputContainer = {
+ };
+ let style = {
+ width: width + 'px'
+ }
+ if(vertical) {
+ class_name += ' ' + this.className + '--vertical';
+ style.position = 'absolute';
+ style.left = (width * -1 + 32) + 'px';
+ style.width = width + 'px';
+ inputContainer.margin = '0 10px';
+ inputContainer.width = '70px';
+ inputContainer.height = (width + 40) + 'px';
+ inputContainer.background = 'white';
+ inputContainer.border = '1px solid #ccc';
+ } else {
+ class_name += ' ' + this.className + '--horizontal';
+ // style.position = 'absolute';
+ // style.top = 12 + 'px';
+ inputContainer.margin = '10px 0px';
+ inputContainer.height = '70px';
+ inputContainer.width = (width + 40) + 'px';
+ inputContainer.background = 'white';
+ inputContainer.border = '1px solid #ccc';
+ }
+ return (
+ <div className={this.className + '--container'} style={inputContainer}>
+ <div className={class_name + ' ' + className + (disabled ? ' is-disabled' : '')}>
+ <input
+ style={style}
+ orient= {vertical ? "vertical" : "horizontal"}
+ type="range"
+ onChange={this.updateHandle}
+ ref="range"
+ max={max}
+ min={min}
+ step={step}
+ disabled={disabled}
+ />
+ </div>
+ </div>
+ )
+ }
+}
+
+SqInputRangeSlider.defaultProps = {
+ //Horizontal vs Vertical Slider
+ vertical: false,
+ //Override classes
+ className: '',
+ min:0,
+ max:100,
+ step: 1,
+ //width/length in px
+ width:100,
+ disabled: false,
+ onChange: function(value) {
+ console.log(value);
+ }
+}
diff --git a/skyquake/framework/widgets/sq-input-range-slider/sq-input-range-slider.scss b/skyquake/framework/widgets/sq-input-range-slider/sq-input-range-slider.scss
new file mode 100644
index 0000000..b09dd16
--- /dev/null
+++ b/skyquake/framework/widgets/sq-input-range-slider/sq-input-range-slider.scss
@@ -0,0 +1,176 @@
+@import '../../style/_colors.scss';
+$slider-background: rgba(19, 82, 150, 0.2);
+$disabled-slider-background: $dark-gray;
+//Generates ticks on bar. Not currently used
+$slider-background-ticks: repeating-linear-gradient(to right, $slider-background, $slider-background 0%, #fff 0%, #fff 1%, $slider-background 1%, $slider-background 25%);
+$thumb-color: rgba(0, 119, 200, 1);
+$disabled-thumb-color: $dark-gray;
+$track-shadow: 0px 0px 0px #000000, 0px 0px 0px #0d0d0d;
+$thumb-height: 50;
+$track-height: 3;
+$input-margin: 20;
+$-webkit-handle-offset: ($track-height - $thumb-height) / 2;
+
+//(thH-ttW) / 2
+
+@mixin thumbStyling {
+ box-shadow: $track-shadow;
+ border: 0px solid #000000;
+ background: $thumb-color;
+ box-shadow: 2px 2px 2px 0px #ccc;
+ height: $thumb-height + px;
+ width: 20px;
+ cursor: pointer;
+}
+@mixin inputTrack {
+ width: 100%;
+ height: $track-height + px;
+ cursor: pointer;
+ animate: 0.2s;
+}
+@mixin infoBase {
+ display: flex;
+ justify-content: space-between;
+}
+@mixin sliderBase {
+ input[type=range] {
+ position:absolute;
+ -webkit-appearance: none;
+ margin: $input-margin + px 0;
+ width: 100%;
+ &:focus {
+ outline: none;
+ }
+ &::-webkit-slider-thumb {
+ @include thumbStyling;
+ -webkit-appearance: none;
+ margin-top: $-webkit-handle-offset + px;
+
+ }
+ &::-moz-range-thumb {
+ @include thumbStyling;
+ }
+ &::-ms-thumb {
+ @include thumbStyling;
+ }
+ &::-webkit-slider-runnable-track {
+ @include inputTrack;
+ box-shadow: $track-shadow;
+ background: $slider-background;
+ border: 0px solid #000101;
+ }
+ &:focus::-webkit-slider-runnable-track {
+ background: $slider-background;
+ }
+ &::-moz-range-track {
+ @include inputTrack;
+ box-shadow: $track-shadow;
+ background: $slider-background;
+ border: 0px solid #000101;
+ }
+ &::-ms-track {
+ @include inputTrack;
+ background: transparent;
+ border-color: transparent;
+ border-width: 39px 0;
+ color: transparent;
+ }
+ &::-ms-fill-lower {
+ background: $slider-background;
+ border: 0px solid #000101;
+ box-shadow: $track-shadow;
+ }
+ &::-ms-fill-upper {
+ background: #ac51b5;
+ border: 0px solid #000101;
+ box-shadow: $track-shadow;
+ }
+ &:focus::-ms-fill-lower {
+ background: $slider-background;
+ }
+ &:focus::-ms-fill-upper {
+ background: $slider-background;
+ }
+ }
+ &-info {
+ @include infoBase
+ }
+}
+.SqRangeInput {
+ &--container {
+ background:white;
+ border: 1px solid #ccc;
+ }
+ &--horizontal {
+ @include sliderBase;
+ position: relative;
+ height:$thumb-height + px;
+ margin: $input-margin / 2 + px;
+ }
+ &--vertical {
+ @include sliderBase;
+ position: relative;
+
+ input[type=range] {
+ -webkit-appearance: none;
+ height: 100%;
+ width: $track-height + px;
+ transform: rotate(270deg);
+ transform-origin: right;
+ &::-webkit-slider-thumb {
+ box-shadow: -2px 2px 2px 0px #ccc;
+ }
+ &::-moz-range-thumb {
+ box-shadow: -2px 2px 2px 0px #ccc;
+ }
+ &::-ms-thumb {
+ box-shadow: -2px 2px 2px 0px #ccc;
+ }
+ }
+ &-info {
+ @include infoBase;
+
+ div {
+ transform: translateY(10px) rotate(90deg);
+ }
+ }
+ }
+ &.is-disabled {
+ input[type=range] {
+ &:disabled {
+ &::-webkit-slider-thumb {
+ background: $disabled-thumb-color;
+ }
+ &::-moz-range-thumb {
+ background: $disabled-thumb-color;
+ }
+ &::-ms-thumb {
+ background: $disabled-thumb-color;
+ }
+ &::-webkit-slider-runnable-track {
+ background: $disabled-slider-background;
+ }
+ &:focus::-webkit-slider-runnable-track {
+ background: $disabled-slider-background;
+ }
+ &::-moz-range-track {
+ background: $disabled-slider-background;
+ }
+ &::-ms-track {
+ }
+ &::-ms-fill-upper {
+ background: $disabled-slider-background;
+ }
+ &::-ms-fill-lower {
+ background: $disabled-slider-background;
+ }
+ &:focus::-ms-fill-lower {
+ background: $disabled-slider-background;
+ }
+ &:focus::-ms-fill-upper {
+ background: $disabled-slider-background;
+ }
+ }
+ }
+ }
+}
diff --git a/skyquake/framework/widgets/text-area/rw.text-area.js b/skyquake/framework/widgets/text-area/rw.text-area.js
new file mode 100644
index 0000000..7a036a6
--- /dev/null
+++ b/skyquake/framework/widgets/text-area/rw.text-area.js
@@ -0,0 +1,232 @@
+
+/*
+ *
+ * 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 ButtonEventListenerMixin = require('../mixins/ButtonEventListener.js');
+
+
+/**
+ * A text input component.
+ * It's props values and a brief description below
+ *
+ * value: Holds the initial text content of the input.
+ * label: The text content of the label.
+ * requiredText: The text content of the "if required" message.
+ * errorText: The text content of the error message.
+ * placeholder: The initial placeholder text of the input
+ * ClassName: Css Classes applied to the element.
+ * rows: Number of text lines the input element displays at one time.
+ * cols: Number of characters per line.
+ * resizable: If the input element is resizable.
+ * isRequired: A boolean indicating whether or not the input is required.
+ * isDisabled: A boolean indicating the disabled state of the element.
+ * isReadONly: A boolean indicating whether or not the input is read only.
+ * maxLength: The hard limit on how many characters can be in the input.
+ **/
+module.exports = React.createClass({
+ displayName: "TextArea",
+ mixins:[ButtonEventListenerMixin],
+ propTypes: {
+ value: React.PropTypes.string,
+ label: React.PropTypes.string,
+ requiredText: React.PropTypes.string,
+ errorText: React.PropTypes.string,
+ placeholder: React.PropTypes.string,
+ className: React.PropTypes.string,
+ rows: React.PropTypes.number,
+ cols: React.PropTypes.number,
+ resizable: React.PropTypes.bool,
+ isRequired: React.PropTypes.bool,
+ isDisabled: React.PropTypes.bool,
+ isReadOnly: React.PropTypes.bool,
+ maxLength: React.PropTypes.number,
+ instructions: React.PropTypes.string
+ },
+
+
+ /**
+ * Sets the default input state.
+ * If there is no description for the variable, assume it's the same as it's props counterpart.
+ *
+ * value: Holds the current text contents of the input element.
+ * isActive: Boolean to indicate if input is active.
+ * isHovered: Boolean to indicate if the input is being hovered over.
+ * isFocused: Boolean to indicate if the input has been focused.
+ * isDisabled: Boolean to indicate if input has been disabled.
+ *
+ * @returns {{sizeOfButton: (*|string), isActive: boolean, isHovered: boolean, isFocused: boolean, isDisabled: (*|boolean),
+ * isReadOnly: (*|.textInput.isReadOnly|.exports.propTypes.isReadOnly|.exports.getInitialState.isReadOnly|boolean),
+ * isRequired: (*|.textInput.isRequired|.exports.propTypes.isRequired|.exports.getInitialState.isRequired|isRequired|null),
+ * isValid: null, isSuccess: null}}
+ */
+ getInitialState: function() {
+ return {
+ value: this.props.value || "",
+ label: this.props.label || "",
+ requiredText: this.props.requiredText || "Required",
+ errorText: this.props.errorText || "",
+ instructionsText: this.props.instructions || "",
+ isActive: false,
+ isHovered: false,
+ isFocused: false,
+ isDisabled: this.props.isDisabled || false,
+ isReadOnly: this.props.isReadOnly || false,
+ isRequired: this.props.isRequired || null,
+ isValid: null, // Three way bool. Valid: true. Invalid: false. Not acted on: null.
+ isSuccess: null // Three way bool. Success: true. Error: false. Not acted on: null.
+
+ }
+ },
+
+ /**
+ * 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.isReadOnly + this.state.isDisabled + this.state.isActive + this.state.isFocused +
+ this.state.isHovered + this.state.isValid + this.state.isSuccess + this.state.value;
+ var nextStateString = nextState.isReadOnly + nextState.isDisabled + nextState.isActive + nextState.isFocused +
+ nextState.isHovered + nextState.isValid + nextState.isSuccess + nextState.value;
+ if (currentStateString == nextStateString) {
+ return false;
+ }
+ return true;
+ },
+ /**
+ * Makes sure that when the user types new input, the contents of the input changes accordingly.
+ *
+ * @param event
+ */
+ handleChange: function(event) {
+ this.setState({value:event.target.value});
+ },
+
+ /**
+ * Returns a string reflecting the current state of the input.
+ * If the component state "isDisabled" is true, returns a string "disabled".
+ * If the component state "isReadOnly" is true, returns a string "readonly".
+ * 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}
+ */
+ setComponentState: function() {
+ var ret = "";
+ if (this.state.isDisabled) {
+ return "disabled";
+ }
+ if (this.state.isReadOnly) {
+ return "readonly";
+ }
+ if (this.state.isActive) {
+ ret += "active ";
+ }
+ if (this.state.isHovered) {
+ ret += "hovered ";
+ }
+ if (this.state.isFocused) {
+ ret += "focused ";
+ }
+ return ret;
+ },
+
+ /**
+ * Renders the Text Input component.
+ *
+ **/
+ render: function() {
+ var value = this.state.value;
+ var input = null;
+ var textarea_style = {};
+ var textarea_state = this.setComponentState();
+
+ // creates a style for the "resizable" attribute.
+ if (!this.props.resizable) {
+ textarea_style.resize = "none";
+ }
+
+ // The input element.
+ input = React.createElement("textarea", {
+ ref: "inputRef",
+ "data-state": textarea_state,
+ value: value,
+ "style": textarea_style,
+ placeholder: this.props.placeholder,
+ rows: this.props.rows,
+ cols: this.props.cols,
+ maxLength: this.props.maxLength,
+ required: this.state.isRequired,
+ disabled: this.state.isDisabled,
+ readOnly: this.state.isReadOnly,
+ onChange: this.handleChange,
+ onClick: this.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,
+ className: (this.props.className || "rw-textarea"),
+ tabIndex: 0
+ },
+ null
+ );
+
+ // The "if required" element. It displays a label if the element is required.
+ if(this.props.isRequired == true){
+ var requiredEle = React.createElement("small", {className: "rw-form-requiredLabel"}, this.state.requiredText);
+ }
+
+ // The label element associated with the input.
+ var label = React.createElement("label", null, this.state.label, requiredEle, input);
+
+ // The "error" element. It pops up as a message if there is an error with the input.
+ if(this.state.errorText != "") {
+ var error = React.createElement("p", {className: "rw-form-errorMsg"}, this.state.errorText);
+ }
+
+ //
+ if(this.state.instructionsText != ""){
+ var instructions = React.createElement("p", {className: "rw-form-instructions"}, this.state.instructionsText)
+ }
+
+ // The parent element for all.
+ var ret = React.createElement("div", {
+ "data-state": textarea_state,
+ required: this.state.isRequired,
+ disabled: this.state.isDisabled,
+ readOnly: this.state.isReadOnly
+ }, label, error, instructions);
+
+ return ret;
+ }
+});
diff --git a/skyquake/framework/widgets/text-input/check-box/rw.check-box.js b/skyquake/framework/widgets/text-input/check-box/rw.check-box.js
new file mode 100644
index 0000000..db8f37e
--- /dev/null
+++ b/skyquake/framework/widgets/text-input/check-box/rw.check-box.js
@@ -0,0 +1,233 @@
+
+/*
+ *
+ * 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 ButtonEventListenerMixin = require('../mixins/ButtonEventListener.js');
+
+
+/**
+ * A check-box component.
+ * It's props values and a brief description below
+ *
+ * label: The label for the checkbox group.
+ * checkboxes: The object that creates each checkbox. Each object has a property "label" and "checked".
+ * label: The label for the individual checkbox.
+ * checked: If set to true, the individual checkbox is initialized with a check.
+ * requiredText: The text content of the "if required" message.
+ * errorText: The text content of the error message.
+ * ClassName: Css Classes applied to the element.
+ * size: The size of the element.
+ * isRequired: A boolean indicating whether or not the input is required.
+ * isDisabled: A boolean indicating the disabled state of the element.
+ * isReadOnly: A boolean indicating whether or not the input is read only.
+ * instructions: The text content of the instructions
+ **/
+module.exports = React.createClass({
+ displayName: "CheckBox",
+ mixins:[ButtonEventListenerMixin],
+ propTypes: {
+ label: React.PropTypes.string,
+ checkboxes: React.PropTypes.arrayOf(
+ React.PropTypes.shape(
+ {
+ label: React.PropTypes.string,
+ checked: React.PropTypes.bool,
+ indeterminate: React.PropTypes.bool
+ }
+ )),
+ requiredText: React.PropTypes.string,
+ instructionText: React.PropTypes.string,
+ errorText: React.PropTypes.string,
+ className: React.PropTypes.string,
+ size: React.PropTypes.string,
+ isRequired: React.PropTypes.bool,
+ isDisabled: React.PropTypes.bool,
+ isReadOnly: React.PropTypes.bool
+ },
+
+
+ /**
+ * Sets the default input state.
+ * If there is no description for the variable, assume it's the same as it's props counterpart.
+ *
+ * isActive: Boolean to indicate if input is active.
+ * isHovered: Boolean to indicate if the input is being hovered over.
+ * isFocused: Boolean to indicate if the input has been focused.
+ * isDisabled: Boolean to indicate if input has been disabled.
+ *
+ * @returns {{sizeOfButton: (*|string), isActive: boolean, isHovered: boolean, isFocused: boolean, isDisabled: (*|boolean),
+ * isReadOnly: (*|.textInput.isReadOnly|.exports.propTypes.isReadOnly|.exports.getInitialState.isReadOnly|boolean),
+ * isRequired: (*|.textInput.isRequired|.exports.propTypes.isRequired|.exports.getInitialState.isRequired|isRequired|null),
+ * isValid: null, isSuccess: null}}
+ */
+ getInitialState: function() {
+
+ return {
+ mounted: false,
+ label: this.props.label || "",
+ requiredText: this.props.requiredText || "Required",
+ instructionsText: this.props.instructions || "",
+ errorText: this.props.errorText || "",
+ size: this.props.size || '',
+ isActive: false,
+ isHovered: false,
+ isFocused: false,
+ isDisabled: this.props.disabled || false,
+ isReadOnly: this.props.isReadOnly || false,
+ isRequired: this.props.isRequired || null,
+ isValid: null, // Three way bool. Valid: true. Invalid: false. Not acted on: null.
+ isSuccess: null // Three way bool. Success: true. Error: false. Not acted on: null.
+ }
+ },
+
+ /**
+ * 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.isReadOnly + this.state.isDisabled + this.state.isActive + this.state.isFocused +
+ this.state.isHovered + this.state.isValid + this.state.isSuccess;
+ var nextStateString = nextState.isReadOnly + nextState.isDisabled + nextState.isActive + nextState.isFocused +
+ nextState.isHovered + nextState.isValid + nextState.isSuccess;
+ if (currentStateString == nextStateString) {
+ return false;
+ }
+ return true;
+ },
+
+ componentDidMount: function() {
+ this.state.mounted = true;
+ this.render();
+ },
+
+ /**
+ * Returns a string reflecting the current state of the input.
+ * If the component state "isDisabled" is true, returns a string "disabled".
+ * If the component state "isReadOnly" is true, returns a string "readonly".
+ * 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}
+ */
+ setComponentState: function() {
+
+ var ret = "";
+ if (this.state.isDisabled) {
+ return "disabled";
+ }
+ if (this.state.isReadOnly) {
+ return "readonly";
+ }
+ if (this.state.isActive) {
+ ret += "active ";
+ }
+ if (this.state.isHovered) {
+ ret += "hovered ";
+ }
+ if (this.state.isFocused) {
+ ret += "focused ";
+ }
+ return ret;
+ },
+
+ localOnClick: function(e) {
+ this.onClick(e);
+ },
+
+ /**
+ * Renders the checkbox component.
+ *
+ **/
+ render: function() {
+ var input = [];
+ var checkbox = [];
+ var label = [];
+ var input_style = {};
+ var input_state = this.setComponentState();
+
+ // The group label element
+ label = React.createElement("label", {className: "rw-form__label"}, this.props.label);
+
+ // Creates each individual checkbox element and the label for each.
+ for (var i = 0; i < this.props.checkboxes.length; i++) {
+ checkbox[i] = React.createElement("input",{
+ key: i,
+ defaultChecked: this.props.checkboxes[i].checked,
+ //indeterminate: true,
+ type: "checkbox",
+ readOnly: this.props.readonly,
+ disabled: this.props.disabled,
+ onClick: this.localOnClick,
+ 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
+ }, null);
+ if (this.state.mounted) {
+ this.getDOMNode().children[i + 1].children[0].indeterminate = this.props.checkboxes[i].indeterminate;
+ }
+
+
+ input[i] = React.createElement("label", {className: "rw-form__label-checkBox", key:i, readOnly:this.props.readonly, disabled:this.props.disabled}, checkbox[i], this.props.checkboxes[i].label);
+
+ }
+
+ // The "if required" element. It displays a label if the element is required.
+ if(this.props.isRequired == true){
+ var requiredEle = React.createElement("small", {className: "rw-form__required-label"}, this.state.requiredText);
+ }
+
+ // The "error" element. It pops up as a message if there is an error with the input.
+ if(this.state.errorText != "") {
+ var error = React.createElement("p", {className: "rw-form__message-error"}, this.state.errorText);
+ }
+
+ // The "instruction" element.
+ if(this.state.instructionsText != ""){
+ var instructions = React.createElement("p", {className: "rw-form-instructions"}, this.state.instructionsText)
+ }
+
+ // The parent element for all.
+ var ret = React.createElement("div", {
+ "data-state": input_state,
+ required: this.state.isRequired,
+ disabled: this.state.isDisabled,
+ readOnly: this.state.isReadOnly,
+ className: "rw-form"
+
+ }, requiredEle, label, input, error, instructions);
+
+ return ret;
+ }
+});
diff --git a/skyquake/framework/widgets/text-input/check-box/rw.check-box2.js b/skyquake/framework/widgets/text-input/check-box/rw.check-box2.js
new file mode 100644
index 0000000..cc8ea87
--- /dev/null
+++ b/skyquake/framework/widgets/text-input/check-box/rw.check-box2.js
@@ -0,0 +1,228 @@
+
+/*
+ *
+ * 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 ButtonEventListenerMixin = require('../mixins/ButtonEventListener.js');
+
+
+/**
+ * A check-box component.
+ * It's props values and a brief description below
+ *
+ * label: The label for the checkbox group.
+ * checkboxes: The object that creates each checkbox. Each object has a property "label" and "checked".
+ * label: The label for the individual checkbox.
+ * checked: If set to true, the individual checkbox is initialized with a check.
+ * requiredText: The text content of the "if required" message.
+ * errorText: The text content of the error message.
+ * ClassName: Css Classes applied to the element.
+ * size: The size of the element.
+ * isRequired: A boolean indicating whether or not the input is required.
+ * isDisabled: A boolean indicating the disabled state of the element.
+ * isReadOnly: A boolean indicating whether or not the input is read only.
+ * instructions: The text content of the instructions
+ **/
+module.exports = React.createClass({
+ displayName: "CheckBox",
+ mixins:[ButtonEventListenerMixin],
+ propTypes: {
+ children: React.PropTypes.arrayOf(React.PropTypes.object),
+ label: React.PropTypes.string,
+ checked: React.PropTypes.bool,
+ indeterminate: React.PropTypes.bool,
+ requiredText: React.PropTypes.string,
+ instructionText: React.PropTypes.string,
+ errorText: React.PropTypes.string,
+ className: React.PropTypes.string,
+ size: React.PropTypes.string,
+ isRequired: React.PropTypes.bool,
+ isDisabled: React.PropTypes.bool,
+ isReadOnly: React.PropTypes.bool
+ },
+
+
+ /**
+ * Sets the default input state.
+ * If there is no description for the variable, assume it's the same as it's props counterpart.
+ *
+ * isActive: Boolean to indicate if input is active.
+ * isHovered: Boolean to indicate if the input is being hovered over.
+ * isFocused: Boolean to indicate if the input has been focused.
+ * isDisabled: Boolean to indicate if input has been disabled.
+ *
+ * @returns {{sizeOfButton: (*|string), isActive: boolean, isHovered: boolean, isFocused: boolean, isDisabled: (*|boolean),
+ * isReadOnly: (*|.textInput.isReadOnly|.exports.propTypes.isReadOnly|.exports.getInitialState.isReadOnly|boolean),
+ * isRequired: (*|.textInput.isRequired|.exports.propTypes.isRequired|.exports.getInitialState.isRequired|isRequired|null),
+ * isValid: null, isSuccess: null}}
+ */
+ getInitialState: function() {
+
+ return {
+ //mounted: false,
+ label: this.props.label || "",
+ requiredText: this.props.requiredText || "Required",
+ instructionsText: this.props.instructions || "",
+ errorText: this.props.errorText || "",
+ size: this.props.size || '',
+ isActive: false,
+ isHovered: false,
+ isFocused: false,
+ isDisabled: this.props.disabled || false,
+ isReadOnly: this.props.isReadOnly || false,
+ isRequired: this.props.isRequired || null,
+ isValid: null, // Three way bool. Valid: true. Invalid: false. Not acted on: null.
+ isSuccess: null // Three way bool. Success: true. Error: false. Not acted on: null.
+ }
+ },
+
+ /**
+ * 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.isReadOnly + this.state.isDisabled + this.state.isActive + this.state.isFocused +
+ this.state.isHovered + this.state.isValid + this.state.isSuccess;
+ var nextStateString = nextState.isReadOnly + nextState.isDisabled + nextState.isActive + nextState.isFocused +
+ nextState.isHovered + nextState.isValid + nextState.isSuccess;
+ if (currentStateString == nextStateString) {
+ return false;
+ }
+ return true;
+ },
+
+ componentDidMount: function() {
+ this.state.mounted = true;
+ this.render();
+ },
+
+ /**
+ * Returns a string reflecting the current state of the input.
+ * If the component state "isDisabled" is true, returns a string "disabled".
+ * If the component state "isReadOnly" is true, returns a string "readonly".
+ * 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}
+ */
+ setComponentState: function() {
+
+ var ret = "";
+ if (this.state.isDisabled) {
+ return "disabled";
+ }
+ if (this.state.isReadOnly) {
+ return "readonly";
+ }
+ if (this.state.isActive) {
+ ret += "active ";
+ }
+ if (this.state.isHovered) {
+ ret += "hovered ";
+ }
+ if (this.state.isFocused) {
+ ret += "focused ";
+ }
+ return ret;
+ },
+
+ localOnClick: function(e) {
+ this.onClick(e);
+ },
+
+ /**
+ * Renders the checkbox component.
+ *
+ **/
+ render: function() {
+ var input = [];
+ var checkbox = [];
+ var label = [];
+ var input_style = {};
+ var input_state = this.setComponentState();
+
+ // The group label element
+ label = React.createElement("label", {className: "rw-form__label"}, this.props.label);
+
+ // Creates each individual checkbox element and the label for each.
+ for (var i = 0; i < this.props.checkboxes.length; i++) {
+ checkbox[i] = React.createElement("input",{
+ key: i,
+ defaultChecked: this.props.checkboxes[i].checked,
+ //indeterminate: true,
+ type: "checkbox",
+ readOnly: this.props.readonly,
+ disabled: this.props.disabled,
+ onClick: this.localOnClick,
+ 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
+ }, null);
+ if (this.state.mounted) {
+ this.getDOMNode().children[i + 1].children[0].indeterminate = this.props.checkboxes[i].indeterminate;
+ }
+
+
+ input[i] = React.createElement("label", {className: "rw-form__label-checkBox", key:i, readOnly:this.props.readonly, disabled:this.props.disabled}, checkbox[i], this.props.checkboxes[i].label);
+
+ }
+
+ // The "if required" element. It displays a label if the element is required.
+ if(this.props.isRequired == true){
+ var requiredEle = React.createElement("small", {className: "rw-form__required-label"}, this.state.requiredText);
+ }
+
+ // The "error" element. It pops up as a message if there is an error with the input.
+ if(this.state.errorText != "") {
+ var error = React.createElement("p", {className: "rw-form__message-error"}, this.state.errorText);
+ }
+
+ // The "instruction" element.
+ if(this.state.instructionsText != ""){
+ var instructions = React.createElement("p", {className: "rw-form-instructions"}, this.state.instructionsText)
+ }
+
+ // The parent element for all.
+ var ret = React.createElement("div", {
+ "data-state": input_state,
+ required: this.state.isRequired,
+ disabled: this.state.isDisabled,
+ readOnly: this.state.isReadOnly,
+ className: "rw-form"
+
+ }, requiredEle, label, input, error, instructions);
+
+ return ret;
+ }
+});
diff --git a/skyquake/framework/widgets/text-input/rw.text-input.js b/skyquake/framework/widgets/text-input/rw.text-input.js
new file mode 100644
index 0000000..62e1bcc
--- /dev/null
+++ b/skyquake/framework/widgets/text-input/rw.text-input.js
@@ -0,0 +1,280 @@
+
+/*
+ *
+ * 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 ButtonEventListenerMixin = require('../mixins/ButtonEventListener.js');
+var validator = require('validator');
+
+/**
+ * A text input component.
+ * It's props values and a brief description below
+ *
+ * value: Holds the initial text content of the input.
+ * label: The text content of the label.
+ * requiredText: The text content of the "if required" message.
+ * errorText: The text content of the error message.
+ * placeholder: The initial placeholder text of the input
+ * ClassName: Css Classes applied to the element.
+ * size: The size of the element.
+ * minWidth: Minimum width of the element.
+ * maxWidth: Maximum width of the element.
+ * isRequired: A boolean indicating whether or not the input is required.
+ * isDisabled: A boolean indicating the disabled state of the element.
+ * isReadOnly: A boolean indicating whether or not the input is read only.
+ * pattern: A regex putting constraints on what the user input can be.
+ * maxLength: The hard limit on how many characters can be in the input.
+ **/
+module.exports = React.createClass({
+ displayName: "TextInput",
+ mixins:[ButtonEventListenerMixin],
+ propTypes: {
+ value: React.PropTypes.string,
+ label: React.PropTypes.string,
+ requiredText: React.PropTypes.string,
+ errorText: React.PropTypes.string,
+ placeholder: React.PropTypes.string,
+ className: React.PropTypes.string,
+ size: React.PropTypes.string,
+ minWidth: React.PropTypes.number,
+ maxWidth: React.PropTypes.number,
+ isRequired: React.PropTypes.bool,
+ isDisabled: React.PropTypes.bool,
+ isReadOnly: React.PropTypes.bool,
+ pattern: React.PropTypes.string,
+ maxLength: React.PropTypes.number,
+ instructions: React.PropTypes.string
+ },
+
+
+ /**
+ * Sets the default input state.
+ * If there is no description for the variable, assume it's the same as it's props counterpart.
+ *
+ * value: The current text contents of the input.
+ * isActive: Boolean to indicate if input is active.
+ * isHovered: Boolean to indicate if the input is being hovered over.
+ * isFocused: Boolean to indicate if the input has been focused.
+ * isDisabled: Boolean to indicate if input has been disabled.
+ *
+ * @returns {{sizeOfButton: (*|string), isActive: boolean, isHovered: boolean, isFocused: boolean, isDisabled: (*|boolean),
+ * isReadOnly: (*|.textInput.isReadOnly|.exports.propTypes.isReadOnly|.exports.getInitialState.isReadOnly|boolean),
+ * isRequired: (*|.textInput.isRequired|.exports.propTypes.isRequired|.exports.getInitialState.isRequired|isRequired|null),
+ * isValid: null, isSuccess: null}}
+ */
+ getInitialState: function() {
+ return {
+ value: this.props.value || '',
+ label: this.props.label || "",
+ requiredText: this.props.requiredText || "Required",
+ instructionsText: this.props.instructions || "",
+ errorText: "",
+ size: this.props.size || '',
+ isActive: false,
+ isHovered: false,
+ isFocused: false,
+ isDisabled: this.props.isDisabled || false,
+ isReadOnly: this.props.isReadOnly || false,
+ isRequired: this.props.isRequired || null,
+ isValid: null, // Three way bool. Valid: true. Invalid: false. Not acted on: null.
+ isSuccess: null // Three way bool. Success: true. Error: false. Not acted on: null.
+ }
+ },
+
+ /**
+ * 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.isReadOnly + this.state.isDisabled + this.state.isActive + this.state.isFocused +
+ this.state.isHovered + this.state.isValid + this.state.isSuccess + this.state.value;
+ var nextStateString = nextState.isReadOnly + nextState.isDisabled + nextState.isActive + nextState.isFocused +
+ nextState.isHovered + nextState.isValid + nextState.isSuccess + nextState.value;
+ if (currentStateString == nextStateString) {
+ return false;
+ }
+ return true;
+ },
+
+ /**
+ * Makes sure that when the user types new input, the contents of the input changes accordingly.
+ *
+ * @param event
+ */
+ handleChange: function(event) {
+ this.setState({value:event.target.value});
+ },
+
+ /**
+ * Makes sure that when the user types new input, the contents of the input changes accordingly.
+ *
+ * @param event
+ */
+ handleValidation: function(event) {
+ if (this.props.isRequired) {
+ if (validator.isNull(event.target.value) || event.target.value == '') {
+ this.setState({
+ errorText: this.props.errorText,
+ isValid: false
+ });
+ } else {
+ this.setState({
+ errorText: '',
+ isValid: true
+ });
+ }
+ }
+ if (this.props.pattern) {
+ if (!validator.matches(event.target.value, this.props.pattern)) {
+ this.setState({
+ errorText: this.props.errorText,
+ isValid: false
+ });
+ } else {
+ this.setState({
+ errorText: '',
+ isValid: true
+ });
+ }
+ }
+
+ },
+
+ /**
+ * Returns a string reflecting the current state of the input.
+ * If the component state "isDisabled" is true, returns a string "disabled".
+ * If the component state "isReadOnly" is true, returns a string "readonly".
+ * 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}
+ */
+ setComponentState: function() {
+ var ret = "";
+ if (this.state.isDisabled) {
+ return "disabled";
+ }
+ if (this.state.isReadOnly) {
+ return "readonly";
+ }
+ if (this.state.isActive) {
+ ret += "active ";
+ }
+ if (this.state.isHovered) {
+ ret += "hovered ";
+ }
+ if (this.state.isFocused) {
+ ret += "focused ";
+ }
+ return ret;
+ },
+
+ /**
+ * Renders the Text Input component.
+ *
+ **/
+ render: function() {
+ var value = this.state.value;
+ var input = null;
+ var input_style = {};
+ var input_state = this.setComponentState();
+
+ // The following if statements translates the min/max width from a number into a string.
+ if (this.props.minWidth) {
+ input_style['min-width'] = String(this.props.minWidth) + 'px';
+ }
+ if (this.props.maxWidth) {
+ input_style['max-width'] = String(this.props.maxWidth) + 'px';
+ }
+
+ // The input element.
+ input = React.createElement("input", {
+ ref: "inputRef",
+ "data-state": input_state,
+ "data-validate": this.state.isValid,
+ value: value,
+ placeholder: this.props.placeholder,
+ pattern: this.props.pattern,
+ maxLength: this.props.maxLength,
+ required: this.state.isRequired,
+ disabled: this.state.isDisabled,
+ readOnly: this.state.isReadOnly,
+ onChange: this.handleChange,
+ onClick: this.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.handleValidation,
+ onFocus: this.onFocus,
+ onBlur: this.handleValidation,
+ className: (this.props.className || "rw-textinput"),
+ tabIndex: 0
+ },
+ null
+ );
+
+ // The "if required" element. It displays a label if the element is required.
+ if(this.props.isRequired == true){
+ var requiredEle = React.createElement("small", {className: "rw-form__required-label"}, this.state.requiredText);
+ }
+
+ //
+ if(this.state.isValid == false) {
+ var validations = React.createElement("svg", {className: "rw-form__icon"}, this.state.errorText);
+ }
+ // The label element associated with the input.
+ var label = React.createElement("label", {className: "rw-form__label"}, this.state.label, requiredEle, input, validations);
+
+ // The "error" element. It pops up as a message if there is an error with the input.
+ if(this.state.errorText != "") {
+ var error = React.createElement("p", {className: "rw-form__message-error"}, this.state.errorText);
+ }
+
+ //
+ if(this.state.instructionsText != ""){
+ var instructions = React.createElement("p", {className: "rw-form__instructions"}, this.state.instructionsText)
+ }
+
+
+
+ // The parent element for all.
+ var ret = React.createElement("div", {
+ "data-state": input_state,
+ required: this.state.isRequired,
+ disabled: this.state.isDisabled,
+ readOnly: this.state.isReadOnly,
+ "data-validate": this.state.isValid,
+ className: "rw-form"
+ }, label, error, instructions);
+
+ return ret;
+ }
+});
diff --git a/skyquake/framework/widgets/topology/topologyL2Graph.jsx b/skyquake/framework/widgets/topology/topologyL2Graph.jsx
new file mode 100644
index 0000000..fe4360b
--- /dev/null
+++ b/skyquake/framework/widgets/topology/topologyL2Graph.jsx
@@ -0,0 +1,253 @@
+
+/*
+ *
+ * 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 d3 from 'd3';
+import DashboardCard from '../dashboard_card/dashboard_card.jsx';
+
+export default class TopologyL2Graph extends React.Component {
+ constructor(props) {
+ super(props);
+ this.data = props.data;
+ this.selectedID = 0;
+ this.nodeCount = 0;
+ this.network_coding = {}
+ this.nodeEvent = props.nodeEvent || null;
+ }
+ componentDidMount(){
+ var weight = 400;
+ var handler = this;
+ this.force = d3.layout.force()
+ .size([this.props.width, this.props.height])
+ .charge(-weight)
+ .linkDistance(weight)
+ .on("tick", this.tick.bind(this));
+
+ this.drag = this.force.drag()
+ .on("dragstart", function(d) {
+ handler.dragstart(d, handler);
+ });
+
+ }
+ componentWillUnmount() {
+ d3.select('svg').remove();
+ }
+ componentWillReceiveProps(props) {
+ if(!this.svg) {
+ // NOTE: We may need to revisit how D3 accesses DOM elements
+ this.svg = d3.select(document.querySelector('#topologyL2')).append("svg")
+ .attr("width", this.props.width)
+ .attr("height", this.props.height)
+ .classed("topology", true);
+ }
+
+ if (props.data.links.length > 0) {
+ this.network_coding = this.create_group_coding(props.data.network_ids.sort());
+ this.update(props.data);
+ }
+ }
+
+ create_group_coding(group_ids) {
+ var group_coding = {};
+ group_ids.forEach(function(element, index, array) {
+ group_coding[element] = index+1;
+ });
+ return group_coding;
+ }
+ getNetworkCoding(network_id) {
+ var group = this.network_coding[network_id];
+ if (group != undefined) {
+ return group;
+ } else {
+ return 0;
+ }
+ }
+
+ drawLegend(graph) {
+ // Hack to prevent multiple legends being displayed
+ this.svg.selectAll(".legend").remove();
+
+ var showBox = false;
+ var svg = this.svg;
+ var item_count = (graph.network_ids) ? graph.network_ids.length : 0;
+ var pos = {
+ anchorX: 5,
+ anchorY: 5,
+ height: 40,
+ width: 200,
+ items_y: 35,
+ items_x: 7,
+ item_height: 25
+ };
+ pos.height += item_count * pos.item_height;
+ var legend_translate = "translate("+pos.anchorX+","+pos.anchorY+")";
+
+ var legend = svg.append("g")
+ .attr("class", "legend")
+ .attr("transform", legend_translate);
+
+ var legend_box = (showBox) ? legend.append("rect")
+ .attr("x", 0)
+ .attr("y", 0)
+ .attr("height", pos.height)
+ .attr("width", pos.width)
+ .style("stroke", "black")
+ .style("fill", "none") : null;
+
+ legend.append("text")
+ .attr("x", 5)
+ .attr("y", 15)
+ .text("Network color mapping:");
+
+ legend.selectAll("g").data(graph.network_ids)
+ .enter()
+ .append("g")
+ .each(function(d, i) {
+ var colors = ["green", "orange", "red" ];
+ var g = d3.select(this);
+ var group_number = i+1;
+ g.attr('class', "node-group-" + group_number);
+
+ g.append("circle")
+ .attr("cx", pos.items_x + 3)
+ .attr("cy", pos.items_y + i * pos.item_height)
+ .attr("r", 6);
+
+ g.append("text")
+ .attr("x", pos.items_x + 25)
+ .attr("y", pos.items_y + (i * pos.item_height + 4))
+ .attr("height", 20)
+ .attr("width", 80)
+ .text(d);
+ })
+ }
+
+ update(graph) {
+ var svg = this.svg;
+ var handler = this;
+ this.force
+ .nodes(graph.nodes)
+ .links(graph.links)
+ .start();
+
+ this.link = svg.selectAll(".link")
+ .data(graph.links)
+ .enter().append("line")
+ .attr("class", "link");
+
+ this.gnodes = svg.selectAll('g.gnode')
+ .data(graph.nodes)
+ .enter()
+ .append('g')
+ .classed('gnode', true)
+ .attr('data-network', function(d) { return d.network; })
+ .attr('class', function(d) {
+ return d3.select(this).attr('class') + ' node-group-'+ handler.getNetworkCoding(d.network);
+ });
+
+ this.node = this.gnodes.append("circle")
+ .attr("class", "node")
+ .attr("r", this.props.radius)
+ .on("dblclick", function(d) {
+ handler.dblclick(d, handler)
+ })
+ .call(this.drag)
+ .on('click', function(d) {
+ handler.click.call(this, d, handler)
+ });
+ var labels = this.gnodes.append("text")
+ .attr("text-anchor", "middle")
+ .attr("fill", "black")
+ .attr("font-size", "12")
+ .attr("y", "-10")
+ .text(function(d) { return d.name; });
+ this.drawLegend(graph);
+ }
+
+ tick = () => {
+ this.link.attr("x1", function(d) { return d.source.x; })
+ .attr("y1", function(d) { return d.source.y; })
+ .attr("x2", function(d) { return d.target.x; })
+ .attr("y2", function(d) { return d.target.y; });
+
+ this.gnodes.attr("transform", function(d) {
+ return 'translate(' + [d.x, d.y] + ')';
+ });
+
+ }
+
+ click(d, topo) {
+ console.log("TopologyL2Graph.click called");
+ // 'This' is the svg circle element
+ var gnode = d3.select(this.parentNode);
+
+ topo.svg.selectAll("text").transition()
+ .duration(topo.props.nodeText.transitionTime)
+ .attr("font-size", topo.props.nodeText.size)
+ .attr("fill", topo.props.nodeText.color)
+
+ // Set focus node text properties
+ d3.select(this.parentNode).selectAll('text').transition()
+ .duration(topo.props.nodeText.transitionTime)
+ .attr("font-size", topo.props.nodeText.focus.size)
+ .attr("fill", topo.props.nodeText.focus.color);
+
+ // Perform detail view
+ topo.selectedID = d.id;
+ if (topo.nodeEvent) {
+ topo.nodeEvent(d.id);
+ }
+ // set record view as listener
+ }
+
+ dblclick(d, topo) {
+ this.d3.select(this).classed("fixed", d.fixed = false);
+ }
+
+ dragstart(d) {
+ //d3.select(this).classed("fixed", d.fixed = true);
+ }
+
+ render() {
+ return ( <DashboardCard showHeader={true} title="Topology L2 Graph">
+ <div id="topologyL2"></div>
+ </DashboardCard>)
+ }
+}
+
+TopologyL2Graph.defaultProps = {
+ width: 700,
+ height: 500,
+ maxLabel: 150,
+ duration: 500,
+ radius: 6,
+ data: {
+ nodes: [],
+ links: [],
+ network_ids: []
+ },
+ nodeText: {
+ size: 12,
+ color: 'black',
+ focus: {
+ size: 14,
+ color: 'blue'
+ },
+ transitionTime: 250
+ }
+}
diff --git a/skyquake/framework/widgets/topology/topologyTree.jsx b/skyquake/framework/widgets/topology/topologyTree.jsx
new file mode 100644
index 0000000..5e0d895
--- /dev/null
+++ b/skyquake/framework/widgets/topology/topologyTree.jsx
@@ -0,0 +1,256 @@
+
+/*
+ *
+ * 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 ReactDOM from 'react-dom';
+import d3 from 'd3';
+import DashboardCard from '../dashboard_card/dashboard_card.jsx';
+import _ from 'lodash';
+import $ from 'jquery';
+import './topologyTree.scss';
+
+export default class TopologyTree extends React.Component {
+ constructor(props) {
+ super(props);
+ this.data = props.data;
+ this.selectedID = 0;
+ this.nodeCount = 0;
+ this.size = this.wrapperSize();
+ this.tree = d3.layout.tree()
+ .size([this.props.treeHeight, this.props.treeWidth]);
+ this.diagonal = d3.svg.diagonal()
+ .projection(function(d) { return [d.y, d.x]; });
+ this.svg = null;
+ }
+ componentWillReceiveProps(props) {
+ let self = this;
+ if(!this.svg) {
+ let zoom = d3.behavior.zoom()
+ .translate([this.props.maxLabel, 0])
+ .scaleExtent([this.props.minScale, this.props.maxScale])
+ .on("zoom", self.zoom);
+ let svg = this.selectParent().append("svg")
+ .attr("width", this.size.width)
+ .attr("height", this.size.height)
+ .append("g")
+ .call(zoom)
+ .append("g")
+ .attr("transform", "translate(" + this.props.maxLabel + ",0)");
+
+ svg.append("rect")
+ .attr("class", "overlay")
+ .attr("width", this.size.width)
+ .attr("height", this.size.height);
+ // this.svg = d3.select()
+ this.svg = svg;
+ this.props.selectNode(props.data);
+ }
+ if(props.data.hasOwnProperty('type') && !this.props.hasSelected) {
+ this.selectedID = props.data.id;
+ //Commenting out to prevent transmitter push error
+ //this.props.selectNode(props.data);
+ }
+ if(this.svg) {
+ this.update(_.cloneDeep(props.data));
+ // this.selectedID = props.data.id;
+ }
+ }
+
+ wrapperSize() {
+ if (this.props.useDynamicWrapperSize) {
+ try {
+ let wrapper = $(".topologyTreeGraph-body");
+
+ return {
+ width: wrapper.width(),
+ height: wrapper.height()
+ }
+ } catch (e) {
+ console.log("ERROR: cannot get width and/or height from element."+
+ " Using props for width and height. e=", e);
+ return {
+ width: this.props.width,
+ height: this.props.height
+ }
+ }
+ } else {
+ return {
+ width: this.props.width,
+ height: this.props.height
+ }
+ }
+ }
+ selectParent() {
+ return d3.select(document.querySelector('#topology'));
+ }
+ computeRadius(d) {
+ // if(d.parameters && d.parameters.vcpu) {
+ // return this.props.radius + d.parameters.vcpu.total;
+ // } else {
+ return this.props.radius;
+ // }
+ }
+ click = (d) => {
+ this.props.selectNode(d);
+ this.selectedID = d.id;
+ // if (d.children){
+ // d._children = d.children;
+ // d.children = null;
+ // }
+ // else{
+ // d.children = d._children;
+ // d._children = null;
+ // }
+ // this.update(d);
+ }
+ zoom = () => {
+ this.svg.attr("transform", "translate(" + d3.event.translate +
+ ")scale(" + d3.event.scale + ")");
+ }
+ update = (source) => {
+ // Compute the new tree layout.
+ var svg = this.svg;
+ var nodes = this.tree.nodes(source).reverse();
+ var links = this.tree.links(nodes);
+ var self = this;
+
+ // Normalize for fixed-depth.
+ nodes.forEach(function(d) { d.y = d.depth * self.props.maxLabel; });
+ // Update the nodes…
+ var node = svg.selectAll("g.node")
+ .data(nodes, function(d){
+ return d.id || (d.id = ++self.nodeCount);
+ });
+ // Enter any new nodes at the parent's previous position.
+ var nodeEnter = node.enter()
+ .append("g")
+ .attr("class", "node")
+ .attr("transform", function(d){
+ return "translate(" + source.y0 + "," + source.x0 + ")"; })
+ .on("click", this.click);
+
+ nodeEnter.append("circle")
+ .attr("r", 0)
+ .style("fill", function(d){
+ return d._children ? "lightsteelblue" : "white";
+ });
+
+ nodeEnter.append("text")
+ .attr("x", function(d){
+ var spacing = self.computeRadius(d) + 5;
+ return d.children || d._children ? -spacing : spacing; })
+ .attr("transform", function(d, i) {
+ return "translate(0," + ((i%2) ? 15 : -15) + ")"; })
+ .attr("dy", "3")
+ .attr("text-anchor", function(d){
+ return d.children || d._children ? "end" : "start";
+ })
+ .text(function(d){ return d.name; })
+ .style("fill-opacity", 0);
+
+ // Transition nodes to their new position.
+ var nodeUpdate = node
+ .transition()
+ .duration(this.props.duration)
+ .attr("transform", function(d) {
+ return "translate(" + d.y + "," + d.x + ")"; });
+
+ nodeUpdate.select("circle")
+ .attr("r", function(d){ return self.computeRadius(d); })
+ .style("fill", function(d) {
+ return d.id == self.selectedID ? "green" : "lightsteelblue"; });
+
+ nodeUpdate.select("text")
+ .style("fill-opacity", 1)
+ .style("font-weight", function(d) {
+ return d.id == self.selectedID ? "900" : "inherit";
+ })
+ .attr("transform", function(d, i) {
+ return "translate(0," + ((i%2) ? 15 : -15) + ")" +
+ (d.id == self.selectedID ? "scale(1.125)" : "scale(1)");
+ });
+
+ // Transition exiting nodes to the parent's new position.
+ var nodeExit = node.exit()
+ .transition()
+ .duration(this.props.duration)
+ .attr("transform", function(d) {
+ return "translate(" + source.y + "," + source.x + ")"; })
+ .remove();
+
+ nodeExit.select("circle").attr("r", 0);
+ nodeExit.select("text").style("fill-opacity", 0);
+
+ // Update the links…
+ var link = svg.selectAll("path.link")
+ .data(links, function(d){ return d.target.id; });
+
+ // Enter any new links at the parent's previous position.
+ link.enter().insert("path", "g")
+ .attr("class", "link")
+ .attr("d", function(d){
+ var o = {x: source.x0, y: source.y0};
+ return self.diagonal({source: o, target: o});
+ });
+
+ // Transition links to their new position.
+ link
+ .transition()
+ .duration(this.props.duration)
+ .attr("d", self.diagonal);
+
+ // Transition exiting nodes to the parent's new position.
+ link.exit()
+ .transition()
+ .duration(this.props.duration)
+ .attr("d", function(d){
+ var o = {x: source.x, y: source.y};
+ return self.diagonal({source: o, target: o});
+ })
+ .remove();
+
+ // Stash the old positions for transition.
+ nodes.forEach(function(d){
+ d.x0 = d.x;
+ d.y0 = d.y;
+ });
+ }
+ render() {
+ let html = (
+ <DashboardCard className="topologyTreeGraph" showHeader={true} title="Topology Tree"
+ headerExtras={this.props.headerExtras} >
+ <div id="topology"></div>
+ </DashboardCard>
+ );
+ return html;
+ }
+}
+
+TopologyTree.defaultProps = {
+ treeWidth: 800,
+ treeHeight: 500,
+ width: 800,
+ height: 800,
+ minScale: 0.5,
+ maxScale: 2,
+ maxLabel: 150,
+ duration: 500,
+ radius: 5,
+ useDynamicWrapperSize: false,
+ data: {}
+}
diff --git a/skyquake/framework/widgets/topology/topologyTree.scss b/skyquake/framework/widgets/topology/topologyTree.scss
new file mode 100644
index 0000000..f975f8e
--- /dev/null
+++ b/skyquake/framework/widgets/topology/topologyTree.scss
@@ -0,0 +1,8 @@
+.topologyTreeGraph {
+ min-width:800px;
+
+ .overlay {
+ fill:none;
+ pointer-events: all;
+ }
+}
diff --git a/skyquake/framework/widgets/transmit-receive/transmit-receive.js b/skyquake/framework/widgets/transmit-receive/transmit-receive.js
new file mode 100644
index 0000000..ff03af1
--- /dev/null
+++ b/skyquake/framework/widgets/transmit-receive/transmit-receive.js
@@ -0,0 +1,104 @@
+
+/*
+ *
+ * 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 MIXINS = require("../mixins/ButtonEventListener.js");
+
+
+
+
+/**
+ * Transmit and Receive Component
+ * It's props values and a brief description below
+ *
+ *
+ *
+ **/
+module.exports = React.createClass({
+ displayName: 'TransmitReceive',
+ mixins:MIXINS,
+ propTypes: {
+ component_data:React.PropTypes.shape({
+ incoming:React.PropTypes.shape({
+ bytes:React.PropTypes.number,
+ packets:React.PropTypes.number,
+ label:React.PropTypes.string,
+ "byte-rate":React.PropTypes.number,
+ "packet-rate":React.PropTypes.number
+ }),
+ outgoing:React.PropTypes.shape({
+ bytes:React.PropTypes.number,
+ packets:React.PropTypes.number,
+ label:React.PropTypes.string,
+ "byte-rate":React.PropTypes.number,
+ "packet-rate":React.PropTypes.number
+ })
+ })
+ },
+
+ /**
+ * Defines default state.
+ *
+
+ */
+ getInitialState: function() {
+ },
+
+
+ /**
+ * Called when props are changed. Syncs props with state.
+ */
+ componentWillReceiveProps: function(nextProps) {
+
+ },
+
+ /**
+ * Calls the render on the gauge object once the component first mounts
+ */
+ componentDidMount: function() {
+ this.canvasRender(this.state);
+ },
+
+ /**
+ * If any of the state variables have changed, the component should update.
+ * Note, this is where the render step occures for the gauge object.
+ */
+ shouldComponentUpdate: function(nextProps, nextState) {
+
+ },
+
+ /**
+ * Renders the Gauge Component
+ * Returns the canvas element the gauge will be housed in.
+ * @returns {*}
+ */
+ render: function() {
+ var gaugeDOM = React.createElement("div", null,
+ React.createElement("canvas",
+ {className: "rwgauge", style:
+ {width:'100%',height:'100%','maxWidth':this.state.width + 'px','maxHeight':'240px'},
+ ref:'gaugeDOM'
+ }
+ )
+ )
+
+
+
+ return gaugeDOM;
+ }
+});
diff --git a/skyquake/framework/widgets/transmit-receive/transmit-receive.jsx b/skyquake/framework/widgets/transmit-receive/transmit-receive.jsx
new file mode 100644
index 0000000..f8ea287
--- /dev/null
+++ b/skyquake/framework/widgets/transmit-receive/transmit-receive.jsx
@@ -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.
+ *
+ */
+import React from 'react';
+import './transmit-receive.scss'
+class TransmitReceive extends React.Component {
+ constructor(props) {
+ super(props)
+ }
+ // getDigits(number) {
+ // return Math.log(number) * Math.LOG10E + 1 | 0;
+ // }
+ getUnits(number) {
+ if (number < Math.pow(10,3)) {
+ return [number, ''];
+ } else if (number < Math.pow(10,6)) {
+ return [(number / Math.pow(10,3)), 'K'];
+ } else if (number < Math.pow(10,9)) {
+ return [(number / Math.pow(10,6)), 'M'];
+ } else if (number < Math.pow(10,12)) {
+ return [(number / Math.pow(10,9)), 'G'];
+ } else if (number < Math.pow(10,15)) {
+ return [(number / Math.pow(10,12)), 'T'];
+ } else if (number < Math.pow(10,18)) {
+ return [(number / Math.pow(10,15)), 'P'];
+ } else if (number < Math.pow(10,21)) {
+ return [(number / Math.pow(10,18)), 'E'];
+ } else if (number < Math.pow(10,24)) {
+ return [(number / Math.pow(10,21)), 'Z'];
+ } else if (number < Math.pow(10,27)) {
+ return [(number / Math.pow(10,24)), 'Z'];
+ } else {
+ return [(number / Math.pow(10,27)), 'Y'];
+ }
+ }
+
+ shouldComponentUpdate(nextProps, nextState) {
+ if (JSON.stringify(this.props.data) === JSON.stringify(nextProps.data)) {
+ return false;
+ }
+ return true;
+ }
+
+ render() {
+ let html;
+
+ html = (
+ <div className="transmit-receive-container">
+ <div className="transmit-container">
+ <div className="label">{this.props.data.outgoing.label}</div>
+ <div className="measure">{this.getUnits(this.props.data.outgoing.bytes)[0].toFixed(1)}</div><div className="units">{this.getUnits(this.props.data.outgoing.bytes)[1]}B</div>
+ <div className="measure">{this.getUnits(this.props.data.outgoing.packets)[0].toFixed(1)}</div><div className="units">{this.getUnits(this.props.data.outgoing.packets)[1]}P</div>
+ <div className="measure">{this.getUnits(this.props.data.outgoing['byte-rate'])[0].toFixed(1)}</div><div className="units">{this.getUnits(this.props.data.outgoing['byte-rate'])[1]}BPS</div>
+ <div className="measure">{this.getUnits(this.props.data.outgoing['packet-rate'])[0].toFixed(1)}</div><div className="units">{this.getUnits(this.props.data.outgoing['packet-rate'])[1]}PPS</div>
+ </div>
+ <div className="receive-container">
+ <div className="label">{this.props.data.incoming.label}</div>
+ <div className="measure">{this.getUnits(this.props.data.incoming.bytes)[0].toFixed(1)}</div><div className="units">{this.getUnits(this.props.data.incoming.bytes)[1]}B</div>
+ <div className="measure">{this.getUnits(this.props.data.incoming.packets)[0].toFixed(1)}</div><div className="units">{this.getUnits(this.props.data.incoming.packets)[1]}P</div>
+ <div className="measure">{this.getUnits(this.props.data.incoming['byte-rate'])[0].toFixed(1)}</div><div className="units">{this.getUnits(this.props.data.incoming['byte-rate'])[1]}BPS</div>
+ <div className="measure">{this.getUnits(this.props.data.incoming['packet-rate'])[0].toFixed(1)}</div><div className="units">{this.getUnits(this.props.data.incoming['packet-rate'])[1]}PPS</div>
+ </div>
+ </div>
+ );
+ return html;
+ }
+}
+
+
+export default TransmitReceive;
diff --git a/skyquake/framework/widgets/transmit-receive/transmit-receive.scss b/skyquake/framework/widgets/transmit-receive/transmit-receive.scss
new file mode 100644
index 0000000..c1336a6
--- /dev/null
+++ b/skyquake/framework/widgets/transmit-receive/transmit-receive.scss
@@ -0,0 +1,49 @@
+
+/*
+ *
+ * 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.
+ *
+ */
+.transmit-receive-container {
+ flex: 1 0;
+ display: flex;
+ flex-direction: row;
+ justify-content: space-around;
+ font-weight:bold;
+ text-align:center;
+ margin: 10px 0px;
+ padding: 0px 10px 0px 10px;
+ background-color:rgb(230,230,230);
+
+ .transmit-container, .receive-container {
+ padding:10px;
+ background-color:rgb(219,219,219);
+ }
+ .transmit-container {
+ margin-right:3px;
+ }
+ .units {
+ font-size:.5rem;
+ margin-bottom:5px;
+
+ }
+ .measure {
+ font-size:1.2rem;
+ }
+ .label {
+ font-size:.8rem;
+ margin-bottom:10px;
+ }
+ }
diff --git a/skyquake/framework/widgets/uptime/uptime.jsx b/skyquake/framework/widgets/uptime/uptime.jsx
new file mode 100644
index 0000000..39b5faf
--- /dev/null
+++ b/skyquake/framework/widgets/uptime/uptime.jsx
@@ -0,0 +1,173 @@
+/*
+ *
+ * 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';
+
+function debugDump(funcName, state, props) {
+ console.log("UpTime." + funcName + ": called");
+ console.log(" -> props = ", props);
+ console.log(" -> state = ", state);
+ }
+
+/**
+ * Uptime Compoeent
+ * Accepts two properties:
+ * initialtime {number} number of milliseconds.
+ * run {boolean} determines whether the uptime ticker should run
+ */
+export default class UpTime extends React.Component {
+
+ constructor(props) {
+ super(props);
+ if (props.debugMode) {
+ console.log("UpTime.constructor called");
+ }
+ let initialTime = Math.floor(props.initialTime);
+ this.state = {
+ run: props.run,
+ initialTime: initialTime,
+ time: this.handleConvert(this.calculateDuration({
+ intialTime: initialTime
+ })),
+ noisySeconds: props.noisySeconds,
+ debugMode: props.debugMode
+ }
+ this.tick;
+ this.handleConvert = this.handleConvert.bind(this);
+ this.updateTime = this.updateTime.bind(this);
+ if (props.debugMode) {
+ debugDump("constructor", this.state, props);
+ }
+ }
+
+ componentWillReceiveProps(nextProps) {
+ if (this.state.debugMode) {
+ debugDump("componentWillReceiveProps", this.state, nextProps);
+ }
+ let initialTime = Math.floor(nextProps.initialTime);
+ if (initialTime > this.state.initialTime) {
+ initialTime = this.state.initialTime;
+ }
+ this.state = {
+ initialTime: initialTime,
+ time: this.handleConvert(this.calculateDuration({
+ intialTime: initialTime
+ }))
+ }
+ }
+
+ calculateDuration(args={}) {
+ let initialTime = args.initialTime;
+ if (!initialTime) {
+ initialTime = this.state && this.state.initialTime
+ ? this.state.initialTime
+ : 0;
+ }
+ let timeNow = args.timeNow ? args.timeNow : Date.now();
+ if (initialTime) {
+ return Math.floor((timeNow/ 1000)) - initialTime;
+ } else {
+ return 0;
+ }
+ }
+
+ handleConvert(input) {
+ var ret = {
+ days: 0,
+ hours: 0,
+ minutes: 0,
+ seconds: 0
+ };
+ if (input == "inactive" || typeof(input) === 'undefined') {
+ ret.seconds = -1;
+ } else if (input !== "" && input != "Expired") {
+ input = Math.floor(input);
+ ret.seconds = input % 60;
+ ret.minutes = Math.floor(input / 60) % 60;
+ ret.hours = Math.floor(input / 3600) % 24;
+ ret.days = Math.floor(input / 86400);
+ }
+ return ret;
+ }
+
+ toString() {
+ var self = this;
+ var ret = "";
+ var unitsRendered = [];
+
+ if (self.state.time.days > 0) {
+ ret += self.state.time.days + "d:";
+ unitsRendered.push("d");
+ }
+
+ if (self.state.time.hours > 0 || unitsRendered.length > 0) {
+ ret += self.state.time.hours + "h:";
+ unitsRendered.push("h");
+ }
+
+ if (self.state.time.minutes > 0 || unitsRendered.length > 0) {
+ ret += self.state.time.minutes + "m:";
+ unitsRendered.push("m");
+ }
+
+ if (self.props.noisySeconds || unitsRendered.length == 0
+ || unitsRendered.indexOf('m') == 0)
+ {
+ // console.log(" -> toString adding seconds: ", self.state.time.seconds);
+ ret += self.state.time.seconds + "s";
+ }
+
+ if (ret.endsWith(':')) {
+ ret = ret.slice(0,-1);
+ }
+ if (ret.length > 0) {
+ return ret;
+ } else {
+ return "--";
+ }
+ }
+
+ updateTime() {
+ if (this.state.initialTime) {
+ this.setState({
+ time: this.handleConvert(this.calculateDuration())
+ });
+ }
+ }
+
+ componentDidMount() {
+ var self = this;
+ if (self.state.run) {
+ clearInterval(self.tick);
+ self.tick = setInterval(self.updateTime, 250);
+ }
+ }
+
+ componentWillUnmount() {
+ clearInterval(this.tick);
+ }
+
+ render() {
+ return (<span>{this.toString()}</span>);
+ }
+}
+UpTime.defaultProps = {
+ noisySeconds: false,
+ caller: '',
+ systemId: '' // System identifyer for uptime (e.g.: VM, NS, or process)
+}
+