├── src ├── main │ ├── webapp │ │ ├── configurator │ │ │ ├── conf_setup_instructions │ │ │ │ ├── js │ │ │ │ │ └── scripts.js │ │ │ │ ├── styles │ │ │ │ │ └── styles.css │ │ │ │ └── Setup.jsx │ │ │ ├── img │ │ │ │ ├── logo.png │ │ │ │ └── appstore-logo.png │ │ │ └── config.js │ │ ├── commons │ │ │ ├── img │ │ │ │ └── spinner.gif │ │ │ ├── components │ │ │ │ ├── SuccessPostingLocation │ │ │ │ │ ├── styles │ │ │ │ │ │ └── styles.css │ │ │ │ │ └── SuccessPostingLocation.jsx │ │ │ │ ├── SubmitWebHook │ │ │ │ │ ├── styles │ │ │ │ │ │ └── styles.css │ │ │ │ │ └── SubmitWebHook.jsx │ │ │ │ ├── Warning │ │ │ │ │ ├── styles │ │ │ │ │ │ └── warning.css │ │ │ │ │ └── Warning.jsx │ │ │ │ ├── SetupInstructions │ │ │ │ │ └── SetupInstructions.jsx │ │ │ │ ├── WebHookURL │ │ │ │ │ └── WebHookURL.jsx │ │ │ │ ├── IntegrationIdentity │ │ │ │ │ └── IntegrationIdentity.jsx │ │ │ │ ├── CreatePostingLocation │ │ │ │ │ └── CreatePostingLocation.jsx │ │ │ │ ├── EditPostingLocation │ │ │ │ │ └── EditPostingLocation.jsx │ │ │ │ └── SearchRooms │ │ │ │ │ └── SearchRooms.jsx │ │ │ ├── views │ │ │ │ ├── Home.js │ │ │ │ ├── Success.js │ │ │ │ ├── CreateView.js │ │ │ │ ├── RemoveView.js │ │ │ │ ├── EditView.js │ │ │ │ ├── ListView.js │ │ │ │ └── SaveWebHook.js │ │ │ ├── html │ │ │ │ ├── controller.html │ │ │ │ └── app.html │ │ │ ├── js │ │ │ │ ├── utils.service.js │ │ │ │ ├── controller.js │ │ │ │ ├── configurator.service.js │ │ │ │ └── app.js │ │ │ └── styles │ │ │ │ └── styles.css │ │ ├── vendors │ │ │ └── font-awesome-4.6.3 │ │ │ │ ├── fonts │ │ │ │ ├── FontAwesome.otf │ │ │ │ ├── fontawesome-webfont.eot │ │ │ │ ├── fontawesome-webfont.ttf │ │ │ │ ├── fontawesome-webfont.woff │ │ │ │ └── fontawesome-webfont.woff2 │ │ │ │ ├── less │ │ │ │ ├── fixed-width.less │ │ │ │ ├── screen-reader.less │ │ │ │ ├── larger.less │ │ │ │ ├── list.less │ │ │ │ ├── core.less │ │ │ │ ├── stacked.less │ │ │ │ ├── font-awesome.less │ │ │ │ ├── bordered-pulled.less │ │ │ │ ├── rotated-flipped.less │ │ │ │ ├── path.less │ │ │ │ ├── animated.less │ │ │ │ ├── mixins.less │ │ │ │ └── variables.less │ │ │ │ ├── scss │ │ │ │ ├── _fixed-width.scss │ │ │ │ ├── _screen-reader.scss │ │ │ │ ├── _larger.scss │ │ │ │ ├── _list.scss │ │ │ │ ├── _core.scss │ │ │ │ ├── font-awesome.scss │ │ │ │ ├── _stacked.scss │ │ │ │ ├── _bordered-pulled.scss │ │ │ │ ├── _rotated-flipped.scss │ │ │ │ ├── _path.scss │ │ │ │ ├── _animated.scss │ │ │ │ ├── _mixins.scss │ │ │ │ └── _variables.scss │ │ │ │ └── HELP-US-OUT.txt │ │ ├── bundle.json │ │ ├── package.json │ │ └── gulpfile.js │ ├── resources │ │ ├── logo.png │ │ └── application-hubspot.yml │ └── java │ │ └── org │ │ └── symphonyoss │ │ └── integration │ │ └── webhook │ │ └── hubspot │ │ └── HubspotWebHookIntegration.java └── test │ ├── resources │ └── log4j2.xml │ └── java │ └── org │ └── symphonyoss │ └── integration │ └── webhook │ └── hubspot │ └── HubspotWebHookIntegrationTest.java ├── local-run ├── application.yaml.template └── env.sh.sample ├── .gitignore ├── NOTICE ├── run.sh ├── README.md ├── .travis.yml ├── pom.xml └── LICENSE /src/main/webapp/configurator/conf_setup_instructions/js/scripts.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | -------------------------------------------------------------------------------- /src/main/resources/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symphonyoss/symphony-hubspot/dev/src/main/resources/logo.png -------------------------------------------------------------------------------- /src/main/webapp/commons/img/spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symphonyoss/symphony-hubspot/dev/src/main/webapp/commons/img/spinner.gif -------------------------------------------------------------------------------- /src/main/webapp/configurator/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symphonyoss/symphony-hubspot/dev/src/main/webapp/configurator/img/logo.png -------------------------------------------------------------------------------- /src/main/webapp/commons/components/SuccessPostingLocation/styles/styles.css: -------------------------------------------------------------------------------- 1 | .search-results input.input-posting-location { 2 | border: none !important; 3 | } -------------------------------------------------------------------------------- /src/main/webapp/configurator/img/appstore-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symphonyoss/symphony-hubspot/dev/src/main/webapp/configurator/img/appstore-logo.png -------------------------------------------------------------------------------- /src/main/webapp/vendors/font-awesome-4.6.3/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symphonyoss/symphony-hubspot/dev/src/main/webapp/vendors/font-awesome-4.6.3/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /src/main/webapp/vendors/font-awesome-4.6.3/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symphonyoss/symphony-hubspot/dev/src/main/webapp/vendors/font-awesome-4.6.3/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /src/main/webapp/vendors/font-awesome-4.6.3/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symphonyoss/symphony-hubspot/dev/src/main/webapp/vendors/font-awesome-4.6.3/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /src/main/webapp/vendors/font-awesome-4.6.3/less/fixed-width.less: -------------------------------------------------------------------------------- 1 | // Fixed Width Icons 2 | // ------------------------- 3 | .@{fa-css-prefix}-fw { 4 | width: (18em / 14); 5 | text-align: center; 6 | } 7 | -------------------------------------------------------------------------------- /src/main/webapp/vendors/font-awesome-4.6.3/less/screen-reader.less: -------------------------------------------------------------------------------- 1 | // Screen Readers 2 | // ------------------------- 3 | 4 | .sr-only { .sr-only(); } 5 | .sr-only-focusable { .sr-only-focusable(); } 6 | -------------------------------------------------------------------------------- /src/main/webapp/vendors/font-awesome-4.6.3/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symphonyoss/symphony-hubspot/dev/src/main/webapp/vendors/font-awesome-4.6.3/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /src/main/webapp/vendors/font-awesome-4.6.3/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symphonyoss/symphony-hubspot/dev/src/main/webapp/vendors/font-awesome-4.6.3/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /src/main/webapp/vendors/font-awesome-4.6.3/scss/_fixed-width.scss: -------------------------------------------------------------------------------- 1 | // Fixed Width Icons 2 | // ------------------------- 3 | .#{$fa-css-prefix}-fw { 4 | width: (18em / 14); 5 | text-align: center; 6 | } 7 | -------------------------------------------------------------------------------- /src/main/webapp/vendors/font-awesome-4.6.3/scss/_screen-reader.scss: -------------------------------------------------------------------------------- 1 | // Screen Readers 2 | // ------------------------- 3 | 4 | .sr-only { @include sr-only(); } 5 | .sr-only-focusable { @include sr-only-focusable(); } 6 | -------------------------------------------------------------------------------- /local-run/application.yaml.template: -------------------------------------------------------------------------------- 1 | spring: 2 | profiles: 3 | active: <> 4 | 5 | applications: 6 | <>: 7 | state: PROVISIONED 8 | keystore: 9 | file: <> 10 | password: <> 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Local run 2 | certs/ 3 | env.sh 4 | application.yaml 5 | tomcat/ 6 | ngrok 7 | ngrok-*.zip 8 | ngrok.log 9 | 10 | # Maven 11 | target/ 12 | 13 | # Intellij 14 | .idea 15 | *.iml 16 | 17 | # NPM 18 | src/main/webapp/node_modules 19 | -------------------------------------------------------------------------------- /src/main/webapp/commons/views/Home.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | class Home extends React.Component { 4 | render() { 5 | return ( 6 |
7 | {this.props.children} 8 |
9 | ); 10 | } 11 | } 12 | export default Home; -------------------------------------------------------------------------------- /src/main/webapp/configurator/config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | app_title: "Configure Hubspot Webhook Integration", 3 | app_name: "Hubspot Webhook", 4 | IM_shorthand: "HWHIB", // will display "One on One with UWHIB" 5 | toogleSetupInstructions: false 6 | } 7 | -------------------------------------------------------------------------------- /src/main/resources/application-hubspot.yml: -------------------------------------------------------------------------------- 1 | applications: 2 | hubspot: 3 | component: hubspotWebHookIntegration 4 | name: Hubspot Webhook 5 | description: "Receive Hubspot notifications in Symphony chat rooms" 6 | publisher: Symphony 7 | context: hubspot 8 | enable: true 9 | visible: true -------------------------------------------------------------------------------- /src/main/webapp/configurator/conf_setup_instructions/styles/styles.css: -------------------------------------------------------------------------------- 1 | /* setup instructions */ 2 | .setup-instructions { 3 | max-height: 128px; 4 | margin-bottom: 10px; 5 | overflow: hidden; 6 | transition: max-height 0.7s; 7 | } 8 | 9 | .setup-instructions-transition { 10 | max-height: 1570px; 11 | } 12 | -------------------------------------------------------------------------------- /src/main/webapp/vendors/font-awesome-4.6.3/HELP-US-OUT.txt: -------------------------------------------------------------------------------- 1 | I hope you love Font Awesome. If you've found it useful, please do me a favor and check out my latest project, 2 | Fort Awesome (https://fortawesome.com). It makes it easy to put the perfect icons on your website. Choose from our awesome, 3 | comprehensive icon sets or copy and paste your own. 4 | 5 | Please. Check it out. 6 | 7 | -Dave Gandy 8 | -------------------------------------------------------------------------------- /src/main/webapp/vendors/font-awesome-4.6.3/less/larger.less: -------------------------------------------------------------------------------- 1 | // Icon Sizes 2 | // ------------------------- 3 | 4 | /* makes the font 33% larger relative to the icon container */ 5 | .@{fa-css-prefix}-lg { 6 | font-size: (4em / 3); 7 | line-height: (3em / 4); 8 | vertical-align: -15%; 9 | } 10 | .@{fa-css-prefix}-2x { font-size: 2em; } 11 | .@{fa-css-prefix}-3x { font-size: 3em; } 12 | .@{fa-css-prefix}-4x { font-size: 4em; } 13 | .@{fa-css-prefix}-5x { font-size: 5em; } 14 | -------------------------------------------------------------------------------- /src/main/webapp/vendors/font-awesome-4.6.3/scss/_larger.scss: -------------------------------------------------------------------------------- 1 | // Icon Sizes 2 | // ------------------------- 3 | 4 | /* makes the font 33% larger relative to the icon container */ 5 | .#{$fa-css-prefix}-lg { 6 | font-size: (4em / 3); 7 | line-height: (3em / 4); 8 | vertical-align: -15%; 9 | } 10 | .#{$fa-css-prefix}-2x { font-size: 2em; } 11 | .#{$fa-css-prefix}-3x { font-size: 3em; } 12 | .#{$fa-css-prefix}-4x { font-size: 4em; } 13 | .#{$fa-css-prefix}-5x { font-size: 5em; } 14 | -------------------------------------------------------------------------------- /src/main/webapp/commons/html/controller.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/main/webapp/vendors/font-awesome-4.6.3/less/list.less: -------------------------------------------------------------------------------- 1 | // List Icons 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-ul { 5 | padding-left: 0; 6 | margin-left: @fa-li-width; 7 | list-style-type: none; 8 | > li { position: relative; } 9 | } 10 | .@{fa-css-prefix}-li { 11 | position: absolute; 12 | left: -@fa-li-width; 13 | width: @fa-li-width; 14 | top: (2em / 14); 15 | text-align: center; 16 | &.@{fa-css-prefix}-lg { 17 | left: (-@fa-li-width + (4em / 14)); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/webapp/vendors/font-awesome-4.6.3/scss/_list.scss: -------------------------------------------------------------------------------- 1 | // List Icons 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-ul { 5 | padding-left: 0; 6 | margin-left: $fa-li-width; 7 | list-style-type: none; 8 | > li { position: relative; } 9 | } 10 | .#{$fa-css-prefix}-li { 11 | position: absolute; 12 | left: -$fa-li-width; 13 | width: $fa-li-width; 14 | top: (2em / 14); 15 | text-align: center; 16 | &.#{$fa-css-prefix}-lg { 17 | left: -$fa-li-width + (4em / 14); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/webapp/vendors/font-awesome-4.6.3/less/core.less: -------------------------------------------------------------------------------- 1 | // Base Class Definition 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix} { 5 | display: inline-block; 6 | font: normal normal normal @fa-font-size-base/@fa-line-height-base FontAwesome; // shortening font declaration 7 | font-size: inherit; // can't have font-size inherit on line above, so need to override 8 | text-rendering: auto; // optimizelegibility throws things off #1094 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/webapp/vendors/font-awesome-4.6.3/scss/_core.scss: -------------------------------------------------------------------------------- 1 | // Base Class Definition 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix} { 5 | display: inline-block; 6 | font: normal normal normal #{$fa-font-size-base}/#{$fa-line-height-base} FontAwesome; // shortening font declaration 7 | font-size: inherit; // can't have font-size inherit on line above, so need to override 8 | text-rendering: auto; // optimizelegibility throws things off #1094 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/webapp/vendors/font-awesome-4.6.3/scss/font-awesome.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome 4.6.3 by @davegandy - http://fontawesome.io - @fontawesome 3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) 4 | */ 5 | 6 | @import "variables"; 7 | @import "mixins"; 8 | @import "path"; 9 | @import "core"; 10 | @import "larger"; 11 | @import "fixed-width"; 12 | @import "list"; 13 | @import "bordered-pulled"; 14 | @import "animated"; 15 | @import "rotated-flipped"; 16 | @import "stacked"; 17 | @import "icons"; 18 | @import "screen-reader"; 19 | -------------------------------------------------------------------------------- /src/test/resources/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/main/webapp/configurator/conf_setup_instructions/Setup.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './styles/styles.css'; 4 | import './js/scripts.js'; 5 | export default class Setup extends React.Component { 6 | render() { 7 | return( 8 |
9 | {/* Start editing area */} 10 |

Refer to the Universal Webhook to learn more how to build your own webhook.

11 | {/* End editing area */} 12 |
13 | ); 14 | } 15 | } -------------------------------------------------------------------------------- /src/main/webapp/bundle.json: -------------------------------------------------------------------------------- 1 | { 2 | "applications": [ 3 | { 4 | "type": "sandbox", 5 | "id": "hubspotWebHookIntegration", 6 | "name": "Hubspot Webhook", 7 | "blurb": "Hubspot Webhook in Development Mode", 8 | "publisher": "Symphony", 9 | "url": "https://localhost.symphony.com/apps/hubspot/controller.html?configurationId=5889cdafe4b06d92cebc31e3&botUserId=7627861919708&id=hubspotWebHookIntegration&context=apps/hubspot", 10 | "icon": "https://localhost.symphony.com/apps/hubspot/img/appstore-logo.png", 11 | "domain": ".symphony.com" 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /src/main/webapp/vendors/font-awesome-4.6.3/less/stacked.less: -------------------------------------------------------------------------------- 1 | // Stacked Icons 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-stack { 5 | position: relative; 6 | display: inline-block; 7 | width: 2em; 8 | height: 2em; 9 | line-height: 2em; 10 | vertical-align: middle; 11 | } 12 | .@{fa-css-prefix}-stack-1x, .@{fa-css-prefix}-stack-2x { 13 | position: absolute; 14 | left: 0; 15 | width: 100%; 16 | text-align: center; 17 | } 18 | .@{fa-css-prefix}-stack-1x { line-height: inherit; } 19 | .@{fa-css-prefix}-stack-2x { font-size: 2em; } 20 | .@{fa-css-prefix}-inverse { color: @fa-inverse; } 21 | -------------------------------------------------------------------------------- /src/main/webapp/vendors/font-awesome-4.6.3/scss/_stacked.scss: -------------------------------------------------------------------------------- 1 | // Stacked Icons 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-stack { 5 | position: relative; 6 | display: inline-block; 7 | width: 2em; 8 | height: 2em; 9 | line-height: 2em; 10 | vertical-align: middle; 11 | } 12 | .#{$fa-css-prefix}-stack-1x, .#{$fa-css-prefix}-stack-2x { 13 | position: absolute; 14 | left: 0; 15 | width: 100%; 16 | text-align: center; 17 | } 18 | .#{$fa-css-prefix}-stack-1x { line-height: inherit; } 19 | .#{$fa-css-prefix}-stack-2x { font-size: 2em; } 20 | .#{$fa-css-prefix}-inverse { color: $fa-inverse; } 21 | -------------------------------------------------------------------------------- /src/main/webapp/vendors/font-awesome-4.6.3/less/font-awesome.less: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome 4.6.3 by @davegandy - http://fontawesome.io - @fontawesome 3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) 4 | */ 5 | 6 | @import "variables.less"; 7 | @import "mixins.less"; 8 | @import "path.less"; 9 | @import "core.less"; 10 | @import "larger.less"; 11 | @import "fixed-width.less"; 12 | @import "list.less"; 13 | @import "bordered-pulled.less"; 14 | @import "animated.less"; 15 | @import "rotated-flipped.less"; 16 | @import "stacked.less"; 17 | @import "icons.less"; 18 | @import "screen-reader.less"; 19 | -------------------------------------------------------------------------------- /src/main/webapp/commons/components/SubmitWebHook/styles/styles.css: -------------------------------------------------------------------------------- 1 | /* Submit Webhook */ 2 | .submit-webhook { 3 | margin: 0 auto; 4 | width: 98%; 5 | height: auto; 6 | padding: 5px 0 0 0; 7 | } 8 | .dark .submit-webhook, 9 | .dark-contrast .submit-webhook { 10 | border-top: 1px solid rgba(255,255,255,0.8); 11 | } 12 | .light .submit-webhook, 13 | .light-contrast .submit-webhook { 14 | border-top: 1px solid rgba(0,0,0,0.1); 15 | } 16 | .submit-webhook .btn-container { 17 | float: left; 18 | width: 100%; 19 | } 20 | .submit-webhook button { 21 | float: right; 22 | margin: 15px 0 0 25px !important; 23 | min-width: 100px; 24 | } -------------------------------------------------------------------------------- /src/main/webapp/commons/html/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 |
12 |
13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/main/webapp/commons/components/Warning/styles/warning.css: -------------------------------------------------------------------------------- 1 | .warning-box { 2 | display: flex; 3 | flex-direction: row; 4 | justify-content: space-between; 5 | position: relative; 6 | top: 0; 7 | left: 0; 8 | width: 100%; 9 | padding: 10px; 10 | margin: 0 0 4px 0; 11 | border-radius: 4px; 12 | box-sizing: border-box; 13 | color: #000; 14 | } 15 | .warning-box.warning { 16 | background: #fcf8e3; 17 | } 18 | .warning-box.error { 19 | background: #f00; 20 | } 21 | .warning-box.success { 22 | background: #7ce0bb; 23 | } 24 | .warning-box.required { 25 | background: #FE7A7D; 26 | } 27 | .warning-box div { 28 | max-width: 90%; 29 | } 30 | .warning-box div a { 31 | color: #000; 32 | } -------------------------------------------------------------------------------- /src/main/webapp/vendors/font-awesome-4.6.3/less/bordered-pulled.less: -------------------------------------------------------------------------------- 1 | // Bordered & Pulled 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-border { 5 | padding: .2em .25em .15em; 6 | border: solid .08em @fa-border-color; 7 | border-radius: .1em; 8 | } 9 | 10 | .@{fa-css-prefix}-pull-left { float: left; } 11 | .@{fa-css-prefix}-pull-right { float: right; } 12 | 13 | .@{fa-css-prefix} { 14 | &.@{fa-css-prefix}-pull-left { margin-right: .3em; } 15 | &.@{fa-css-prefix}-pull-right { margin-left: .3em; } 16 | } 17 | 18 | /* Deprecated as of 4.4.0 */ 19 | .pull-right { float: right; } 20 | .pull-left { float: left; } 21 | 22 | .@{fa-css-prefix} { 23 | &.pull-left { margin-right: .3em; } 24 | &.pull-right { margin-left: .3em; } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/webapp/vendors/font-awesome-4.6.3/scss/_bordered-pulled.scss: -------------------------------------------------------------------------------- 1 | // Bordered & Pulled 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-border { 5 | padding: .2em .25em .15em; 6 | border: solid .08em $fa-border-color; 7 | border-radius: .1em; 8 | } 9 | 10 | .#{$fa-css-prefix}-pull-left { float: left; } 11 | .#{$fa-css-prefix}-pull-right { float: right; } 12 | 13 | .#{$fa-css-prefix} { 14 | &.#{$fa-css-prefix}-pull-left { margin-right: .3em; } 15 | &.#{$fa-css-prefix}-pull-right { margin-left: .3em; } 16 | } 17 | 18 | /* Deprecated as of 4.4.0 */ 19 | .pull-right { float: right; } 20 | .pull-left { float: left; } 21 | 22 | .#{$fa-css-prefix} { 23 | &.pull-left { margin-right: .3em; } 24 | &.pull-right { margin-left: .3em; } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/webapp/vendors/font-awesome-4.6.3/less/rotated-flipped.less: -------------------------------------------------------------------------------- 1 | // Rotated & Flipped Icons 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-rotate-90 { .fa-icon-rotate(90deg, 1); } 5 | .@{fa-css-prefix}-rotate-180 { .fa-icon-rotate(180deg, 2); } 6 | .@{fa-css-prefix}-rotate-270 { .fa-icon-rotate(270deg, 3); } 7 | 8 | .@{fa-css-prefix}-flip-horizontal { .fa-icon-flip(-1, 1, 0); } 9 | .@{fa-css-prefix}-flip-vertical { .fa-icon-flip(1, -1, 2); } 10 | 11 | // Hook for IE8-9 12 | // ------------------------- 13 | 14 | :root .@{fa-css-prefix}-rotate-90, 15 | :root .@{fa-css-prefix}-rotate-180, 16 | :root .@{fa-css-prefix}-rotate-270, 17 | :root .@{fa-css-prefix}-flip-horizontal, 18 | :root .@{fa-css-prefix}-flip-vertical { 19 | filter: none; 20 | } 21 | -------------------------------------------------------------------------------- /src/main/webapp/vendors/font-awesome-4.6.3/scss/_rotated-flipped.scss: -------------------------------------------------------------------------------- 1 | // Rotated & Flipped Icons 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-rotate-90 { @include fa-icon-rotate(90deg, 1); } 5 | .#{$fa-css-prefix}-rotate-180 { @include fa-icon-rotate(180deg, 2); } 6 | .#{$fa-css-prefix}-rotate-270 { @include fa-icon-rotate(270deg, 3); } 7 | 8 | .#{$fa-css-prefix}-flip-horizontal { @include fa-icon-flip(-1, 1, 0); } 9 | .#{$fa-css-prefix}-flip-vertical { @include fa-icon-flip(1, -1, 2); } 10 | 11 | // Hook for IE8-9 12 | // ------------------------- 13 | 14 | :root .#{$fa-css-prefix}-rotate-90, 15 | :root .#{$fa-css-prefix}-rotate-180, 16 | :root .#{$fa-css-prefix}-rotate-270, 17 | :root .#{$fa-css-prefix}-flip-horizontal, 18 | :root .#{$fa-css-prefix}-flip-vertical { 19 | filter: none; 20 | } 21 | -------------------------------------------------------------------------------- /local-run/env.sh.sample: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Tomcat and pod provisioning configurations 4 | 5 | export INTEGRATION_BRIDGE_DOMAIN=".ngrok.io" 6 | export INTEGRATION_BRIDGE_HOST=myid.ngrok.io 7 | export INTEGRATION_BRIDGE_HTTP_PORT=8080 8 | 9 | export POD_HOST=foundation-dev.symphony.com 10 | export POD_PORT=443 11 | export AGENT_HOST=foundation-dev-api.symphony.com 12 | export AGENT_PORT=443 13 | export KEY_MANAGER_HOST=foundation-dev.symphony.com 14 | export KEY_MANAGER_PORT=443 15 | export KEY_MANAGER_AUTH_HOST=foundation-dev-api.symphony.com 16 | export KEY_MANAGER_AUTH_PORT=443 17 | export POD_SESSION_MANAGER_HOST=foundation-dev-api.symphony.com 18 | export POD_SESSION_MANAGER_PORT=443 19 | 20 | export APP_ID=universal 21 | export CERT_FILE=universal.p12 22 | export CERT_PWD=changeit 23 | 24 | export RUN_SH_SKIP_BUILD=false 25 | export NGROK_TOKEN= 26 | -------------------------------------------------------------------------------- /src/main/webapp/vendors/font-awesome-4.6.3/less/path.less: -------------------------------------------------------------------------------- 1 | /* FONT PATH 2 | * -------------------------- */ 3 | 4 | @font-face { 5 | font-family: 'FontAwesome'; 6 | src: url('@{fa-font-path}/fontawesome-webfont.eot?v=@{fa-version}'); 7 | src: url('@{fa-font-path}/fontawesome-webfont.eot?#iefix&v=@{fa-version}') format('embedded-opentype'), 8 | url('@{fa-font-path}/fontawesome-webfont.woff2?v=@{fa-version}') format('woff2'), 9 | url('@{fa-font-path}/fontawesome-webfont.woff?v=@{fa-version}') format('woff'), 10 | url('@{fa-font-path}/fontawesome-webfont.ttf?v=@{fa-version}') format('truetype'), 11 | url('@{fa-font-path}/fontawesome-webfont.svg?v=@{fa-version}#fontawesomeregular') format('svg'); 12 | // src: url('@{fa-font-path}/FontAwesome.otf') format('opentype'); // used when developing fonts 13 | font-weight: normal; 14 | font-style: normal; 15 | } 16 | -------------------------------------------------------------------------------- /src/main/webapp/vendors/font-awesome-4.6.3/scss/_path.scss: -------------------------------------------------------------------------------- 1 | /* FONT PATH 2 | * -------------------------- */ 3 | 4 | @font-face { 5 | font-family: 'FontAwesome'; 6 | src: url('#{$fa-font-path}/fontawesome-webfont.eot?v=#{$fa-version}'); 7 | src: url('#{$fa-font-path}/fontawesome-webfont.eot?#iefix&v=#{$fa-version}') format('embedded-opentype'), 8 | url('#{$fa-font-path}/fontawesome-webfont.woff2?v=#{$fa-version}') format('woff2'), 9 | url('#{$fa-font-path}/fontawesome-webfont.woff?v=#{$fa-version}') format('woff'), 10 | url('#{$fa-font-path}/fontawesome-webfont.ttf?v=#{$fa-version}') format('truetype'), 11 | url('#{$fa-font-path}/fontawesome-webfont.svg?v=#{$fa-version}#fontawesomeregular') format('svg'); 12 | // src: url('#{$fa-font-path}/FontAwesome.otf') format('opentype'); // used when developing fonts 13 | font-weight: normal; 14 | font-style: normal; 15 | } 16 | -------------------------------------------------------------------------------- /src/main/webapp/vendors/font-awesome-4.6.3/less/animated.less: -------------------------------------------------------------------------------- 1 | // Animated Icons 2 | // -------------------------- 3 | 4 | .@{fa-css-prefix}-spin { 5 | -webkit-animation: fa-spin 2s infinite linear; 6 | animation: fa-spin 2s infinite linear; 7 | } 8 | 9 | .@{fa-css-prefix}-pulse { 10 | -webkit-animation: fa-spin 1s infinite steps(8); 11 | animation: fa-spin 1s infinite steps(8); 12 | } 13 | 14 | @-webkit-keyframes fa-spin { 15 | 0% { 16 | -webkit-transform: rotate(0deg); 17 | transform: rotate(0deg); 18 | } 19 | 100% { 20 | -webkit-transform: rotate(359deg); 21 | transform: rotate(359deg); 22 | } 23 | } 24 | 25 | @keyframes fa-spin { 26 | 0% { 27 | -webkit-transform: rotate(0deg); 28 | transform: rotate(0deg); 29 | } 30 | 100% { 31 | -webkit-transform: rotate(359deg); 32 | transform: rotate(359deg); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/webapp/vendors/font-awesome-4.6.3/scss/_animated.scss: -------------------------------------------------------------------------------- 1 | // Spinning Icons 2 | // -------------------------- 3 | 4 | .#{$fa-css-prefix}-spin { 5 | -webkit-animation: fa-spin 2s infinite linear; 6 | animation: fa-spin 2s infinite linear; 7 | } 8 | 9 | .#{$fa-css-prefix}-pulse { 10 | -webkit-animation: fa-spin 1s infinite steps(8); 11 | animation: fa-spin 1s infinite steps(8); 12 | } 13 | 14 | @-webkit-keyframes fa-spin { 15 | 0% { 16 | -webkit-transform: rotate(0deg); 17 | transform: rotate(0deg); 18 | } 19 | 100% { 20 | -webkit-transform: rotate(359deg); 21 | transform: rotate(359deg); 22 | } 23 | } 24 | 25 | @keyframes fa-spin { 26 | 0% { 27 | -webkit-transform: rotate(0deg); 28 | transform: rotate(0deg); 29 | } 30 | 100% { 31 | -webkit-transform: rotate(359deg); 32 | transform: rotate(359deg); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/webapp/commons/components/Warning/Warning.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './styles/warning.css'; 3 | 4 | export default class Warning extends React.Component { 5 | constructor(props) { 6 | super(props); 7 | this.onClose = this.onClose.bind(this); 8 | } 9 | 10 | onClose() { 11 | this.props.onclose(); 12 | return false; 13 | } 14 | 15 | render() { 16 | var _class = ""; 17 | switch(this.props.category) { 18 | case "ERROR": _class = "error"; 19 | break; 20 | case "WARNING": _class = "warning"; 21 | break; 22 | case "REQUIRED": _class = "required"; 23 | break; 24 | default: _class = "success"; 25 | break; 26 | } 27 | return( 28 |
29 |
30 |

{this.props.message}

31 |
32 |
33 | 34 |
35 |
36 | ); 37 | } 38 | } -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Symphony Integrations - Symphony Software Foundation 2 | Copyright 2016-2017 Symphony LLC 3 | 4 | This product includes software developed at the Symphony Software Foundation (http://symphony.foundation). 5 | 6 | The following dependencies are provided under other licenses. See project links for details. 7 | 8 | ======================================================================== 9 | Eclipse Public License 1.0 10 | ======================================================================== 11 | 12 | junit - http://junit.org/junit4/ 13 | 14 | ======================================================================== 15 | CDDL 1.1 16 | ======================================================================== 17 | 18 | Jersey - https://jersey.java.net/ 19 | Jax-RS - https://jax-rs-spec.java.net/ 20 | HK2 - https://hk2.java.net/2.5.0-b36/ 21 | 22 | ======================================================================== 23 | MPL 1.1 24 | ======================================================================== 25 | 26 | Javassist - http://jboss-javassist.github.io/javassist/ 27 | -------------------------------------------------------------------------------- /src/main/webapp/commons/components/SubmitWebHook/SubmitWebHook.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import { hashHistory } from 'react-router' 4 | require('./styles/styles.css'); 5 | var ConfService = require('../../js/configurator.service.js'); 6 | 7 | var SubmitWebHook = React.createClass({ 8 | propTypes: { 9 | showMessage: React.PropTypes.func 10 | }, 11 | onSubmit: function() { 12 | var msg = []; 13 | if(ConfService.required.name) { 14 | msg.push(ConfService.messages.name_required); 15 | } 16 | if(ConfService.required.rooms) { 17 | msg.push(ConfService.messages.rooms_required); 18 | } 19 | if(msg.length > 0) { 20 | this.props.showMessage(msg); 21 | } 22 | else { 23 | hashHistory.push('/save-webhook'); 24 | } 25 | 26 | }, 27 | onCancel: function() { 28 | hashHistory.push('/list-view'); 29 | }, 30 | render: function() { 31 | return( 32 |
33 |
34 | 35 | 36 |
37 |
38 | ) 39 | } 40 | }); 41 | module.exports = SubmitWebHook; -------------------------------------------------------------------------------- /src/main/webapp/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "symphony-hubspot", 3 | "version": "0.9.0", 4 | "description": "Symphony App Integrations - Hubspot Webhook", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "license": "Apache-2.0", 10 | "author": "maoo", 11 | "devDependencies": { 12 | "alt": "^0.18.6", 13 | "async": "^2.0.1", 14 | "async-foreach": "^0.1.3", 15 | "babel-core": "^6.10.4", 16 | "babel-loader": "^6.2.4", 17 | "babel-preset-es2015": "^6.9.0", 18 | "babel-preset-react": "^6.11.1", 19 | "copy-webpack-plugin": "^3.0.1", 20 | "cors": "^2.7.1", 21 | "css-loader": "^0.23.1", 22 | "del": "^2.2.2", 23 | "file-loader": "^0.9.0", 24 | "gulp": "^3.9.1", 25 | "html-webpack-plugin": "^2.16.0", 26 | "http-server": "^0.9.0", 27 | "img-loader": "^1.2.2", 28 | "merge-stream": "^1.0.0", 29 | "morgan": "^1.7.0", 30 | "react": "^15.2.0", 31 | "react-dom": "^15.2.0", 32 | "style-loader": "^0.13.1", 33 | "url-loader": "^0.5.7", 34 | "webpack": "^1.13.1", 35 | "webpack-dev-server": "^1.14.1", 36 | "webpack-stream": "^3.2.0" 37 | }, 38 | "dependencies": { 39 | "react-router": "^2.5.2" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/webapp/commons/views/Success.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import { hashHistory } from 'react-router' 4 | import IntegrationIdentity from '../components/IntegrationIdentity/IntegrationIdentity' 5 | import SuccessPostingLocation from '../components/SuccessPostingLocation/SuccessPostingLocation' 6 | import WebHookURL from '../components/WebHookURL/WebHookURL' 7 | import SetupInstructions from '../components/SetupInstructions/SetupInstructions' 8 | var ConfService = require('../js/configurator.service.js'); 9 | require('../styles/styles.css'); 10 | var Success = React.createClass({ 11 | onClick: function() { 12 | ConfService.resetProtoInstance(); 13 | hashHistory.push('/list-view/created'); 14 | }, 15 | render: function() { 16 | return( 17 |
18 | 19 | 20 | 21 |
22 | 23 |
24 | 25 |
26 |
27 |
28 | ); 29 | } 30 | }) 31 | module.exports = Success; 32 | -------------------------------------------------------------------------------- /src/main/webapp/commons/views/CreateView.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import IntegrationIdentity from '../components/IntegrationIdentity/IntegrationIdentity' 4 | import CreatePostingLocation from '../components/CreatePostingLocation/CreatePostingLocation' 5 | import SubmitWebHook from '../components/SubmitWebHook/SubmitWebHook' 6 | import Warning from '../components/Warning/Warning'; 7 | var ConfService = require('../js/configurator.service.js'); 8 | 9 | var CreateView = React.createClass({ 10 | getInitialState: function() { 11 | return { 12 | showWarning: true, 13 | messages: [] 14 | } 15 | }, 16 | showMessage: function(_msg) { 17 | this.setState({ 18 | messages: _msg.slice() 19 | }) 20 | }, 21 | onClose: function(item) { 22 | var _msgs = this.state.messages.slice(); 23 | _msgs.map( (__item, i) => { 24 | if(item == __item) { 25 | _msgs.splice(i, 1); 26 | } 27 | } ) 28 | this.setState({ 29 | messages: _msgs.slice() 30 | }) 31 | }, 32 | render: function() { 33 | var that = this; 34 | return( 35 |
36 | {this.state.messages.map(function(item, i) { 37 | return 38 | })} 39 | 40 | 41 | 42 |
43 | ); 44 | } 45 | }); 46 | 47 | 48 | 49 | module.exports = CreateView; -------------------------------------------------------------------------------- /src/main/webapp/commons/js/utils.service.js: -------------------------------------------------------------------------------- 1 | import ConfService from './configurator.service'; 2 | 3 | const Utils = { 4 | 5 | sendWelcomeMessage(streams, instanceId) { 6 | const query = ConfService.baseURL +'/v1/whi/'+ ConfService.appId +'/'+ ConfService.configurationId +'/'+ instanceId +'/welcome'; 7 | const payload = { 8 | streams: streams 9 | } 10 | $.ajax({ 11 | url: query, 12 | type:'POST', 13 | data: JSON.stringify(payload), 14 | dataType: "json", 15 | contentType: "application/json", 16 | success: success, 17 | error: error 18 | }); 19 | const success = data => { 20 | console.log('success welcome:', data); 21 | } 22 | const error = err => { 23 | console.log('error welcome: ', err); 24 | } 25 | }, 26 | 27 | getUpdatedRooms(_service, _cb) { 28 | var promisedRooms = _service.getRooms(); 29 | 30 | // store all user chat rooms 31 | var userChatRooms = []; 32 | var that = this; 33 | promisedRooms.then(function(data) { 34 | for(var prop in data) { 35 | if(data[prop].userIsOwner) { 36 | userChatRooms.push(data[prop]); 37 | } 38 | } 39 | var regExp = /\//g; 40 | // normalize all rooms threadIds 41 | userChatRooms.map(function(room, idx) { 42 | room.threadId = room.threadId.replace(regExp,'_').replace("==",""); 43 | }, this); 44 | ConfService.userChatRooms = userChatRooms.slice(); 45 | _cb(); 46 | }, function(err) { 47 | console.log('Error retrieving user rooms. ', err); 48 | }); 49 | } 50 | } 51 | 52 | 53 | export default Utils; -------------------------------------------------------------------------------- /src/main/webapp/commons/components/SetupInstructions/SetupInstructions.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import Setup from '../../../configurator/conf_setup_instructions/Setup'; 4 | import ConfService from '../../js/configurator.service' 5 | 6 | class SetupInstructions extends React.Component { 7 | constructor(props) { 8 | super(props); 9 | this.state = { 10 | hidden: true 11 | } 12 | this.showHideInstructions = this.showHideInstructions.bind(this); 13 | } 14 | 15 | showHideInstructions() { 16 | if(this.state.hidden) { 17 | this.refs.setupInstructions.className += " setup-instructions-transition"; 18 | 19 | } else { 20 | this.refs.setupInstructions.className = "setup-instructions"; 21 | } 22 | this.setState({ 23 | hidden: !this.state.hidden 24 | }) 25 | return false; 26 | } 27 | 28 | render() { 29 | return( 30 |
31 | { ConfService.toogleSetup && 32 | ( 33 |
34 |
35 |

Setup Instructions

36 |

Here are the steps necessary to add the {ConfService.configurationName} integration.

37 |
38 |
39 | 40 | 41 | 42 |
43 |
44 | )} 45 | 46 |
47 | ); 48 | } 49 | } 50 | 51 | export default SetupInstructions; 52 | -------------------------------------------------------------------------------- /src/main/webapp/vendors/font-awesome-4.6.3/less/mixins.less: -------------------------------------------------------------------------------- 1 | // Mixins 2 | // -------------------------- 3 | 4 | .fa-icon() { 5 | display: inline-block; 6 | font: normal normal normal @fa-font-size-base/@fa-line-height-base FontAwesome; // shortening font declaration 7 | font-size: inherit; // can't have font-size inherit on line above, so need to override 8 | text-rendering: auto; // optimizelegibility throws things off #1094 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | 12 | } 13 | 14 | .fa-icon-rotate(@degrees, @rotation) { 15 | -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=@{rotation})"; 16 | -webkit-transform: rotate(@degrees); 17 | -ms-transform: rotate(@degrees); 18 | transform: rotate(@degrees); 19 | } 20 | 21 | .fa-icon-flip(@horiz, @vert, @rotation) { 22 | -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=@{rotation}, mirror=1)"; 23 | -webkit-transform: scale(@horiz, @vert); 24 | -ms-transform: scale(@horiz, @vert); 25 | transform: scale(@horiz, @vert); 26 | } 27 | 28 | 29 | // Only display content to screen readers. A la Bootstrap 4. 30 | // 31 | // See: http://a11yproject.com/posts/how-to-hide-content/ 32 | 33 | .sr-only() { 34 | position: absolute; 35 | width: 1px; 36 | height: 1px; 37 | padding: 0; 38 | margin: -1px; 39 | overflow: hidden; 40 | clip: rect(0,0,0,0); 41 | border: 0; 42 | } 43 | 44 | // Use in conjunction with .sr-only to only display content when it's focused. 45 | // 46 | // Useful for "Skip to main content" links; see http://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1 47 | // 48 | // Credit: HTML5 Boilerplate 49 | 50 | .sr-only-focusable() { 51 | &:active, 52 | &:focus { 53 | position: static; 54 | width: auto; 55 | height: auto; 56 | margin: 0; 57 | overflow: visible; 58 | clip: auto; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/webapp/vendors/font-awesome-4.6.3/scss/_mixins.scss: -------------------------------------------------------------------------------- 1 | // Mixins 2 | // -------------------------- 3 | 4 | @mixin fa-icon() { 5 | display: inline-block; 6 | font: normal normal normal #{$fa-font-size-base}/#{$fa-line-height-base} FontAwesome; // shortening font declaration 7 | font-size: inherit; // can't have font-size inherit on line above, so need to override 8 | text-rendering: auto; // optimizelegibility throws things off #1094 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | 12 | } 13 | 14 | @mixin fa-icon-rotate($degrees, $rotation) { 15 | -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation})"; 16 | -webkit-transform: rotate($degrees); 17 | -ms-transform: rotate($degrees); 18 | transform: rotate($degrees); 19 | } 20 | 21 | @mixin fa-icon-flip($horiz, $vert, $rotation) { 22 | -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation}, mirror=1)"; 23 | -webkit-transform: scale($horiz, $vert); 24 | -ms-transform: scale($horiz, $vert); 25 | transform: scale($horiz, $vert); 26 | } 27 | 28 | 29 | // Only display content to screen readers. A la Bootstrap 4. 30 | // 31 | // See: http://a11yproject.com/posts/how-to-hide-content/ 32 | 33 | @mixin sr-only { 34 | position: absolute; 35 | width: 1px; 36 | height: 1px; 37 | padding: 0; 38 | margin: -1px; 39 | overflow: hidden; 40 | clip: rect(0,0,0,0); 41 | border: 0; 42 | } 43 | 44 | // Use in conjunction with .sr-only to only display content when it's focused. 45 | // 46 | // Useful for "Skip to main content" links; see http://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1 47 | // 48 | // Credit: HTML5 Boilerplate 49 | 50 | @mixin sr-only-focusable { 51 | &:active, 52 | &:focus { 53 | position: static; 54 | width: auto; 55 | height: auto; 56 | margin: 0; 57 | overflow: visible; 58 | clip: auto; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/webapp/commons/views/RemoveView.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import { hashHistory } from 'react-router' 4 | import IntegrationIdentity from '../components/IntegrationIdentity/IntegrationIdentity' 5 | import SuccessPostingLocation from '../components/SuccessPostingLocation/SuccessPostingLocation' 6 | import WebHookURL from '../components/WebHookURL/WebHookURL' 7 | import SetupInstructions from '../components/SetupInstructions/SetupInstructions' 8 | var ConfService = require('../js/configurator.service.js'); 9 | 10 | var RemoveView = React.createClass({ 11 | propTypes: { 12 | params: React.PropTypes.object.isRequired 13 | }, 14 | getInitialState: function() { 15 | return{ 16 | name: this.props.params.name, 17 | instanceId: this.props.params.instanceId 18 | } 19 | }, 20 | componentWillMount: function() { 21 | var that = this; 22 | var _instance = ConfService.instanceList.filter(function(item){ 23 | return item.instanceId == that.props.params.instanceId; 24 | }) 25 | ConfService.protoInstance.postingLocationsRooms = _instance[0].postingLocationsRooms.slice(); 26 | ConfService.protoInstance.streamType = _instance[0].streamType; 27 | }, 28 | onRemove: function() { 29 | hashHistory.push('/save-webhook/'+ this.state.instanceId); 30 | }, 31 | onCancel: function() { 32 | hashHistory.push('/list-view'); 33 | }, 34 | render: function() { 35 | return( 36 |
37 | 38 | 39 | 40 |
41 | 42 | 43 |
44 |
45 | ); 46 | } 47 | }); 48 | module.exports = RemoveView; -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Run integrations locally 4 | 5 | INTEGRATION_JAR=target/bundle/integration.jar 6 | LOG_BASEDIR=target 7 | JAVA_CMD_OPTS=-agentlib:jdwp=transport=dt_socket,server=y,address=5000,suspend=n 8 | YAML_TEMPLATE=./target/bundle/application.yaml.template 9 | 10 | if [[ -d /opt/openshift ]]; then 11 | echo "run.sh - Running on Openshift" 12 | cd /opt/openshift 13 | mkdir ./certs 14 | cp -s /tmp/*.p12 ./certs 15 | INTEGRATION_JAR=integration.jar 16 | YAML_TEMPLATE=./application.yaml.template 17 | LOG_BASEDIR=. 18 | JAVA_CMD_OPTS="-Xmx350m" 19 | fi 20 | 21 | # Import environment variables 22 | if [ -f ./env.sh ]; then 23 | . ./env.sh 24 | fi 25 | 26 | # Build the project and the spring boot bundle 27 | if [[ "$RUN_SH_SKIP_BUILD" != "true" ]]; then 28 | mvn clean install -Prun,bundle 29 | fi 30 | 31 | # Inject environment variables in application.yaml 32 | # TODO - use environment vars syntax in application.yaml and skip this step 33 | echo "Generating application.yaml file" 34 | rm -rf application.yaml 35 | curl -s https://raw.githubusercontent.com/symphonyoss/contrib-toolbox/master/scripts/inject-vars.sh | bash -s -- $YAML_TEMPLATE application.yaml 36 | 37 | # Cleanup tomcat folder from previous runs 38 | rm -rf tomcat ; mkdir tomcat 39 | 40 | # Install and start ngrok 41 | echo "Installing ngrok" 42 | curl -O https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-linux-amd64.zip 43 | unzip -o ngrok-stable-linux-amd64.zip 44 | chmod +x ngrok 45 | echo "Running ngrok from folder $PWD" 46 | ./ngrok authtoken $NGROK_TOKEN 47 | ./ngrok http --subdomain hubspot.symphonyoss --log=stdout 8186 > ngrok.log & 48 | 49 | echo "Checking that ngrok is running..." 50 | sleep 3 51 | ps auxwww | grep ngrok 52 | echo "ngrok logs..." 53 | cat ngrok.log 54 | 55 | # Run the Spring Boot application 56 | echo "Running Spring Boot app from folder $PWD:" 57 | java -Dlog4j2.outputAllToConsole=true -Dlogs.basedir=$LOG_BASEDIR $JAVA_CMD_OPTS \ 58 | -jar $INTEGRATION_JAR \ 59 | --spring.profiles.active=$APP_ID \ 60 | --server.tomcat.basedir=$PWD/tomcat \ 61 | --server.address=0.0.0.0 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Dependencies](https://www.versioneye.com/user/projects/58cc3b2fdcaf9e0045d9700f/badge.svg?style=flat-square)](https://www.versioneye.com/user/projects/58cc3b2fdcaf9e0045d9700f) 2 | [![Build Status](https://travis-ci.org/maoo/symphony-hubspot.svg)](https://travis-ci.org/maoo/symphony-hubspot) 3 | 4 | # Hubspot Webhook Integration 5 | 6 | A simple integration to connect Hubspot workflows (webhooks) with Symphony chat. 7 | Right now, it is possible to easily connect a Hubspot form submission with a chat (either group or 1:1) notification. 8 | 9 | This project is forked from the [Symphony Universal Integration](https://github.com/symphonyoss/App-Integrations-Universal), where you can read more about the integration framework. 10 | 11 | ## Run locally 12 | 13 | 1. Define your certificate paths and passwords 14 | ``` 15 | cp local-run/env.sh.sample env.sh 16 | open env.sh 17 | ``` 18 | 19 | Make sure that 20 | - Paths and passwords are correct 21 | - You can reach all Symphony Pod endpoints 22 | - Service accounts exists and cert CNs match with account's usernames 23 | - `./env.sh`, `./application.yaml` and `./certs/` are ignored by Git and don't end up in any code repository 24 | 25 | 2. Run the integrations 26 | ``` 27 | ./run.sh 28 | ``` 29 | 30 | This command will create an `application.yaml` file in the project root folder, using `local-run/application.yaml.template` as template. 31 | 32 | ## Expose local endpoint to a public host 33 | 34 | In order to be able to create the app in the Foundation pod, you must provide a public `App Url`; you can use [ngrok](https://ngrok.com/) (or similar) to tunnel your local connection and expose it via a public DNS: 35 | ``` 36 | ngrok http 8080 37 | ``` 38 | 39 | Your local port 8080 is now accessible via `.ngrok.io` 40 | 41 | If you have a paid subscription, you can also use 42 | ``` 43 | ngrok http -subdomain=my.static.subdomain 8080 44 | ``` 45 | 46 | Note. The team is working on a integration-provisioning module that will automate this process; until further notice, please contact Symphony Support to get your Symphony integration deployed on your pod. 47 | -------------------------------------------------------------------------------- /src/main/webapp/commons/js/controller.js: -------------------------------------------------------------------------------- 1 | //create our own service 2 | var app_id = getParameterByName('id'); 3 | var listService = SYMPHONY.services.register(app_id+":controller"); 4 | var config = require('../../configurator/config.js'); 5 | 6 | SYMPHONY.remote.hello().then(function(data) { 7 | SYMPHONY.application.register(app_id, ["ui", "modules", "applications-nav", "account", "integrationConfigService", "stream-service"], [app_id+":controller"]).then(function(response) { 8 | 9 | //system services 10 | var navService = SYMPHONY.services.subscribe("applications-nav"); 11 | var userId = response.userReferenceId; 12 | var uiService = SYMPHONY.services.subscribe("ui"); 13 | var navService = SYMPHONY.services.subscribe("applications-nav"); 14 | var modulesService = SYMPHONY.services.subscribe("modules"); 15 | var ac = SYMPHONY.services.subscribe("account"); 16 | 17 | uiService.registerExtension ('app-settings', app_id, app_id+":controller", {label: 'Configure'}); 18 | 19 | listService.implement({ 20 | trigger: function(id) { 21 | //invoke the module service to show our own application in the grid 22 | var confId = getParameterByName('configurationId'); 23 | var botUserId = getParameterByName('botUserId'); 24 | var context = getParameterByName('context') ? "/"+getParameterByName('context') : ""; 25 | var host = window.location.protocol + "//" + window.location.hostname + ":" + window.location.port; 26 | modulesService.show(app_id, {title: config.app_title}, app_id+":controller", host + context + "/app.html?configurationId="+confId+"&botUserId="+botUserId+"&id="+app_id, {canFloat: true}); 27 | }, 28 | }); 29 | 30 | }.bind(this)) 31 | }.bind(this)); 32 | 33 | function getParameterByName(name, url) { 34 | if (!url) url = window.location.href; 35 | name = name.replace(/[\[\]]/g, "\\$&"); 36 | var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"), 37 | results = regex.exec(url); 38 | if (!results) return null; 39 | if (!results[2]) return ''; 40 | return decodeURIComponent(results[2].replace(/\+/g, " ")); 41 | } 42 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # 2 | # 3 | # Copyright 2016 The Symphony Software Foundation 4 | # 5 | # Licensed to The Symphony Software Foundation (SSF) under one 6 | # or more contributor license agreements. See the NOTICE file 7 | # distributed with this work for additional information 8 | # regarding copyright ownership. The ASF licenses this file 9 | # to you under the Apache License, Version 2.0 (the 10 | # "License"); you may not use this file except in compliance 11 | # with the License. You may obtain a copy of the License at 12 | # 13 | # http://www.apache.org/licenses/LICENSE-2.0 14 | # 15 | # Unless required by applicable law or agreed to in writing, 16 | # software distributed under the License is distributed on an 17 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18 | # KIND, either express or implied. See the License for the 19 | # specific language governing permissions and limitations 20 | # under the License. 21 | # 22 | 23 | sudo: required 24 | dist: trusty 25 | language: node_js 26 | node_js: 27 | - "6.10" 28 | 29 | after_success: "if [[ \"$TRAVIS_PULL_REQUEST\" = \"false\" ]]; then curl -s https://raw.githubusercontent.com/symphonyoss/contrib-toolbox/master/scripts/oc-deploy.sh | bash ; fi" 30 | 31 | addons: 32 | apt: 33 | packages: 34 | - openjdk-8-jdk 35 | - maven 36 | 37 | before_script: "npm install node-license-validator -g" 38 | 39 | script: 40 | - "pushd src/main/webapp ; node-license-validator --allow-licenses BSD-like BSD BSD-2-Clause BSD-3-Clause Apache-2.0 MIT ISC Unlicense MIT/X11 \"MIT / http://rem.mit-license.org\" \"Public Domain\" --allow-packages stream-cache@0.0.2 ripemd160@0.2.0 domutils@1.5.1 domhandler@2.1.0 domelementtype@1.3.0 ; popd" 41 | - "mvn clean package -Prun,bundle" 42 | 43 | env: 44 | global: 45 | # Used by oc-deploy.sh for CD 46 | - OC_BINARY_FOLDER=./target/bundle 47 | - OC_BUILD_CONFIG_NAME=hubspot 48 | 49 | # Coverity scan can be enabled for demo purposes 50 | # addons: 51 | # coverity_scan: 52 | # project: 53 | # name: "symphonyoss/symphony-java-sample-bots" 54 | # description: "Build submitted via Travis CI" 55 | # notification_email: maoo@symphony.foundation 56 | # build_command_prepend: "mvn clean" 57 | # build_command: "mvn package" 58 | # branch_pattern: master 59 | -------------------------------------------------------------------------------- /src/main/webapp/commons/components/SuccessPostingLocation/SuccessPostingLocation.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | require('./styles/styles.css'); 4 | var ConfService = require('../../js/configurator.service.js'); 5 | 6 | var CreatePostingLocation = React.createClass({ 7 | getInitialState: function() { 8 | return { 9 | filters: [] 10 | } 11 | }, 12 | componentWillMount: function() { 13 | var _filters = []; 14 | ConfService.protoInstance.postingLocationsRooms.map(function(item,i){ 15 | _filters.push(item); 16 | }) 17 | this.setState({ 18 | filters: _filters.slice() 19 | }); 20 | }, 21 | render: function() { 22 | return( 23 |
24 | {this.state.filters.length > 0 && (
Posting Location
)} 25 | {this.state.filters.length > 0 && ()} 26 |
27 | ); 28 | } 29 | }); 30 | 31 | var SearchRooms = React.createClass({ 32 | propTypes: { 33 | filters: React.PropTypes.arrayOf(React.PropTypes.object).isRequired, 34 | }, 35 | getInitialState: function() { 36 | var __filters = this.props.filters.slice(); 37 | return { 38 | filters: __filters //filters: [], //array of objects 39 | }; 40 | }, 41 | render: function() { 42 | return( 43 |
44 | {this.state.filters.map(function(item, idx) { 45 | return 46 | })} 47 |
48 | ); 49 | } 50 | }); 51 | 52 | /* FilterBox Component filter 53 | */ 54 | var FilterBox = React.createClass({ 55 | propTypes: { 56 | room: React.PropTypes.object.isRequired, 57 | }, 58 | render: function() { 59 | var members = this.props.room['memberCount'] > 1 ? this.props.room['memberCount'] +" Members" : this.props.room['memberCount'] +" Member";; 60 | return( 61 |
62 | {this.props.room['publicRoom'] == false ?
{this.props.room['name']}
:
{this.props.room['name']}
} 63 |
64 | {members +", created by "+ this.props.room['creatorPrettyName']} 65 |
66 | 67 |
68 | ); 69 | } 70 | }) 71 | module.exports = SearchRooms; 72 | module.exports = CreatePostingLocation; -------------------------------------------------------------------------------- /src/test/java/org/symphonyoss/integration/webhook/hubspot/HubspotWebHookIntegrationTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2016-2017 Symphony Integrations - Symphony LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.symphonyoss.integration.webhook.hubspot; 18 | 19 | import static org.junit.Assert.assertEquals; 20 | 21 | import com.fasterxml.jackson.databind.JsonNode; 22 | import org.junit.Test; 23 | import org.mockito.Mock; 24 | import org.symphonyoss.integration.json.JsonUtils; 25 | import org.symphonyoss.integration.model.config.IntegrationInstance; 26 | import org.symphonyoss.integration.webhook.WebHookPayload; 27 | 28 | import java.io.IOException; 29 | import java.util.Collections; 30 | 31 | public class HubspotWebHookIntegrationTest { 32 | 33 | private static final String FORM_SUBMISSION_WEB_HOOK_PAYLOAD_JSON = 34 | "webhook-payload.json"; 35 | 36 | private static final String FORM_SUBMISSION_MESSAGE = 37 | "" 38 | + "New FinDEVr Enrollment at Symphony Software Foundation Demos Landing Page: Demo Form" 39 | + "
" 40 | + "John Smith (demo@email.com) from Symphony" 41 | + "
"; 42 | 43 | @Mock 44 | private IntegrationInstance mockIntegrationInstance; 45 | 46 | private HubspotWebHookIntegration hubSpotWebHookIntegration = new HubspotWebHookIntegration(); 47 | 48 | @Test 49 | public void testFormSubmissionMessage() throws IOException { 50 | 51 | ClassLoader classLoader = getClass().getClassLoader(); 52 | JsonNode payload = JsonUtils.readTree(classLoader.getResourceAsStream(FORM_SUBMISSION_WEB_HOOK_PAYLOAD_JSON)); 53 | 54 | WebHookPayload webHookPayload = new WebHookPayload(Collections.emptyMap(), 55 | Collections.emptyMap(), 56 | JsonUtils.writeValueAsString(payload)); 57 | 58 | String message = hubSpotWebHookIntegration.parse(mockIntegrationInstance, webHookPayload); 59 | 60 | assertEquals(FORM_SUBMISSION_MESSAGE, message); 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /src/main/webapp/commons/components/WebHookURL/WebHookURL.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | 4 | var WebHookURL = React.createClass({ 5 | propTypes: { 6 | baseUrl: React.PropTypes.string.isRequired, 7 | instanceId: React.PropTypes.string.isRequired, 8 | appId: React.PropTypes.string, 9 | configurationId: React.PropTypes.string.isRequired 10 | }, 11 | getDefaultProps: function() { 12 | return { 13 | baseUrl: window.location.protocol + "//" + window.location.hostname + ":" + window.location.port 14 | }; 15 | }, 16 | getInitialState: function() { 17 | return { 18 | url: this.props.baseUrl, 19 | disabled: true 20 | }; 21 | }, 22 | copyURL: function(e) { 23 | var target = e.target; 24 | var copyTarget = target.dataset.copytarget; 25 | var textfield = (copyTarget ? document.querySelector(copyTarget) : null); 26 | // is element selectable? 27 | if (textfield && textfield.select) { 28 | this.setState({disabled: false}); 29 | textfield.select(); 30 | try { 31 | // copy text 32 | document.execCommand('copy'); 33 | textfield.blur(); 34 | e.target.innerHTML = "Copied!"; 35 | setTimeout(function(){ 36 | e.target.innerHTML = "Copy URL"; 37 | },2000); 38 | } catch (err) { 39 | console.log(err); 40 | } 41 | this.setState({disabled: true}); 42 | } else { 43 | console.log('element not found ' + copyTarget); 44 | } 45 | }, 46 | componentWillMount: function() { 47 | var _url = this.props.baseUrl +"/v1/whi/"+ this.props.appId +"/"+ this.props.configurationId + '/' + this.props.instanceId; 48 | this.setState({ 49 | url: _url 50 | }) 51 | }, 52 | componentDidMount: function() { 53 | var copyLink = document.getElementById('copy-link'); 54 | copyLink.addEventListener('click', this.copyURL, true); 55 | }, 56 | render: function() { 57 | return ( 58 |
59 |
Webhook URL
60 |
61 | 62 | Copy URL 63 |
64 |
65 | ) 66 | } 67 | }); 68 | module.exports = WebHookURL; 69 | -------------------------------------------------------------------------------- /src/main/webapp/commons/views/EditView.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import { hashHistory } from 'react-router'; 4 | import IntegrationIdentity from '../components/IntegrationIdentity/IntegrationIdentity'; 5 | import EditPostingLocation from '../components/EditPostingLocation/EditPostingLocation'; 6 | import SubmitWebHook from '../components/SubmitWebHook/SubmitWebHook'; 7 | import SetupInstructions from '../components/SetupInstructions/SetupInstructions'; 8 | import WebHookURL from '../components/WebHookURL/WebHookURL'; 9 | import Warning from '../components/Warning/Warning'; 10 | var ConfService = require('../js/configurator.service.js'); 11 | 12 | var EditView = React.createClass({ 13 | propTypes: { 14 | params: React.PropTypes.object.isRequired 15 | }, 16 | getInitialState: function() { 17 | var that = this; 18 | var _instance = ConfService.instanceList.filter(function(item){ 19 | return item.instanceId == that.props.params.instanceId; 20 | }) 21 | return{ 22 | instance: _instance[0], 23 | messages: [] 24 | } 25 | }, 26 | onUpdate: function() { 27 | var _msgs = [], _str = ""; 28 | if(ConfService.required.name) { 29 | _msgs.push(ConfService.messages.name_required); 30 | } 31 | if(ConfService.required.rooms) { 32 | _msgs.push(ConfService.messages.rooms_required); 33 | } 34 | if(_msgs.length == 0) { 35 | hashHistory.push('/save-webhook/'+ this.state.instance.instanceId); 36 | } else { 37 | this.setState({ 38 | messages: _msgs.slice() 39 | }) 40 | } 41 | }, 42 | onClose: function(item) { 43 | var _msgs = this.state.messages.slice(); 44 | _msgs.map( (__item, i) => { 45 | if(item == __item) { 46 | _msgs.splice(i, 1); 47 | } 48 | } ) 49 | this.setState({ 50 | messages: _msgs.slice() 51 | }) 52 | }, 53 | onCancel: function() { 54 | hashHistory.push('/list-view'); 55 | }, 56 | render: function() { 57 | var that = this; 58 | return( 59 |
60 | {this.state.messages.map(function(item, i) { 61 | return 62 | })} 63 | 64 | 65 | 66 | 67 |
68 | 69 | 70 |
71 |
72 | ); 73 | } 74 | }); 75 | module.exports = EditView; -------------------------------------------------------------------------------- /src/main/java/org/symphonyoss/integration/webhook/hubspot/HubspotWebHookIntegration.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2016-2017 Symphony Integrations - Symphony LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.symphonyoss.integration.webhook.hubspot; 18 | 19 | import com.fasterxml.jackson.databind.JsonNode; 20 | import org.springframework.stereotype.Component; 21 | import org.symphonyoss.integration.json.JsonUtils; 22 | import org.symphonyoss.integration.webhook.WebHookIntegration; 23 | import org.symphonyoss.integration.webhook.WebHookPayload; 24 | import org.symphonyoss.integration.webhook.exception.WebHookParseException; 25 | 26 | import java.io.IOException; 27 | 28 | @Component 29 | public class HubspotWebHookIntegration extends WebHookIntegration { 30 | 31 | private static final String WEBHOOK_EVENT = "HubSpot Webhook"; 32 | 33 | private static final String FORMATTED_MESSAGE = "New FinDEVr Enrollment at %s
%s %s (%s) from %s"; 34 | 35 | private static final String PROPERTIES = "properties"; 36 | 37 | private static final String VALUE = "value"; 38 | 39 | private static final String FORM_NAME = "recent_conversion_event_name"; 40 | 41 | private static final String FIRST_NAME = "firstname"; 42 | 43 | private static final String LAST_NAME = "lastname"; 44 | 45 | private static final String EMAIL = "email"; 46 | 47 | private static final String COMPANY = "company"; 48 | 49 | @Override 50 | public String parse(WebHookPayload input) { 51 | try { 52 | // Retrieves data from the webhook payload 53 | JsonNode payload = JsonUtils.readTree(input.getBody()); 54 | JsonNode properties = payload.path(PROPERTIES); 55 | String formName = properties.path(FORM_NAME).path(VALUE).asText(); 56 | String firstName = properties.path(FIRST_NAME).path(VALUE).asText(); 57 | String lastName = properties.path(LAST_NAME).path(VALUE).asText(); 58 | String email = properties.path(EMAIL).path(VALUE).asText(); 59 | String company = properties.path(COMPANY).path(VALUE).asText(); 60 | 61 | // Formats the message 62 | String formattedMessage = String.format(FORMATTED_MESSAGE, formName, firstName, lastName, email, company); 63 | 64 | // Returns a messageML document 65 | return super.buildMessageML(formattedMessage, WEBHOOK_EVENT); 66 | } catch (IOException e) { 67 | throw new WebHookParseException(getClass().getSimpleName(), "Failed to parse payload", e); 68 | } 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /src/main/webapp/commons/components/IntegrationIdentity/IntegrationIdentity.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | var ConfService = require('../../js/configurator.service.js'); 4 | 5 | var IntegrationIdentity = React.createClass({ 6 | propTypes: { 7 | renderLogo: React.PropTypes.bool, 8 | instanceName: React.PropTypes.string, 9 | disabled: React.PropTypes.bool 10 | }, 11 | getDefaultProps: function() { 12 | return { 13 | renderLogo: false, 14 | instanceName: "", 15 | disabled: false 16 | }; 17 | }, 18 | getInitialState: function() { 19 | return { 20 | name: "", 21 | filled: false, 22 | required: true 23 | }; 24 | }, 25 | componentWillMount: function() { 26 | if(this.props.instanceName !== "") { 27 | ConfService.required.name = false; 28 | this.setState({ 29 | name: this.props.instanceName, 30 | filled: true 31 | }) 32 | } else { 33 | ConfService.required.name = true; 34 | } 35 | this.setState({ 36 | required: ConfService.required.name 37 | }) 38 | }, 39 | componentDidMount: function() { 40 | this.refs.instanceName.focus(); 41 | }, 42 | handleNameChange: function(e) { 43 | if(e.target.value !== "") { 44 | ConfService.required.name = false; 45 | this.setState({ 46 | filled: true, 47 | required: false 48 | }) 49 | } else { 50 | ConfService.required.name = true; 51 | this.setState({ 52 | filled: false, 53 | required: true 54 | }) 55 | } 56 | this.setState({ 57 | name: e.target.value 58 | }); 59 | ConfService.protoInstance.name = e.target.value; 60 | }, 61 | render: function() { 62 | const displayName = ConfService.configurationName.replace(new RegExp('(webHook)', 'gi'), ''); 63 | return ( 64 |
65 |
66 |
67 | {ConfService.configurationName} 68 |
69 |
70 | {ConfService.configurationName} 71 |
72 |
73 |

{displayName} Webhook Integration

74 |
75 |
76 | {/*!this.props.disabled && (this.state.filled ? : )*/} 77 | {!this.props.disabled && !this.state.filled && ()} 78 |
79 |
80 | ) 81 | } 82 | }); 83 | module.exports = IntegrationIdentity; -------------------------------------------------------------------------------- /src/main/webapp/gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var webpack = require('webpack-stream'); 3 | 4 | var path = require("path"), 5 | HtmlWebpackPlugin = require('html-webpack-plugin'), 6 | CopyWebpackPlugin = require('copy-webpack-plugin'); 7 | 8 | /* AUTOMATED DEPLOY */ 9 | function callWebPackProd(output) { 10 | var dist = output + '/static'; 11 | console.log('webpack dist: ', dist); 12 | gulp.src(["./commons/js/controller.js", "./commons/js/app.js"]) 13 | .pipe( 14 | webpack( 15 | { 16 | entry: { 17 | controller: path.resolve(__dirname, "./commons/js/controller.js"), 18 | app: path.resolve(__dirname, "./commons/js/app.js") 19 | }, 20 | output: { 21 | path: dist, 22 | filename: "[name].bundle.js", 23 | publicPath: '' 24 | }, 25 | module: { 26 | loaders: [ 27 | { test: /\.css$/, loader: "style!css" }, 28 | { test: /\.less$/, loader: "style!css!less" }, 29 | { 30 | test: /\.jsx?$/, 31 | exclude: /node_modules/, 32 | loader: 'babel', 33 | query: { 34 | presets: ['es2015', 'react'] 35 | } 36 | }, 37 | {test: /\.(jpe?g|png|gif|svg)$/i, loader: 'url' }, 38 | {test: /\.(woff|woff2)(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?mimetype=application/font-woff'}, 39 | {test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?mimetype=application/octet-stream'}, 40 | {test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, loader: 'file'}, 41 | {test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?mimetype=image/svg+xml'} 42 | ] 43 | }, 44 | resolve: { 45 | extensions: [ '', '.js', '.jsx' ] 46 | }, 47 | plugins: [ 48 | new HtmlWebpackPlugin({ 49 | filename: "controller.html", 50 | template: "./commons/html/controller.html", 51 | inject: false 52 | }), 53 | new HtmlWebpackPlugin({ 54 | filename: "app.html", 55 | template: "./commons/html/app.html", 56 | inject: false 57 | }), 58 | new CopyWebpackPlugin([ 59 | { from: './commons/img', to: 'img' } 60 | ]), 61 | new CopyWebpackPlugin([ 62 | { from: './configurator/img', to: 'img' } 63 | ]), 64 | new CopyWebpackPlugin([ 65 | { from: './bundle.json', to: 'bundle' } 66 | ]) 67 | ] 68 | }, 69 | null 70 | ) 71 | ).pipe(gulp.dest(dist)); 72 | console.log('callWebPack ',dist); 73 | } 74 | 75 | gulp.task('build', function callWatchTasks() { 76 | var output, i = process.argv.indexOf("--output-path"); 77 | 78 | if (i == -1) { 79 | console.log("You should define the output path. Usage: gulp build --output-path PATH"); 80 | process.exit(1); 81 | } 82 | 83 | output = process.argv[i+1]; 84 | 85 | callWebPackProd(output); 86 | }); -------------------------------------------------------------------------------- /src/main/webapp/commons/js/configurator.service.js: -------------------------------------------------------------------------------- 1 | var config = require('../../configurator/config'); 2 | 3 | var successCreatedMessage; 4 | 5 | if (config.app_name === 'Universal Webhook') { 6 | successCreatedMessage = 'You have successfully configured a new '+ config.app_name +' integration.'; 7 | } else { 8 | successCreatedMessage = 'You have successfully configured a new '+ config.app_name +' integration. Register your configured integration on '+ config.app_name +' to complete setup.'; 9 | } 10 | 11 | module.exports = { 12 | 13 | instanceList: [], 14 | 15 | userChatRooms: [], 16 | 17 | userId: "", 18 | 19 | baseURL: "", 20 | 21 | appId: "", 22 | 23 | appTitle: "", 24 | 25 | configurationName: "", 26 | 27 | configurationId: "", 28 | 29 | toogleSetup: true, 30 | 31 | newInstanceCreated: false, 32 | 33 | protoInstance: { 34 | instanceId: "", 35 | name: "", 36 | description: "", 37 | creatorId: "", 38 | newPostingLocationsRooms: [], // Rooms used to check welcome message 39 | postingLocationsRooms: [], 40 | notPostingLocationsRooms: [], 41 | lastPostedDate: null 42 | }, 43 | 44 | dataResponse: 1, 45 | 46 | timeout: 10000, // timeout errors in miliseconds 47 | 48 | botUserId: "", 49 | 50 | required: { 51 | name: true, 52 | rooms: false // streamType IM is default 53 | }, 54 | 55 | messages: { 56 | created: successCreatedMessage, 57 | updated: 'You have successfully updated your '+ config.app_name +' integration.', 58 | deactivated: 'Instance removed from Symphony. You can now remove the webhook from '+ config.app_name, 59 | error: "An error has ocurred and the operation could not be completed. Please try again later.", 60 | not_found: "No webhook instances were found.", 61 | loading: "Searching for instances...", 62 | working: "Working...", 63 | name_required: "Description is required!", 64 | rooms_required: "Posting Location is required!" 65 | }, 66 | 67 | labelPostinLocations: "One-on-one with ", 68 | 69 | setPostingLocationsRooms: function(_id, _arr) { 70 | this.instanceList.map(function(item, idx) { 71 | if(item.instanceId == _id) { 72 | item.postingLocationsRooms = _arr.slice(); 73 | } 74 | }) 75 | }, 76 | 77 | setNotPostingLocationsRooms: function(_id, _arr) { 78 | this.instanceList.map(function(item, idx) { 79 | if(item.instanceId == _id) { 80 | item.notPostingLocationsRooms = _arr.slice(); 81 | } 82 | }) 83 | }, 84 | 85 | setNewInstance: function(_elem) { 86 | this.instanceList.push(_elem); 87 | this.dataResponse = 1; 88 | //this.resetProtoInstance(); 89 | }, 90 | 91 | updateInstance: function(_id, _item) { 92 | var that = this; 93 | this.instanceList.map(function(item, idx){ 94 | if(_id == item.instanceId) { 95 | that.instanceList[idx] = _item; 96 | return; 97 | } 98 | }); 99 | }, 100 | 101 | removeInstanceById: function(_id) { 102 | var that = this; 103 | this.instanceList.map(function(item, idx){ 104 | if(_id == item.instanceId) { 105 | that.instanceList.splice(idx,1); 106 | return; 107 | } 108 | }); 109 | if(this.instanceList.length == 0) { 110 | this.dataResponse = 0; 111 | } 112 | }, 113 | 114 | resetProtoInstance: function() { 115 | this.protoInstance.instanceId = ""; 116 | this.protoInstance.name = ""; 117 | this.protoInstance.description = ""; 118 | this.protoInstance.creatorId = ""; 119 | this.protoInstance.postingLocationsRooms = []; 120 | this.protoInstance.notPostingLocationsRooms = []; 121 | this.protoInstance.newPostingLocationsRooms = []; 122 | this.protoInstance.lastPostedDate = null; 123 | this.protoInstance.streamType = "IM"; 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/main/webapp/commons/components/CreatePostingLocation/CreatePostingLocation.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import SearchRooms from '../SearchRooms/SearchRooms' 4 | import ConfService from '../../js/configurator.service'; 5 | import Utils from '../../js/utils.service'; 6 | 7 | var extendedUserService; 8 | 9 | var CreatePostingLocation = React.createClass({ 10 | getInitialState: function() { 11 | var oneOneLabel = 'New one-on-one chat'; 12 | for(var instance of ConfService.instanceList){ 13 | if(instance.streamType === "IM"){ 14 | oneOneLabel = `Existing one-on-one chat with ${ConfService.configurationName}`; 15 | break; 16 | } 17 | } 18 | return { 19 | checked: true, 20 | showSearch: false, 21 | rooms: [], 22 | filteredRooms: [], 23 | filters: [], 24 | oneOneLabel: oneOneLabel, 25 | } 26 | }, 27 | componentWillMount: function() { 28 | this.setState({ 29 | filteredRooms: [] 30 | }); 31 | extendedUserService = SYMPHONY.services.subscribe("extended-user-service"); 32 | }, 33 | componentDidMount: function() { 34 | var that = this; 35 | this.setState({ 36 | rooms: ConfService.userChatRooms 37 | }) 38 | this.refs.oneOne.checked = true; 39 | ConfService.protoInstance.streamType = "IM"; 40 | 41 | }, 42 | onChange: function(e) { 43 | this.setState({ 44 | checked: e.target.checked 45 | }) 46 | var _rooms = ConfService.userChatRooms.slice(); 47 | if(e.target.id === 'chat-room') { 48 | ConfService.protoInstance.streamType = "CHATROOM"; 49 | this.setState({ 50 | showSearch: true, 51 | rooms: _rooms//rooms: SYMPHONY.getRooms 52 | }); 53 | } else if(e.target.id === 'one-one') { 54 | ConfService.protoInstance.streamType = "IM"; 55 | ConfService.required.rooms = false; 56 | this.setState({ 57 | showSearch: false, 58 | filteredRooms: [], 59 | filters: [], 60 | }); 61 | } 62 | }, 63 | onChangeSearch: function(e) { 64 | var that = this; 65 | Utils.getUpdatedRooms(extendedUserService, function() { 66 | that.setState({ 67 | rooms: ConfService.userChatRooms 68 | }); 69 | }); 70 | var suggestionList = this.state.rooms.slice(); 71 | if(e.target.value === "") { 72 | this.setState({ 73 | filteredRooms: [] 74 | }); 75 | return; 76 | } 77 | var _filters = this.state.filters.slice(); 78 | _filters.map(function(item, i) { 79 | suggestionList.map(function(_item, j) { 80 | if(item['threadId'] === _item['threadId']) 81 | suggestionList.splice(j,1); 82 | }) 83 | }) 84 | suggestionList = suggestionList.filter(function(item) { 85 | return item['name'].toLowerCase().search(e.target.value.toLowerCase()) !== -1; 86 | }); 87 | this.setState({ 88 | filteredRooms: suggestionList 89 | }); 90 | }, 91 | addFilter: function(elem, event) { 92 | var _filters = this.state.filters.concat([elem]); 93 | this.setState({ 94 | filters: _filters, 95 | filteredRooms: [] 96 | }) 97 | ConfService.protoInstance.newPostingLocationsRooms.push(elem.threadId); 98 | var _postingRooms = ConfService.protoInstance.postingLocationsRooms.length > 0 ? ConfService.protoInstance.postingLocationsRooms.slice() : [] ; 99 | var _rooms = ConfService.protoInstance.notPostingLocationsRooms.length > 0 ? ConfService.protoInstance.notPostingLocationsRooms.slice() : ConfService.userChatRooms.slice(); 100 | _postingRooms.push(elem); 101 | _rooms.map(function(item, i) { 102 | if(item['threadId'] == elem['threadId']) { 103 | _rooms.splice(i,1); 104 | return; 105 | } 106 | }) 107 | ConfService.protoInstance.postingLocationsRooms = _postingRooms.slice(); 108 | ConfService.protoInstance.notPostingLocationsRooms = _rooms.slice(); 109 | return false; 110 | }, 111 | removeFilter: function(elem, target) { 112 | var sugestionsList = this.state.rooms.slice(); 113 | var _filteredRooms = this.state.filteredRooms.slice(); 114 | var _filters = this.state.filters.slice(); 115 | var that = this; 116 | var _postingRooms = ConfService.protoInstance.postingLocationsRooms.length > 0 ? ConfService.protoInstance.postingLocationsRooms.slice() : [] ; 117 | var _rooms = ConfService.protoInstance.notPostingLocationsRooms; 118 | var _newPostingRooms = ConfService.protoInstance.newPostingLocationsRooms.slice(); 119 | _postingRooms.map(function(item, i) { 120 | if(item['threadId'] == elem['threadId']) { 121 | _rooms.push(item); 122 | _postingRooms.splice(i,1); 123 | ConfService.protoInstance.postingLocationsRooms = _postingRooms.slice(); 124 | ConfService.protoInstance.notPostingLocationsRooms = _rooms.slice(); 125 | return; 126 | } 127 | }) 128 | _filters.map(function(item, i) { 129 | if(item['threadId'] == elem['threadId']) { 130 | _filters.splice(i,1); 131 | _filteredRooms.push(elem); 132 | that.setState({ 133 | filters: _filters 134 | }) 135 | return; 136 | } 137 | }) 138 | _filteredRooms = _filteredRooms.filter(function(item) { 139 | return item['name'].toLowerCase().search(target.value.toLowerCase()) !== -1; 140 | }); 141 | _newPostingRooms.some(function(item) { 142 | if (item == elem['threadId']) { 143 | _newPostingRooms.splice(i,1); 144 | ConfService.protoInstance.newPostingLocationsRooms = _newPostingRooms.slice(); 145 | } 146 | }) 147 | if(target.value === "") { 148 | this.setState({ 149 | filters: _filters, 150 | filteredRooms: [] 151 | }); 152 | return; 153 | } 154 | this.setState({ 155 | filters: _filters, 156 | filteredRooms: _filteredRooms 157 | }) 158 | }, 159 | clearInput: function() { 160 | this.setState({ 161 | filteredRooms: [] 162 | }) 163 | }, 164 | render: function() { 165 | return( 166 |
167 |
Posting Location
168 |
169 |
170 | 171 | 172 |
173 |
174 | 175 |
You can only add this integration to a room of which you are an owner. You can choose one or more rooms.
176 |
177 |
178 | {this.state.showSearch && ()} 179 |
180 | ); 181 | } 182 | }); 183 | module.exports = CreatePostingLocation; 184 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | integration-parent 7 | org.symphonyoss.symphony.integrations 8 | 0.11.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | symphony-hubspot 13 | 14 | 15 | hubspot 16 | hubspot 17 | 18 | 19 | Hubspot WebHook Integration 20 | https://github.com/maoo/symphony-hubspot 21 | 22 | 23 | scm:git:git@github.com:maoo/symphony-hubspot.git 24 | scm:git:git@github.com:maoo/symphony-hubspot.git 25 | https://github.com/maoo/symphony-hubspot 26 | 27 | 28 | 29 | 30 | Apache License, Version 2.0 31 | https://www.apache.org/licenses/LICENSE-2.0.txt 32 | 33 | 34 | 35 | 36 | 37 | oss-sonatype 38 | oss-sonatype 39 | https://oss.sonatype.org/content/repositories/snapshots/ 40 | 41 | true 42 | 43 | 44 | 45 | 46 | 47 | 48 | org.symphonyoss.symphony.integrations 49 | integration-webhook 50 | ${project.version} 51 | 52 | 53 | 54 | 55 | 56 | 57 | bundle 58 | 59 | 60 | 68 | 69 | org.springframework.boot 70 | spring-boot-maven-plugin 71 | ${spring.boot.version} 72 | 73 | ${project.build.directory}/bundle 74 | 75 | 76 | 77 | maven-resources-plugin 78 | 3.0.2 79 | 80 | 81 | copy-resources 82 | validate 83 | 84 | copy-resources 85 | 86 | 87 | ${project.build.directory}/bundle 88 | 89 | 95 | 96 | local-run 97 | 98 | application.yaml.template 99 | 100 | 101 | 102 | . 103 | 104 | run.sh 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | cmarcondes 120 | Caue Marcondes 121 | caue.marcondes@symphony.com 122 | Daitan 123 | 124 | Senior Software Engineer 125 | 126 | America/Sao_Paulo 127 | 128 | 129 | ecarrenho 130 | Evandro Carrenho 131 | evandro.carrenho@symphony.com 132 | Daitan 133 | 134 | Software Architect 135 | 136 | America/Sao_Paulo 137 | 138 | 139 | mquilzini 140 | Milton Gonçalves Quilzini 141 | mquilzini@symphony.com 142 | Daitan 143 | 144 | Senior Software Engineer 145 | 146 | America/Sao_Paulo 147 | 148 | 149 | pdarde 150 | Pablo Darde 151 | pdarde@symphony.com 152 | Daitan 153 | 154 | Senior Software Engineer 155 | 156 | America/Sao_Paulo 157 | 158 | 159 | rsanchez 160 | Robson Vinicius Vieira Sanchez 161 | rsanchez@symphony.com 162 | Daitan 163 | 164 | Senior Software Engineer 165 | 166 | America/Sao_Paulo 167 | 168 | 169 | 170 | 171 | 172 | Adrian Zarifis 173 | adrian.zarifis@symphony.com 174 | Symphony 175 | 176 | QA Engineer 177 | 178 | 179 | 180 | Daniel Nathanson 181 | daniel.nathanson@symphony.com 182 | Symphony 183 | 184 | Senior Director Platform Engineering 185 | 186 | 187 | 188 | Eduardo Camargo 189 | eduardo.camargo@symphony.com 190 | Daitan 191 | 192 | QA Engineer 193 | 194 | 195 | 196 | Paul Teyssier 197 | p@symphony.com 198 | Symphony 199 | 200 | Senior Director Platform 201 | 202 | 203 | 204 | Vincent Gurle 205 | vincent@symphony.com 206 | Symphony 207 | 208 | Product Manager Platform 209 | 210 | 211 | 212 | 213 | 214 | -------------------------------------------------------------------------------- /src/main/webapp/commons/components/EditPostingLocation/EditPostingLocation.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import SearchRooms from '../SearchRooms/SearchRooms' 4 | import ConfService from '../../js/configurator.service'; 5 | import Utils from '../../js/utils.service'; 6 | 7 | var extendedUserService; 8 | 9 | var EditPostingLocation = React.createClass({ 10 | propTypes: { 11 | instanceId: React.PropTypes.string.isRequired 12 | }, 13 | getInitialState: function() { 14 | var oneOneLabel = 'New one-on-one chat'; 15 | for(var instance of ConfService.instanceList){ 16 | if(instance.streamType === "IM"){ 17 | oneOneLabel = `Existing one-on-one chat with ${ConfService.configurationName}`; 18 | break; 19 | } 20 | } 21 | return { 22 | checked: false, 23 | showSearch: false, 24 | rooms: [], 25 | filteredRooms: [], 26 | filters: [], 27 | oneOneLabel: oneOneLabel, 28 | } 29 | }, 30 | componentWillMount: function() { 31 | extendedUserService = SYMPHONY.services.subscribe("extended-user-service"); 32 | }, 33 | componentDidMount: function() { 34 | var that = this; 35 | var _rooms = ConfService.instanceList.filter(function(item){ 36 | return item.instanceId == that.props.instanceId 37 | }); 38 | ConfService.protoInstance.instanceId = _rooms[0].instanceId; 39 | ConfService.protoInstance.name = _rooms[0].name; 40 | ConfService.protoInstance.description = _rooms[0].description; 41 | ConfService.protoInstance.creatorId = ConfService.userId; 42 | ConfService.protoInstance.postingLocationsRooms = _rooms[0].postingLocationsRooms.slice(); 43 | ConfService.protoInstance.created = _rooms[0].created; 44 | ConfService.protoInstance.lastPostedDate = _rooms[0].lastPostedTimestamp; 45 | ConfService.protoInstance.notPostingLocationsRooms = _rooms[0].notPostingLocationsRooms.slice(); 46 | ConfService.protoInstance.streamType = _rooms[0].streamType; 47 | if(ConfService.protoInstance.streamType == "IM") { 48 | this.refs.oneOne.checked = true; 49 | ConfService.protoInstance.streamType = "IM"; 50 | } else if(_rooms[0].postingLocationsRooms.length > 0) { 51 | this.refs.chatRoom.checked = true; 52 | this.setState({ 53 | showSearch: true, 54 | rooms: _rooms[0].notPostingLocationsRooms.slice(), //rooms: SYMPHONY.getRooms 55 | filters: _rooms[0].postingLocationsRooms.slice(), 56 | }); 57 | } else { 58 | this.setState({ 59 | rooms: ConfService.userChatRooms.slice(), 60 | showSearch: true 61 | }) 62 | this.refs.chatRoom.checked = true; 63 | ConfService.required.rooms = true; 64 | } 65 | }, 66 | onChange: function(e) { 67 | this.setState({ 68 | checked: e.target.checked 69 | }) 70 | if(e.target.id === 'chat-room') { 71 | ConfService.protoInstance.streamType = "CHATROOM"; 72 | this.setState({ 73 | showSearch: true, 74 | rooms: ConfService.userChatRooms.slice()//rooms: SYMPHONY.getRooms 75 | }); 76 | } else if(e.target.id == 'one-one') { 77 | ConfService.protoInstance.streamType = "IM"; 78 | ConfService.required.rooms = false; 79 | this.setState({ 80 | showSearch: false, 81 | filteredRooms: [], 82 | filters: [], 83 | }); 84 | ConfService.protoInstance.postingLocationsRooms = []; 85 | ConfService.protoInstance.notPostingLocationsRooms = ConfService.userChatRooms.slice(); 86 | } 87 | }, 88 | onChangeSearch: function(e) { 89 | var that = this; 90 | Utils.getUpdatedRooms(extendedUserService, function() { 91 | that.setState({ 92 | rooms: ConfService.userChatRooms 93 | }); 94 | }); 95 | var suggestionList = this.state.rooms.slice(); 96 | this.setState({ 97 | filteredRooms: suggestionList 98 | }) 99 | if(e.target.value === "") { 100 | this.setState({ 101 | filteredRooms: [] 102 | }); 103 | return; 104 | } 105 | var _filters = this.state.filters.slice(); 106 | _filters.map(function(item, i) { 107 | suggestionList.map(function(_item, j) { 108 | if(item['threadId'] === _item['threadId']) 109 | suggestionList.splice(j,1); 110 | }) 111 | }) 112 | suggestionList = suggestionList.filter(function(item) { 113 | return item['name'].toLowerCase().search(e.target.value.toLowerCase()) !== -1; 114 | }); 115 | this.setState({ 116 | filteredRooms: suggestionList 117 | }); 118 | }, 119 | addFilter: function(elem, event) { 120 | var that = this; 121 | var _filters = this.state.filters.concat([elem]); 122 | 123 | this.setState({ 124 | filters: _filters, 125 | filteredRooms: [] 126 | }) 127 | ConfService.protoInstance.newPostingLocationsRooms.push(elem.threadId); 128 | var _postingRooms = ConfService.protoInstance.postingLocationsRooms.length > 0 ? ConfService.protoInstance.postingLocationsRooms.slice() : [] ; 129 | var _notPostingRooms = ConfService.protoInstance.notPostingLocationsRooms.length > 0 ? ConfService.protoInstance.notPostingLocationsRooms.slice() : ConfService.userChatRooms.slice(); 130 | _postingRooms.push(elem); 131 | _notPostingRooms.map(function(item, i) { 132 | if(item['threadId'] == elem['threadId']) { 133 | _notPostingRooms.splice(i,1); 134 | that.setState({ 135 | rooms: _notPostingRooms.slice() 136 | }) 137 | return; 138 | } 139 | }); 140 | 141 | ConfService.protoInstance.postingLocationsRooms = _postingRooms.slice(); 142 | ConfService.protoInstance.notPostingLocationsRooms = _notPostingRooms.slice(); 143 | return false; 144 | }, 145 | removeFilter: function(elem, target) { 146 | var that = this; 147 | var suggestionsList = ConfService.protoInstance.notPostingLocationsRooms.slice(); 148 | var _filteredRooms = this.state.filteredRooms.slice(); 149 | var _filters = this.state.filters.slice(); 150 | var _postingRooms = ConfService.protoInstance.postingLocationsRooms.length > 0 ? ConfService.protoInstance.postingLocationsRooms.slice() : [] ; 151 | var _notPostingRooms = ConfService.protoInstance.notPostingLocationsRooms.slice(); 152 | var _newPostingRooms = ConfService.protoInstance.newPostingLocationsRooms.slice(); 153 | _postingRooms.map(function(item, i) { 154 | if(item['threadId'] == elem['threadId']) { 155 | _notPostingRooms.push(item); 156 | _postingRooms.splice(i,1); 157 | ConfService.protoInstance.postingLocationsRooms = _postingRooms.slice(); 158 | ConfService.protoInstance.notPostingLocationsRooms = _notPostingRooms.slice(); 159 | that.setState({ 160 | rooms: _notPostingRooms.slice() 161 | }) 162 | return; 163 | } 164 | }) 165 | _filters.map(function(item, i) { 166 | if(item['threadId'] == elem['threadId']) { 167 | _filters.splice(i,1); 168 | _filteredRooms.push(elem); 169 | that.setState({ 170 | filters: _filters, 171 | filteredRooms: _filteredRooms 172 | }) 173 | return; 174 | } 175 | }) 176 | if(target.value === "") { 177 | this.setState({ 178 | filters: _filters, 179 | filteredRooms: [] 180 | }); 181 | return; 182 | } 183 | _filteredRooms = _filteredRooms.filter(function(item) { 184 | return item['name'].toLowerCase().search(target.value.toLowerCase()) !== -1; 185 | }); 186 | _newPostingRooms.some(function(item) { 187 | if (item == elem['threadId']) { 188 | _newPostingRooms.splice(i,1); 189 | ConfService.protoInstance.newPostingLocationsRooms = _newPostingRooms.slice(); 190 | } 191 | }); 192 | this.setState({ 193 | filteredRooms: _filteredRooms 194 | }) 195 | }, 196 | clearInput: function() { 197 | this.setState({ 198 | filteredRooms: [] 199 | }) 200 | }, 201 | render: function() { 202 | return( 203 |
204 |
Posting Location
205 |
206 |
207 | 208 | 209 |
210 |
211 | 212 |
You can only add this integration to a room of which you are an owner. You can choose one or more rooms.
213 |
214 |
215 | {this.state.showSearch && ()} 216 |
217 | ); 218 | } 219 | }); 220 | module.exports = EditPostingLocation; 221 | -------------------------------------------------------------------------------- /src/main/webapp/commons/views/ListView.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import { hashHistory } from 'react-router'; 4 | import SetupInstructions from '../components/SetupInstructions/SetupInstructions'; 5 | import Warning from '../components/Warning/Warning'; 6 | import config from '../../configurator/config'; 7 | var ConfService = require('../js/configurator.service.js'); 8 | 9 | 10 | var ListView = React.createClass({ 11 | propTypes: { 12 | params: React.PropTypes.object 13 | }, 14 | onConfigureNew: function() { 15 | hashHistory.push('/create-view'); 16 | }, 17 | getInitialState: function() { 18 | return { 19 | showWarning: true, 20 | loading: false, 21 | status: false, 22 | message: "", 23 | error: false, 24 | showIntances: false 25 | } 26 | }, 27 | componentWillMount: function() { 28 | var _msg; 29 | 30 | if(this.props.params) { 31 | if(this.props.params.status !== undefined){ 32 | switch(this.props.params.status) { 33 | case "created": _msg = ConfService.messages.created; 34 | break; 35 | case "updated": _msg = ConfService.messages.updated; 36 | break; 37 | case "deactivated": 38 | _msg = ConfService.messages.deactivated; 39 | this.setState({ 40 | showIntances: true 41 | }) 42 | break; 43 | case "error": 44 | _msg = ConfService.messages.error; 45 | this.setState({ 46 | error: true 47 | }) 48 | break; 49 | default: 50 | this.setState({ 51 | error: false 52 | }) 53 | break; 54 | } 55 | this.setState({ 56 | message: _msg, 57 | status: true 58 | }) 59 | } 60 | } 61 | 62 | if(ConfService.dataResponse == 1) { 63 | this.setState({ 64 | showWarning: false, 65 | loading: true, 66 | }) 67 | } else { 68 | this.setState({ 69 | showWarning: true, 70 | loading: false, 71 | message: ConfService.messages.not_found 72 | }) 73 | } 74 | }, 75 | componentDidMount: function() { 76 | if(this.refs.instanceTable.getElementsByTagName("td").length > 0) { 77 | this.setState({ 78 | showWarning: false, 79 | loading: false, 80 | showIntances: true 81 | }) 82 | } 83 | if(this.props.params) { 84 | if(this.props.params.status == "error") { 85 | this.setState({ 86 | loading: false 87 | }) 88 | } 89 | } 90 | 91 | }, 92 | onCloseStatus: function() { 93 | this.setState({ 94 | status: false, 95 | message: "" 96 | }) 97 | }, 98 | onCopyWebhookUrl: function() { 99 | var whurl = ConfService.baseURL +"/v1/whi/"+ ConfService.appId +"/"+ this.props.instance.configurationId + '/' + this.props.instance.instanceId; 100 | 101 | }, 102 | render: function() { 103 | var rows = ConfService.instanceList.slice() || []; 104 | var _cat; 105 | if(this.state.error) { 106 | _cat = "ERROR"; 107 | } else if(this.state.showWarning) { 108 | _cat = "WARNING"; 109 | } else { 110 | _cat = "SUCCESS"; 111 | } 112 | return ( 113 |
114 | {this.state.status && ()} 115 |
116 | 117 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | { rows.map(function(item, idx) { 133 | return 134 | })} 135 | 136 |
DescriptionActive InWebhook URLLast PostedActions
137 |
138 | {this.state.loading && (

{ConfService.messages.loading}

)} 139 |
140 | ) 141 | } 142 | }) 143 | 144 | var DataRow = React.createClass ({ 145 | propTypes: { 146 | instance: React.PropTypes.object, 147 | copyId: React.PropTypes.number 148 | }, 149 | getInitialState: function() { 150 | return { 151 | enableCopy: true 152 | } 153 | }, 154 | onClickRemove: function() { 155 | hashHistory.push('/remove-view/'+ this.props.instance.instanceId +'/'+ this.props.instance.name); 156 | }, 157 | onClickEdit: function() { 158 | hashHistory.push('/edit-view/'+ this.props.instance.instanceId); 159 | }, 160 | onCopyURL: function(e) { 161 | var target = e.target; 162 | this.setState({ 163 | enableCopy: false 164 | }) 165 | // Copy to clipboard without displaying input 166 | var textarea = document.createElement('textarea'); 167 | textarea.style.position = 'relative'; 168 | textarea.style.top = 0; 169 | textarea.style.left = 0; 170 | textarea.style.width = '1px'; 171 | textarea.style.height = '1px'; 172 | textarea.style.padding = 0; 173 | textarea.style.border = 0; 174 | textarea.style.outline = 0; 175 | textarea.style.boxShadow = 0; 176 | textarea.style.background = 'transparent'; 177 | textarea.style.fontSize = 0; 178 | 179 | var webhookUrl = document.querySelector(target.dataset.copytarget) ? document.querySelector(target.dataset.copytarget).getAttribute('data-value') : null; 180 | textarea.value = webhookUrl; 181 | if (textarea) { 182 | target.parentNode.appendChild(textarea); 183 | textarea.select(); 184 | try { 185 | // copy text 186 | document.execCommand('copy'); 187 | target.innerHTML = "Copied!"; 188 | var that = this; 189 | setTimeout(function(){ 190 | target.innerHTML = "Copy URL"; 191 | target.parentNode.removeChild(target.parentNode.getElementsByTagName('textarea')[0]); 192 | that.setState({ 193 | enableCopy: true 194 | }) 195 | },2000); 196 | } catch (err) { 197 | console.log(err); 198 | } 199 | } else { 200 | console.log('element not found ' + textarea); 201 | } 202 | }, 203 | render: function() { 204 | var posting_locations_names = []; 205 | this.props.instance.postingLocationsRooms.map(function(item,i){ 206 | posting_locations_names.push(item.name); 207 | }) 208 | var that = this; 209 | var _url = '/crud-view/' + this.props.instance.instanceId; 210 | 211 | var whUrl = ConfService.baseURL +"/v1/whi/"+ ConfService.appId +"/"+ this.props.instance.configurationId + '/' + this.props.instance.instanceId; 212 | var cropedWebHookUrl = whUrl.substr(0, 35) + '...'; 213 | return( 214 | 215 | {this.props.instance.name} 216 | 217 |
    218 | {posting_locations_names.map(function(e, i){ 219 | return
  • {that.props.instance.streamType == "IM" ? ConfService.labelPostinLocations : e} { (i < posting_locations_names.length-1) ? ", " : ""}
  • 220 | })} 221 | {posting_locations_names.length === 0 && this.props.instance.streamType == "IM" && (
  • {ConfService.labelPostinLocations + config.IM_shorthand}
  • )} 222 |
223 | 224 | 225 |
226 | {cropedWebHookUrl} 227 | { this.state.enableCopy ? Copy URL : Copy URL } 228 |
229 | 230 | {this.props.instance.lastPosted} 231 | 232 | 236 | 237 | 238 | ) 239 | } 240 | }) 241 | ReactDOM.render(, document.getElementById("instance-list")); 242 | module.exports = ListView; 243 | -------------------------------------------------------------------------------- /src/main/webapp/commons/components/SearchRooms/SearchRooms.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | var ConfService = require('../../js/configurator.service.js'); 4 | 5 | var SearchRooms = React.createClass({ 6 | propTypes: { 7 | filteredRooms: React.PropTypes.arrayOf(React.PropTypes.object).isRequired, 8 | rooms: React.PropTypes.arrayOf(React.PropTypes.object).isRequired, 9 | filters: React.PropTypes.arrayOf(React.PropTypes.object).isRequired, 10 | onChangeInput: React.PropTypes.func.isRequired, 11 | callAddFilter: React.PropTypes.func.isRequired, 12 | callRemoveFilter: React.PropTypes.func.isRequired, 13 | callClearInput: React.PropTypes.func.isRequired 14 | }, 15 | getInitialState: function() { 16 | var __filters = []; 17 | var that = this; 18 | var __obj = {}; 19 | this.props.filters.map(function(elem, idx) { 20 | __filters.push(elem); 21 | }); 22 | return { 23 | filters: __filters, //filters: [], //array of objects 24 | disabled: false, 25 | saved: ConfService.newInstanceCreated, 26 | filled: false, 27 | required: true, 28 | focused: -1, 29 | listening: false 30 | }; 31 | }, 32 | componentWillMount: function() { 33 | if(this.props.filters.length > 0) { 34 | this.setState({ 35 | filled: true, 36 | required: false 37 | }) 38 | ConfService.required.rooms = false; 39 | } else { 40 | ConfService.required.rooms = true; 41 | } 42 | }, 43 | componentDidMount: function() { 44 | var input = this.refs.inputSearch; 45 | input.focus(); 46 | //input.addEventListener('keydown', this.inputListener); 47 | }, 48 | inputListener: function() { 49 | var idx = this.state.focused; 50 | var input = this.refs.inputSearch; 51 | if(document.getElementsByTagName("ul")[0] !== undefined) { 52 | if(event.keyCode == "40") { //down 53 | input.blur(); 54 | input.removeEventListener('keydown', this.inputListener); 55 | if(this.props.filteredRooms.length > 0 && idx < this.props.filteredRooms.length) { 56 | idx++; 57 | document.getElementsByTagName("ul")[0].focus(); 58 | document.getElementsByTagName("ul")[0].addEventListener('keydown', this.listListener); 59 | } 60 | } 61 | this.setState({ 62 | focused: idx 63 | }) 64 | } 65 | }, 66 | listListener: function() { 67 | var idx = this.state.focused; 68 | var input = this.refs.inputSearch; 69 | if(event.keyCode == "40") { //down 70 | if(this.props.filteredRooms.length > 0 && idx < this.props.filteredRooms.length-1) { 71 | idx++; 72 | } 73 | } else if(event.keyCode == "38") { //up 74 | if(idx > 0) { 75 | idx--; 76 | } else { 77 | idx = -1; 78 | document.getElementsByTagName("ul")[0].removeEventListener('keydown', this.listListener); 79 | var _tmr = setInterval(function(){ 80 | if(input.value != "") { 81 | clearInterval(_tmr); 82 | input.focus(); 83 | } 84 | },50); 85 | input.addEventListener('keydown', this.inputListener); 86 | } 87 | } 88 | this.setState({ 89 | focused: idx 90 | }) 91 | }, 92 | addFilterBox: function(elem, event) { //expects a object room 93 | var input = this.refs.inputSearch; 94 | this.setState({ 95 | filters: this.state.filters.concat([elem]) 96 | }); 97 | this.props.callAddFilter(elem, event); 98 | input.value = ""; 99 | input.focus(); 100 | this.setState({ 101 | filled: true, 102 | required: false, 103 | focused: -1 104 | }) 105 | ConfService.required.rooms = false; 106 | input.addEventListener('keydown', this.inputListener); 107 | }, 108 | removeFilterBox: function(elem) { 109 | var _filters = this.state.filters.slice(); 110 | _filters.map(function(item, i) { 111 | if(item['threadId'] === elem['threadId']) { 112 | _filters.splice(i,1); 113 | } 114 | }); 115 | this.setState({ 116 | filters: _filters 117 | }); 118 | if (_filters.length == 0) { 119 | this.setState({ 120 | filled: false, 121 | required: true 122 | }) 123 | ConfService.required.rooms = true; 124 | }; 125 | this.refs.inputSearch.focus(); 126 | this.props.callRemoveFilter(elem, this.refs.inputSearch); 127 | }, 128 | onChangeInput: function(e) { 129 | var input = e.target; 130 | var that = this; 131 | this.props.onChangeInput(e); 132 | if(input.value != "") { 133 | if(!this.state.listening) { 134 | this.setState({ 135 | listening: true 136 | }) 137 | input.addEventListener('keydown', this.inputListener); 138 | } 139 | } else if(input.value == "") { 140 | this.setState({ 141 | listening: false 142 | }) 143 | input.removeEventListener('keydown', this.inputListener); 144 | } 145 | 146 | }, 147 | clearInputSearch: function() { 148 | this.refs.inputSearch.value = ""; 149 | this.refs.inputSearch.focus(); 150 | this.props.callClearInput(); 151 | return false; 152 | }, 153 | render: function() { 154 | var that = this; 155 | return( 156 |
157 |
158 |
159 | 160 | 161 |
162 |
163 |
164 | {this.props.filteredRooms.length > 0 && ()} 165 |
166 |
0 ? "filter-box-container-hide" : "filter-box-container"}> 167 | {this.state.filters.map(function(item, idx) { 168 | return 169 | })} 170 |
171 |
172 |
173 | {/*this.state.filled ? : */} 174 | {!this.state.filled && ()} 175 |
176 | ); 177 | } 178 | }); 179 | 180 | /* SugestionsList List of sugestions based on the content typed within the input search 181 | */ 182 | var SugestionsList = React.createClass({ 183 | propTypes: { 184 | items: React.PropTypes.arrayOf(React.PropTypes.object).isRequired, 185 | callAddFilter: React.PropTypes.func.isRequired, 186 | focusItem: React.PropTypes.number.isRequired 187 | }, 188 | //hasListener: false, 189 | //focusIndex: -1, 190 | getInitialState: function() { 191 | return { 192 | items: this.props.items 193 | } 194 | }, 195 | onSetFocus: function(_val) { 196 | this.setState({ 197 | focusedItem: _val 198 | }) 199 | }, 200 | addFilter: function(item, event) { 201 | this.props.callAddFilter(item, event); 202 | return false; 203 | }, 204 | render: function() { 205 | var that = this; 206 | var members; 207 | return( 208 | 231 | ); 232 | } 233 | }) 234 | 235 | /* FilterBox Component filter 236 | */ 237 | var FilterBox = React.createClass({ 238 | propTypes: { 239 | room: React.PropTypes.object.isRequired, 240 | callRemoveFilter: React.PropTypes.func.isRequired 241 | }, 242 | removeFilter: function() { 243 | this.props.callRemoveFilter(this.props.room); 244 | return false; 245 | }, 246 | render: function() { 247 | var members = this.props.room['memberCount'] > 1 ? this.props.room['memberCount'] +" Members" : this.props.room['memberCount'] +" Member";; 248 | return( 249 |
250 | {this.props.room['publicRoom'] == false ?
{this.props.room['name']}
:
{this.props.room['name']}
} 251 |
252 | {members +", created by "+ this.props.room['creatorPrettyName']} 253 |
254 | 255 |
256 | ); 257 | } 258 | }) 259 | module.exports = SearchRooms; 260 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright 2016-2017 Symphony LLC 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /src/main/webapp/commons/js/app.js: -------------------------------------------------------------------------------- 1 | require('../../vendors/font-awesome-4.6.3/css/font-awesome.min.css'); 2 | require('../../vendors/jquery-1.7.1/jquery-1.7.1.min.js'); 3 | require('../styles/styles.css'); 4 | var ConfService = require('./configurator.service.js'); // ConfService js object that stores instance and user infomration on memory to share info among screens 5 | var instanceList = []; // instanceList main object. Stores all instance webhook objects. 6 | var configurationId; // configurator id 7 | var app_id = getParameterByName('id'); 8 | var config = require('../../configurator/config.js'); 9 | 10 | import React from 'react' 11 | import { render } from 'react-dom' 12 | import { Link, Router, Route, hashHistory, IndexRoute } from 'react-router' 13 | import Home from '../views/Home' 14 | import ListView from '../views/ListView' 15 | import CreateView from '../views/CreateView' 16 | import SaveWebHook from '../views/SaveWebHook' 17 | import Success from '../views/Success' 18 | import EditView from '../views/EditView' 19 | import RemoveView from '../views/RemoveView' 20 | 21 | /* getParameterByName get url parameters */ 22 | function getParameterByName(name, url) { 23 | if (!url) url = window.location.href; 24 | name = name.replace(/[\[\]]/g, "\\$&"); 25 | var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"), 26 | results = regex.exec(url); 27 | if (!results) return null; 28 | if (!results[2]) return ''; 29 | return decodeURIComponent(results[2].replace(/\+/g, " ")); 30 | } 31 | 32 | /* SYMPHONY API */ 33 | SYMPHONY.remote.hello().then(function(data) { 34 | setTheme(data.themeV2); 35 | function setTheme(theme) { 36 | document.getElementsByTagName('html')[0].className = 'symphony-external-app '+ theme.name +' '+theme.size; 37 | } 38 | function onThemeChange(theme) { 39 | setTheme(theme); 40 | } 41 | SYMPHONY.application.connect(app_id, ['ui', 'modules', 'applications-nav', "extended-user-service", "integration-config", "stream-service"], [app_id+':module']).then(function(response) { 42 | //set theme 43 | var uiService = SYMPHONY.services.subscribe('ui'); 44 | uiService.listen('themeChangeV2', onThemeChange.bind(this)); 45 | 46 | // subscribe services 47 | // exteded user service 48 | var extendedUserService = SYMPHONY.services.subscribe("extended-user-service"); 49 | // integration config services 50 | var integrationConfService = SYMPHONY.services.subscribe("integration-config"); 51 | 52 | // controls the warning of empty instances on list view 53 | var dataResponse 54 | // get configurationId 55 | configurationId = getParameterByName('configurationId'); 56 | // get botUserId 57 | var botUserId = getParameterByName('botUserId'); 58 | // store all user chat rooms 59 | var userChatRooms = []; 60 | // userId 61 | var userId; 62 | // baseURL 63 | var baseURL = window.location.protocol + "//" + window.location.hostname + "/integration"; 64 | // user instances list 65 | var instanceList = []; 66 | // set timeout error exception 67 | var tmr; 68 | // default index route, ListView or CreateView 69 | var defaultIndexRoute = ListView; 70 | setTimeoutError(true); 71 | 72 | /** PROMISES **/ 73 | /* promise 1: get user rooms from api 74 | /* promise 2: get user id from api 75 | /* promise 3: get user's instances list from api 76 | */ 77 | //--> (promise 1) 78 | var promisedRooms = extendedUserService.getRooms(); 79 | //--> (promise 2) 80 | var promisedUserId = extendedUserService.getUserId(); 81 | //--> (promise 3) 82 | var promisedList = integrationConfService.getConfigurationInstanceList(configurationId); 83 | /** END PROMISES **/ 84 | 85 | Promise.all([promisedRooms, promisedUserId, promisedList]).then(function(values) { 86 | getUserRooms(values[0]); 87 | userId = values[1]; 88 | ConfService.userId = userId.toString(); 89 | ConfService.botUserId = botUserId; 90 | ConfService.configurationId = configurationId; 91 | ConfService.baseURL = baseURL; 92 | ConfService.appId = app_id; 93 | ConfService.configurationName = config.app_name; 94 | ConfService.appTitle = config.app_title; 95 | ConfService.toogleSetup = config.toogleSetupInstructions; 96 | getUserInstanceList(values[2]); 97 | }); 98 | 99 | /* getUserRooms retrives all user's rooms 100 | * @param data data returned from the first promise 101 | */ 102 | function getUserRooms(data) { 103 | for(var prop in data) { 104 | if(data[prop].userIsOwner) { 105 | userChatRooms.push(data[prop]); 106 | } 107 | } 108 | var regExp = /\//g; 109 | // normalize all rooms threadIds 110 | userChatRooms.map(function(room, idx) { 111 | room.threadId = room.threadId.replace(regExp,'_').replace("==",""); 112 | }, this); 113 | ConfService.userChatRooms = userChatRooms.slice(); 114 | } 115 | 116 | /* getUserInstanceList retrieves all user's webhook instances 117 | * @param data data returned from the fourth promise 118 | */ 119 | function getUserInstanceList(data) { 120 | dataResponse = data.length || 0; 121 | if(dataResponse == 0) ConfService.dataResponse = 0; 122 | // retrieve all webhook instances 123 | for(var obj in data) { 124 | var op = data[obj].optionalProperties; 125 | var obj_op = JSON.parse(op); 126 | instanceList.push( 127 | { 128 | streams: obj_op.streams, // rooms threadId's (string) that are posting locations 129 | name: data[obj].name, 130 | configurationId: data[obj].configurationId, 131 | postingLocationsRooms: [], // user rooms (object) that are posting locations 132 | notPostingLocationsRooms: [], // user rooms (object) that are NOT posting locations 133 | description: '', 134 | instanceId: data[obj].instanceId, 135 | lastPostedTimestamp: obj_op.lastPostedDate, 136 | streamType: obj_op['streamType'], 137 | lastPosted: obj_op.lastPostedDate ? timestampToDate(obj_op.lastPostedDate) : 'not available', 138 | created: data[obj].createdDate ? timestampToDate(data[obj].createdDate) : 'not available' 139 | } 140 | ); 141 | }; 142 | // stores all posting locations (object) into instanceList 143 | var aux_rooms = []; 144 | instanceList.map(function(inst, i) { 145 | inst.streams.map(function(stream, j) { 146 | userChatRooms.map(function(room, k) { 147 | if(stream == room.threadId) { 148 | inst.postingLocationsRooms.push(clone(room)); 149 | } 150 | }); 151 | }); 152 | }); 153 | // stores all indexes of the rooms (object) that are not posting locations into an array 154 | var pl, idx, aux; 155 | instanceList.map(function(inst, i){ 156 | pl=false; 157 | idx = []; 158 | aux=userChatRooms.slice(); 159 | inst.streams.map(function(stream, j){ 160 | for(var k=0,n=aux.length; k 0 ? ListView : CreateView; 185 | render(( 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | ), document.getElementById('app')) 198 | } 199 | 200 | // set timeout error 201 | function setTimeoutError(val) { 202 | if(val) { 203 | tmr = setTimeout(function() { 204 | render(( 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | ), document.getElementById('app')) 217 | clearTimeout(tmr); 218 | hashHistory.push('/save-webhook/error'); 219 | }, ConfService.timeout); 220 | } else { 221 | clearTimeout(tmr); 222 | } 223 | } 224 | 225 | /********** HELPER FUNCTIONS *********/ 226 | 227 | /* timestampToDate format unix timestamp in date format */ 228 | function timestampToDate(_ts) { 229 | var date = new Date(Number(_ts)); 230 | var monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; 231 | var month = monthNames[date.getMonth()]; 232 | return month +' '+ date.getDate() +', '+date.getFullYear(); 233 | } 234 | 235 | /* javascript clone object function */ 236 | function clone(obj) { 237 | if (null == obj || "object" != typeof obj) return obj; 238 | var copy = obj.constructor(); 239 | for (var attr in obj) { 240 | if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr]; 241 | } 242 | return copy; 243 | } 244 | 245 | }.bind(this)) 246 | }.bind(this)); 247 | -------------------------------------------------------------------------------- /src/main/webapp/commons/views/SaveWebHook.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import { hashHistory } from 'react-router' 4 | import Utils from '../js/utils.service'; 5 | import ConfService from '../js/configurator.service'; 6 | 7 | var SaveWebHook = React.createClass({ 8 | propTypes: { 9 | params: React.PropTypes.object 10 | }, 11 | componentDidMount: function() { 12 | var that = this; 13 | // integration config services 14 | var integrationConfService = SYMPHONY.services.subscribe("integration-config"); 15 | // configurationId 16 | var configurationId = ConfService.configurationId; 17 | // IM service 18 | var streamService = SYMPHONY.services.subscribe('stream-service'); 19 | // Build Optional Properties 20 | var str_streams = ""; 21 | var postingLocationsRooms = ConfService.protoInstance.postingLocationsRooms.slice(); 22 | postingLocationsRooms.map(function(item,i){ 23 | str_streams += "\""+ item.threadId +"\""; 24 | if(i < postingLocationsRooms.length-1) { 25 | str_streams += ","; 26 | } 27 | }) 28 | var optionalProperties; 29 | if(ConfService.protoInstance.lastPostedDate) { 30 | optionalProperties = "{\"owner\":\""+ ConfService.userId +"\",\"streams\":["+ str_streams +"],\"lastPostedDate\":"+ ConfService.protoInstance.lastPostedDate +",\"streamType\":\""+ ConfService.protoInstance.streamType +"\"}"; 31 | } else { 32 | optionalProperties = "{\"owner\":\""+ ConfService.userId +"\",\"streams\":["+ str_streams +"],\"streamType\":\""+ ConfService.protoInstance.streamType +"\"}"; 33 | } 34 | var _streams = []; 35 | ConfService.protoInstance.postingLocationsRooms.map(function(item,i){ 36 | _streams.push(item.threadId); 37 | }) 38 | // errors... 39 | var timeout = setTimeout(function(){ 40 | hashHistory.push('/list-view/error'); 41 | },ConfService.timeout); 42 | if(this.props.params) { 43 | if(this.props.params.instanceId == "error") { 44 | clearTimeout(timeout); 45 | hashHistory.push('/list-view/error'); 46 | } 47 | } 48 | // TYPE STREAM IS IM 49 | if(ConfService.protoInstance.streamType == "IM") { 50 | // UPDATE IM 51 | if(this.props.params.instanceId !== undefined && ConfService.protoInstance.name !== "") { 52 | var promisedIM = streamService.createIM([ConfService.botUserId]); 53 | promisedIM.then(function(data){ 54 | _streams.push(data.id); 55 | var streamId = data.id; 56 | optionalProperties = "{\"owner\":\""+ ConfService.userId +"\",\"streams\":[\""+ data.id +"\"],\"streamType\":\""+ ConfService.protoInstance.streamType +"\"}"; 57 | var payload = { 58 | instanceId: ConfService.protoInstance.instanceId, 59 | configurationId: ConfService.configurationId, 60 | name: ConfService.protoInstance.name, 61 | description: ConfService.protoInstance.description, 62 | optionalProperties: optionalProperties 63 | } 64 | var updateInstance = integrationConfService.updateConfigurationInstanceById(ConfService.configurationId, ConfService.protoInstance.instanceId, payload); 65 | updateInstance.then(function(data){ 66 | if (!checkExistingIM()) { 67 | that.sendWelcomeMessage(_streams, data.instanceId); 68 | } 69 | callUpdateInstance(data); 70 | 71 | },function(error){ 72 | console.log('error: ',error); 73 | clearTimeout(timeout); 74 | hashHistory.push('/list-view/error'); 75 | }) 76 | },function(error){ 77 | console.log('error: ',error); 78 | clearTimeout(timeout); 79 | hashHistory.push('/list-view/error'); 80 | }); 81 | } 82 | // REMOVE IM 83 | else if(this.props.params.instanceId !== undefined) { 84 | callRemoveInstance(); 85 | } 86 | // CREATE IM 87 | else { 88 | // request the IM stream from service 89 | var promisedIM = streamService.createIM([ConfService.botUserId]); 90 | promisedIM.then(function(data){ 91 | _streams.push(data.id); 92 | var streamId = data.id; 93 | optionalProperties = "{\"owner\":\""+ ConfService.userId +"\",\"streams\":[\""+ data.id +"\"],\"streamType\":\""+ ConfService.protoInstance.streamType +"\"}"; 94 | var payload = { 95 | configurationId: ConfService.configurationId, 96 | name: ConfService.protoInstance.name, 97 | description: ConfService.protoInstance.description, 98 | creatorId: ConfService.userId, 99 | optionalProperties: optionalProperties 100 | } 101 | var saveInstance = integrationConfService.createConfigurationInstance(ConfService.configurationId, payload); 102 | saveInstance.then( 103 | function (data) { 104 | if (!checkExistingIM()) { 105 | Utils.sendWelcomeMessage(_streams, data.instanceId); 106 | } 107 | callSetNewInstance(data); 108 | 109 | }, function (error) { 110 | console.log('error: ', error); 111 | clearTimeout(timeout); 112 | hashHistory.push('/list-view/error'); 113 | }) 114 | 115 | },function(error){ 116 | console.log('error: ',error); 117 | clearTimeout(timeout); 118 | hashHistory.push('/list-view/error'); 119 | }) 120 | } 121 | } 122 | // TYPE STREAM IS CHATROOM 123 | else if(ConfService.protoInstance.streamType == "CHATROOM") { 124 | // UPDATE Chat room 125 | if(this.props.params.instanceId !== undefined && ConfService.protoInstance.name !== "") { 126 | if(_streams.length > 0) { 127 | var promises = []; 128 | _streams.map(function(item) { 129 | promises.push(streamService.addRoomMembership(item,ConfService.botUserId)); 130 | }) 131 | 132 | Promise.all(promises).then(function(data) { 133 | updateRooms(data); 134 | }, function(err) { 135 | clearTimeout(timeout); 136 | hashHistory.push('/list-view/error'); 137 | }) 138 | 139 | } else { 140 | updateRooms(); 141 | } 142 | function updateRooms(dataRooms) { 143 | // #2 after the bot user was added to all rooms, call the service to update the instance ... 144 | var payload = { 145 | instanceId: that.props.params.instanceId, 146 | configurationId: ConfService.configurationId, 147 | name: ConfService.protoInstance.name, 148 | description: ConfService.protoInstance.description, 149 | optionalProperties: optionalProperties 150 | } 151 | var updateInstance = integrationConfService.updateConfigurationInstanceById(ConfService.configurationId, that.props.params.instanceId, payload); 152 | updateInstance.then(function(data){ 153 | Utils.sendWelcomeMessage(ConfService.protoInstance.newPostingLocationsRooms, data.instanceId); 154 | callUpdateInstance(data); 155 | },function(error){ 156 | console.log('error: ',error); 157 | clearTimeout(timeout); 158 | hashHistory.push('/list-view/error'); 159 | }) 160 | } 161 | } 162 | // REMOVE Chat room 163 | else if(this.props.params.instanceId !== undefined) { 164 | callRemoveInstance(); 165 | } 166 | // CREATE Chat room 167 | else { 168 | if(_streams.length > 0) { 169 | var promises = []; 170 | for(var str in _streams) { 171 | promises.push(streamService.addRoomMembership(_streams[str],ConfService.botUserId)); 172 | } 173 | Promise.all(promises).then(function(data){ 174 | createRooms(data); 175 | },function(err){ 176 | console.log('error: ',err); 177 | clearTimeout(timeout); 178 | hashHistory.push('/list-view/error'); 179 | }) 180 | } else { 181 | createRooms(); 182 | } 183 | function createRooms(dataRooms) { 184 | // #2 after the bot user was added to all rooms, call the service to create a new instance ... 185 | var payload = { 186 | configurationId: ConfService.configurationId, 187 | name: ConfService.protoInstance.name, 188 | description: ConfService.protoInstance.description, 189 | creatorId: ConfService.userId, 190 | optionalProperties: optionalProperties 191 | } 192 | var saveInstance = integrationConfService.createConfigurationInstance(ConfService.configurationId, payload); 193 | saveInstance.then( 194 | function (data) { 195 | Utils.sendWelcomeMessage(_streams, data.instanceId); 196 | callSetNewInstance(data); 197 | }, function (error) { 198 | console.log('error: ', error); 199 | clearTimeout(timeout); 200 | hashHistory.push('/list-view/error'); 201 | }) 202 | } 203 | } 204 | } 205 | //------------ HELPER FUNCTIONS -------------// 206 | // callSetNewInstance set new instance on configurator service 207 | function callSetNewInstance(_data) { 208 | ConfService.protoInstance.instanceId = _data.instanceId; 209 | var obj_OP = JSON.parse(_data.optionalProperties); 210 | ConfService.setNewInstance({ 211 | streams: _streams, 212 | name: ConfService.protoInstance.name, 213 | configurationId: ConfService.configurationId, 214 | postingLocationsRooms: ConfService.protoInstance.postingLocationsRooms.slice(), 215 | newPostingLocationsRooms: [], 216 | notPostingLocationsRooms: ConfService.protoInstance.notPostingLocationsRooms.slice(), 217 | description: ConfService.protoInstance.description, 218 | instanceId: ConfService.protoInstance.instanceId, 219 | lastPosted: obj_OP.lastPostedDate ? timestampToDate(obj_OP.lastPostedDate) : 'not available', 220 | streamType: ConfService.protoInstance.streamType, 221 | created: _data.createdDate ? timestampToDate(_data.createdDate) : 'not available' 222 | }); 223 | clearTimeout(timeout); 224 | hashHistory.push('/success'); 225 | } 226 | // callUpdateInstance update an instance on configurator service 227 | function callUpdateInstance(_data) { 228 | ConfService.protoInstance.instanceId = _data.instanceId; 229 | var obj_OP = JSON.parse(_data.optionalProperties); 230 | ConfService.updateInstance(ConfService.protoInstance.instanceId ,{ 231 | streams: _streams, 232 | name: ConfService.protoInstance.name, 233 | configurationId: ConfService.configurationId, 234 | postingLocationsRooms: ConfService.protoInstance.postingLocationsRooms.slice(), 235 | newPostingLocationsRooms: [], 236 | notPostingLocationsRooms: ConfService.protoInstance.notPostingLocationsRooms.slice(), 237 | description: ConfService.protoInstance.description, 238 | instanceId: ConfService.protoInstance.instanceId, 239 | lastPosted: obj_OP.lastPostedDate ? timestampToDate(obj_OP.lastPostedDate) : 'not available', 240 | streamType: ConfService.protoInstance.streamType, 241 | created: ConfService.protoInstance.created ? ConfService.protoInstance.created : 'not available' 242 | }); 243 | ConfService.resetProtoInstance(); 244 | clearTimeout(timeout); 245 | hashHistory.push('/list-view/updated'); 246 | } 247 | // callRemoveInstance remove the instance on configurator service 248 | function callRemoveInstance() { 249 | var deactivateInstance = integrationConfService.deactivateConfigurationInstanceById(ConfService.configurationId, that.props.params.instanceId); 250 | deactivateInstance.then(function(data){ 251 | ConfService.resetProtoInstance(); 252 | ConfService.removeInstanceById(that.props.params.instanceId); 253 | clearTimeout(timeout); 254 | hashHistory.push('/list-view/deactivated'); 255 | },function(error){ 256 | console.log('error: ',error); 257 | clearTimeout(timeout); 258 | hashHistory.push('/list-view/error'); 259 | }) 260 | } 261 | // timestampToDate format unix timestamp in date format 262 | function timestampToDate(_ts) { 263 | var date = new Date(Number(_ts)); 264 | var monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; 265 | var month = monthNames[date.getMonth()]; 266 | return month +' '+ date.getDate() +', '+date.getFullYear(); 267 | } 268 | // checkExistingIM check if there are any IM stream type instance 269 | function checkExistingIM() { 270 | var im = ConfService.instanceList.filter(function(item) { return item.streamType === 'IM' }); 271 | return im.length > 0 ? true : false; 272 | } 273 | }, 274 | 275 | render: function() { 276 | return( 277 |
278 |

{ConfService.messages.working}

279 |
280 | ); 281 | } 282 | }) 283 | module.exports = SaveWebHook; -------------------------------------------------------------------------------- /src/main/webapp/commons/styles/styles.css: -------------------------------------------------------------------------------- 1 | ::-webkit-scrollbar { 2 | width: 10px; /* 1px wider than Lion. */ 3 | height: 10px; 4 | /* This is more usable for users trying to click it. */ 5 | background-color: rgba(0,0,0,0); 6 | -webkit-border-radius: 100px; 7 | } 8 | ::-webkit-scrollbar-corner { 9 | background: transparent; 10 | } 11 | /* hover effect for both scrollbar area, and scrollbar 'thumb' */ 12 | ::-webkit-scrollbar:hover { 13 | background-color: rgba(0, 0, 0, 0.09); 14 | } 15 | /* The scrollbar 'thumb' ...that marque oval shape in a scrollbar */ 16 | ::-webkit-scrollbar-thumb:vertical, ::-webkit-scrollbar-thumb:horizontal { 17 | background: rgba(0,0,0,0.5); 18 | -webkit-border-radius: 100px; 19 | background-clip: padding-box; 20 | border: 2px solid rgba(0, 0, 0, 0); 21 | min-height: 10px; /*Prevent it from getting too small */ 22 | } 23 | ::-webkit-scrollbar-thumb:vertical:active, ::-webkit-scrollbar-thumb:horizontal:active { 24 | background: rgba(0,0,0,0.61); /* Some darker color when you click it */ 25 | -webkit-border-radius: 100px; 26 | } 27 | /* IE11 soln */ 28 | html, body { 29 | scrollbar-shadow-color: rgba(black, 0); 30 | scrollbar-face-color: rgba(black, 0.5); 31 | scrollbar-track-color: rgba(black, 0); 32 | scrollbar-arrow-color: rgba(black, 0); 33 | } 34 | /* Dark contrast visibility */ 35 | body.dark-contrast ::-webkit-scrollbar-thumb:vertical, ::-webkit-scrollbar-thumb:horizontal { 36 | background: rgba(110,110,110,0.5); 37 | background-clip: padding-box; 38 | padding: 0; 39 | margin: 0 40 | } 41 | body h5 { 42 | /*display: inline-flex;*/ 43 | padding: 0; 44 | font: 600 15px "Alright Sans", "Avenir", "Tahoma", sans-serif; 45 | } 46 | ol li { 47 | margin: 0 0 10px 0; 48 | } 49 | .container-component { 50 | display: flex; 51 | width: 100%; 52 | flex-direction: column; 53 | } 54 | .component { 55 | display: flex; 56 | flex-grow: 1; 57 | flex-direction: column; 58 | margin: 0 0 20px 0; 59 | } 60 | .block { 61 | display: flex; 62 | flex-grow: 1; 63 | flex-direction: column; 64 | padding: 20px; 65 | margin: 0 0 10px 0; 66 | box-sizing: border-box; 67 | } 68 | 69 | /* Setup Instructions */ 70 | .setup-instructions { 71 | border: 1px solid rgba(180, 180, 180, 0.4); 72 | padding: 8px; 73 | margin-bottom: 24px; 74 | max-height: 51px; 75 | overflow: hidden; 76 | border-radius: 2px; 77 | transition: max-height 0.7s; 78 | } 79 | .setup-instructions-transition { 80 | max-height: 5000px; 81 | } 82 | .setup-instructions p, body .setup-instructions h2, body .setup-instructions h4 { 83 | font-weight: normal; 84 | } 85 | .setup-instructions h2, body .setup-instructions h4 { 86 | font-family: "Alright Sans", "Avenir", "Tahoma", sans-serif; 87 | font-weight: 600; 88 | } 89 | .setup-instructions h4 { 90 | margin-top: 2rem; 91 | } 92 | .dark .setup-instructions p, .dark body .setup-instructions h2, .dark body .setup-instructions h4, 93 | .dark-contrast .setup-instructions p, .dark-contrast body .setup-instructions h2, .dark-contrast body .setup-instructions h4 { 94 | color: #fff; 95 | } 96 | .light .setup-instructions p, .light body .setup-instructions h2, .light body .setup-instructions h4, 97 | .light-contrast .setup-instructions p, .light-contrast body .setup-instructions h2, .light-contrast body .setup-instructions h4 { 98 | color: #000; 99 | } 100 | .setup-instructions-header { 101 | display: flex; 102 | flex-direction: row; 103 | justify-content: space-between; 104 | margin-bottom: 24px; 105 | } 106 | .setup-instructions-header h2:first-child { 107 | font-family: "Alright Sans", "Avenir", "Tahoma", sans-serif; 108 | font-weight: 500; 109 | font-size: 1.6rem; 110 | margin: 0; 111 | } 112 | .setup-instructions-header > div > a { 113 | display: flex; 114 | align-items: center; 115 | height: 100%; 116 | font-size: 14px; 117 | text-decoration: none; 118 | } 119 | .dark .setup-instructions-header > div > a, 120 | .dark-contrast .setup-instructions-header > div > a { 121 | color: rgba(255, 255, 255, 0.5); 122 | } 123 | .light .setup-instructions-header > div > a, 124 | .light-contrast .setup-instructions-header > div > a { 125 | color: rgba(0, 0, 0, 0.5); 126 | } 127 | 128 | 129 | /* WHIDataGrid */ 130 | .hide { 131 | display: none !important; 132 | } 133 | .whi-table #header { 134 | display: flex; 135 | flex-direction: row; 136 | align-items: center; 137 | } 138 | .whi-table #header h2 { 139 | font-family: "Alright Sans", "Avenir", "Tahoma", sans-serif; 140 | font-weight: 500; 141 | font-size: 1.6rem; 142 | margin-right: 10px; 143 | } 144 | .whi-table #header button { 145 | 146 | } 147 | .whi-table table { 148 | float: left; 149 | border-collapse: collapse; 150 | border: 0; 151 | } 152 | .whi-table table tr th { 153 | padding: 5px 15px 0 0; 154 | font-size: 11px; 155 | text-align: left; 156 | } 157 | .dark .whi-table table tr th, 158 | .dark-contrast .whi-table table tr th { 159 | color: #fff; 160 | } 161 | .light .whi-table table tr th, 162 | .light-contrast .whi-table table tr th { 163 | color: #000; 164 | } 165 | .whi-table table tbody tr { 166 | border-bottom: 1px solid rgba(0, 0, 0, 0.1); 167 | } 168 | .whi-table table tr td { 169 | padding: 0 35px 0 0; 170 | min-width: 20%; 171 | } 172 | .whi-table table tr td:last-child { 173 | padding: 0; 174 | } 175 | .whi-table table tr td span { 176 | display: block; 177 | float: left; 178 | font-family: "Alright Sans", "Avenir", "Tahoma", sans-serif; 179 | font-size: 12px; 180 | font-weight: normal; 181 | color: #909090; 182 | } 183 | .dark .whi-table table tr td span, .dark .whi-table #header h2, 184 | .dark-contrast .whi-table table tr td span, .dark-contrast .whi-table #header h2 { 185 | color: #fff; 186 | } 187 | .light .whi-table table tr td span, .light .whi-table #header h2, 188 | .light-contrast .whi-table table tr td span, .light-contrast .whi-table #header h2 { 189 | color: #000; 190 | } 191 | .whi-table table tr td ul { 192 | float: left; 193 | width: 100%; 194 | padding: 0; 195 | list-style: none; 196 | } 197 | .whi-table table tr td ul li { 198 | float: left; 199 | margin: 0 7px 0 0; 200 | } 201 | .whi-table table tr td ul li .btn-remove { 202 | color: #F24405; 203 | } 204 | .whi-table table tr td .url-container { 205 | display: flex; 206 | flex-direction: row; 207 | justify-content: space-between; 208 | align-items: flex-start; 209 | } 210 | .whi-table table tr td .url-container span { 211 | margin: 0 10px 4px 0; 212 | max-width: 260px; 213 | word-break: break-all; 214 | } 215 | .whi-table table tr td .url-container a { 216 | display: flex; 217 | float: none; 218 | white-space: nowrap; 219 | width: 60px; 220 | } 221 | .whi-table table tr td a { 222 | display: block; 223 | float: left; 224 | font-size: 12px; 225 | } 226 | .whi-table table tr td a:hover { 227 | text-decoration: none; 228 | } 229 | /* Integration Identity */ 230 | .integration-identity-container { 231 | display: block; 232 | width: 100%; 233 | margin-bottom: 20px; 234 | } 235 | .integration-identity-container h3 { 236 | margin-bottom: 20px; 237 | } 238 | .dark .integration-identity-container h3, .dark .integration-identity-container h5 label, 239 | .dark-contrast .integration-identity-container h3, .dark-contrast .integration-identity-container h5 label { 240 | color: #fff; 241 | } 242 | .light .integration-identity-container h3, .light .integration-identity-container h5 label, 243 | .light-contrast .integration-identity-container h3, .light-contrast .integration-identity-container h5 label { 244 | color: #000; 245 | } 246 | .integration-identity-logo-div { 247 | display: flex; 248 | flex-grow: 1; 249 | flex-direction: row; 250 | justify-content: flex-start; 251 | align-items: center; 252 | margin-bottom: 24px; 253 | } 254 | .integration-identity-logo-div figure { 255 | display: inline-flex; 256 | margin: 0 10px 0 0; 257 | } 258 | .integration-identity-logo-div figure img { 259 | border-radius: 50%; 260 | } 261 | .integration-identity-input { 262 | display: flex; 263 | flex-grow: 1; 264 | } 265 | .integration-identity-input input, 266 | .light .integration-identity-input input, 267 | .dark .integration-identity-input input, 268 | .dark-contrast .integration-identity-input input { 269 | max-width: 490px; 270 | height: 32px; 271 | box-sizing: border-box; 272 | } 273 | span.required, 274 | span.checked, 275 | .light span.required, 276 | .light span.checked, 277 | .dark span.required, 278 | .dark span.checked, 279 | .dark-contrast span.checked, 280 | .dark-contrast span.required { 281 | display: block; 282 | float: left; 283 | padding: 7px 0 0 7px; 284 | color: #f00; 285 | font-size: 7px !important; 286 | } 287 | .dark span.required, 288 | .dark-contrast span.required { 289 | color: #f68128; 290 | } 291 | span.checked, 292 | .light span.checked, 293 | .dark span.checked, 294 | .dark-contrast span.checked { 295 | color: #5cb85c; 296 | font-size: 12px !important; 297 | } 298 | .warning-required { 299 | float: left; 300 | width: 100%; 301 | padding: 10px; 302 | background: #FE7A7D; 303 | border-radius: 4px; 304 | box-sizing: border-box; 305 | } 306 | .warning-required > div { 307 | width: 80%; 308 | } 309 | .warning-required p { 310 | color: #fff; 311 | text-align: left; 312 | } 313 | input.required-field, .required-field { 314 | border: 1px solid red !important; 315 | } 316 | .radio-group { 317 | display: flex; 318 | flex-direction: column; 319 | } 320 | .radio-group .radio { 321 | display: inline-flex; 322 | flex-grow: 1; 323 | align-items: center; 324 | min-height: 25px; 325 | } 326 | .radio-group .radio.top { 327 | align-items: flex-start; 328 | } 329 | .radio-group input[type='radio'] { 330 | display: inline-flex; 331 | width: auto; 332 | height: 18px; 333 | margin: 0 8px 0 0; 334 | } 335 | .radio-group label { 336 | display: flex; 337 | flex-grow: 1; 338 | } 339 | 340 | /***************************** REACT COMPONENTS ***************************/ 341 | /* Posting Location Component */ 342 | .posting-location-container { 343 | float: left; 344 | clear: both; 345 | width: 100%; 346 | margin-bottom: 20px; 347 | max-width: 100%; 348 | } 349 | .posting-location-container h5 { 350 | display: flex; 351 | margin-bottom: 10px; 352 | } 353 | .dark .posting-location-container h5, 354 | .dark-contrast .posting-location-container h5 { 355 | color: #fff; 356 | } 357 | .light .posting-location-container h5, 358 | .light-contrast .posting-location-container h5 { 359 | color: #000; 360 | } 361 | .posting-location-warning { 362 | width: 100%; 363 | max-width: 490px; 364 | line-height: 18px; 365 | } 366 | .posting-location-warning h6 { 367 | margin: 0.1rem 0px 0.4rem; 368 | } 369 | .posting-location-container .input-container { 370 | float: left; 371 | width: 100%; 372 | max-width: 490px; 373 | } 374 | .input-search-rooms-container { 375 | float: left; 376 | width: 100%; 377 | height: 32px; 378 | margin: 0 0 0 0; 379 | box-sizing: border-box; 380 | } 381 | .light .input-search-rooms-container, 382 | .light-contrast .input-search-rooms-container { 383 | border: 1px solid rgba(0, 0, 0, 0.08); 384 | background-color: #fff; 385 | border-radius: 4px; 386 | } 387 | .dark .input-search-rooms-container, 388 | .dark-contrast .input-search-rooms-container { 389 | border: 1px solid rgba(255, 255, 255, 0.08); 390 | border-radius: 3px; 391 | background-color: rgba(0, 0, 0, 0.4); 392 | } 393 | .input-search-rooms-container input { 394 | float: left; 395 | width: 93% !important; 396 | height: 32px !important; 397 | border: none !important; 398 | margin: 0 !important; 399 | box-sizing: border-box; 400 | background: none !important; 401 | } 402 | .input-search-rooms-container a { 403 | display: block; 404 | float: right; 405 | width: 5%; 406 | height: 100%; 407 | text-align: center; 408 | box-sizing: border-box; 409 | margin: 8px 0 0 0; 410 | } 411 | .dark .input-search-rooms-container a , 412 | .dark-contrast .input-search-rooms-container a { 413 | color: rgba(255, 255, 255, 0.8) !important; 414 | } 415 | .light .input-search-rooms-container a, 416 | .light-contrast .input-search-rooms-container a { 417 | color: rgba(0, 0, 0, 0.8) !important; 418 | } 419 | .list-container { 420 | float: left; 421 | width: 100%; 422 | margin: 0; 423 | padding: 0; 424 | border-top: 0; 425 | box-sizing: border-box; 426 | } 427 | .list-rooms-container { 428 | float: left; 429 | width: 100%; 430 | } 431 | .room-box { 432 | float: left; 433 | width: 100%; 434 | margin: 0; 435 | padding: 0 0.5% 0 0.5%; 436 | box-sizing: border-box; 437 | } 438 | .dark .room-box, 439 | .dark-contrast .room-box { 440 | background: #333; 441 | } 442 | .light .room-box, 443 | .light-contrast .room-box { 444 | background: #fff; 445 | } 446 | .room-box li { 447 | float: left; 448 | width: 100%; 449 | min-height: 32px; 450 | margin: 0; 451 | padding: 4px 2px; 452 | list-style-type: none; 453 | box-sizing: border-box; 454 | } 455 | .room-box li a { 456 | display: block; 457 | float: left; 458 | width: 100%; 459 | min-height: 28px; 460 | padding: 7px 3px 2px 5px; 461 | text-decoration: none; 462 | box-sizing: border-box; 463 | border-radius: 2px; 464 | background: #fff; 465 | } 466 | .dark .room-box li, 467 | .dark-contrast .room-box li { 468 | border-bottom: 1px solid rgba(255,255,255,.08); 469 | } 470 | .light .room-box li, 471 | .light-contrast .room-box li { 472 | border-bottom: 1px solid #cdcdcd; 473 | } 474 | .room-box li:last-child { 475 | border-bottom: 0; 476 | } 477 | .dark .room-box li a, 478 | .dark-contrast .room-box li a { 479 | background: #333; 480 | } 481 | .light .room-box li a, 482 | .light-contrast .room-box li a { 483 | 484 | } 485 | .dark .room-box li a:hover, .dark .room-box li a:focus, 486 | .dark-contrast .room-box li a:hover, .dark-contrast .room-box li a:focus { 487 | background: rgba(255,255,255,.05); 488 | } 489 | .light .room-box li a:hover, .light .room-box li a:focus, 490 | .light-contrast .room-box li a:hover, .light-contrast .room-box li a:focus { 491 | background: rgba(0,0,0,.05); 492 | } 493 | .room-box li a span { 494 | font-family: "Alright Sans",Avenir,Tahoma,sans-serif; 495 | font-weight: 500; 496 | } 497 | .room-box li a span, 498 | .dark .room-box li a span, 499 | .dark-contrast .room-box li a span { 500 | color: rgba(255,255,255,.8); 501 | } 502 | .light .room-box li a span, 503 | .light-contrast .room-box li a span { 504 | color: rgba(0,0,0,.8); 505 | } 506 | .room-box .room-info, .filter-box .room-info { 507 | display: block; 508 | float: left; 509 | width: 100%; 510 | } 511 | .room-box .room-info span, .filter-box .room-info span { 512 | font-size: 11px; 513 | font-weight: normal; 514 | } 515 | .dark .room-box .room-info span, .dark .filter-box .room-info span, 516 | .dark-contrast .room-box .room-info span, .dark-contrast .filter-box .room-info span { 517 | color: rgba(255,255,255,.6); 518 | } 519 | .light .room-box .room-info span, .light .filter-box .room-info span, 520 | .light-contrast .room-box .room-info span, .light-contrast .filter-box .room-info span { 521 | color: rgba(0,0,0,.6); 522 | } 523 | .room-box li a span { 524 | display: block; 525 | float: left; 526 | font-size: 13px; 527 | color: rgba(255,255,255,.8);; 528 | } 529 | .room-box li a i { 530 | float: left; 531 | padding: 1px 0 0 9px; 532 | color: #ee5555; 533 | font-size: 10px; 534 | } 535 | .filter-box-container, .filter-box-container-success { 536 | display: block; 537 | float: left; 538 | width: 100%; 539 | border-bottom-left-radius: 3px; 540 | border-bottom-right-radius: 3px; 541 | box-sizing: border-box; 542 | } 543 | .filter-box-container-hide { 544 | display: none; 545 | } 546 | .filter-box-container-success { 547 | width: 100%; 548 | max-width: 490px; 549 | } 550 | .filter-box-container, .filter-box-container-success { 551 | background: none; 552 | } 553 | .filter-box { 554 | float: left; 555 | width: 100%; 556 | height: 35px; 557 | padding: 5px 8px 0 8px; 558 | margin: 5px 0 0 0; 559 | border-radius: 3px; 560 | font-size: 12px; 561 | box-sizing: border-box; 562 | } 563 | .filter-box span { 564 | display: block; 565 | float: left; 566 | padding: 0 5px 0 0; 567 | } 568 | .filter-box a { 569 | display: block; 570 | float: right; 571 | padding: 0; 572 | } 573 | .filter-box span i { 574 | color: #ee5555; 575 | font-size: 10px; 576 | } 577 | .light .filter-box, 578 | .light-contrast .filter-box { 579 | background: #F9F9FA; 580 | color: rgba(0,0,0,.8); 581 | } 582 | .dark .filter-box, 583 | .dark-contrast .filter-box { 584 | background: #333; 585 | color: rgba(255,255,255,.8); 586 | } 587 | .light .filter-box a, 588 | .light-contrast .filter-box a { 589 | color: #959596; 590 | } 591 | .dark .filter-box a, 592 | .dark-contrast .filter-box a { 593 | color: rgba(255,255,255,.8); 594 | } 595 | .gen-webhook-url { 596 | float: left; 597 | width: 100%; 598 | margin: 15px 0 0 0; 599 | } 600 | .webhook-url-container { 601 | display: block; 602 | width: 100%; 603 | margin-bottom: 20px; 604 | } 605 | .dark .webhook-url-container h5, .dark-contrast .webhook-url-container h5 { 606 | color: #fff; 607 | } 608 | .light .webhook-url-container h5, .light-contrast .webhook-url-container h5 { 609 | color: #000; 610 | } 611 | .webhook-url-div input.ipt-whurl { 612 | width: 100%; 613 | max-width: 490px; 614 | height: 32px; 615 | box-sizing: border-box; 616 | } 617 | .webhook-url-div a { 618 | padding-left: 10px; 619 | } 620 | /* Submit Webhooks */ 621 | .submit-webhook { 622 | margin-top: 20px; 623 | } 624 | 625 | 626 | /* Success Views */ 627 | .success-view { 628 | float: left; 629 | width: 100%; 630 | height: auto; 631 | margin: 15px 0; 632 | padding: 5px 0 0 0; 633 | border-top: 1px solid rgba(0,0,0,.1); 634 | } 635 | .dark .success-view, 636 | .dark-contrast .success-view { 637 | border-top: 1px solid rgba(255,255,255,0.8); 638 | } 639 | .success-view button { 640 | float: right; 641 | display: block !important; 642 | margin: 15px 0 !important; 643 | min-width: 100px; 644 | } 645 | /* Remove view */ 646 | .remove-btn-container { 647 | margin: 0 auto; 648 | width: 98%; 649 | height: auto; 650 | padding: 5px 0 0 0; 651 | border-top: 1px solid rgba(0,0,0,.1); 652 | } 653 | .dark .remove-btn-container, 654 | .dark-contrast .remove-btn-container { 655 | border-top: 1px solid rgba(255,255,255,0.8); 656 | } 657 | .remove-btn-container button { 658 | float: right; 659 | margin: 15px 0 0 25px !important; 660 | min-width: 100px; 661 | } 662 | .spinner div { 663 | display: block; 664 | margin: 20px auto 10px auto; 665 | font-size: 40px; 666 | text-align: center; 667 | } 668 | .spinner p { 669 | font-size: 14px; 670 | text-align: center; 671 | } 672 | .save-spinner img { 673 | margin: 10px auto 0 auto; 674 | display: block; 675 | } 676 | .save-spinner p { 677 | margin: 100px 0 20px 0; 678 | text-align: center; 679 | font-size: 14px; 680 | } -------------------------------------------------------------------------------- /src/main/webapp/vendors/font-awesome-4.6.3/less/variables.less: -------------------------------------------------------------------------------- 1 | // Variables 2 | // -------------------------- 3 | 4 | @fa-font-path: "../fonts"; 5 | @fa-font-size-base: 14px; 6 | @fa-line-height-base: 1; 7 | //@fa-font-path: "//netdna.bootstrapcdn.com/font-awesome/4.6.3/fonts"; // for referencing Bootstrap CDN font files directly 8 | @fa-css-prefix: fa; 9 | @fa-version: "4.6.3"; 10 | @fa-border-color: #eee; 11 | @fa-inverse: #fff; 12 | @fa-li-width: (30em / 14); 13 | 14 | @fa-var-500px: "\f26e"; 15 | @fa-var-adjust: "\f042"; 16 | @fa-var-adn: "\f170"; 17 | @fa-var-align-center: "\f037"; 18 | @fa-var-align-justify: "\f039"; 19 | @fa-var-align-left: "\f036"; 20 | @fa-var-align-right: "\f038"; 21 | @fa-var-amazon: "\f270"; 22 | @fa-var-ambulance: "\f0f9"; 23 | @fa-var-american-sign-language-interpreting: "\f2a3"; 24 | @fa-var-anchor: "\f13d"; 25 | @fa-var-android: "\f17b"; 26 | @fa-var-angellist: "\f209"; 27 | @fa-var-angle-double-down: "\f103"; 28 | @fa-var-angle-double-left: "\f100"; 29 | @fa-var-angle-double-right: "\f101"; 30 | @fa-var-angle-double-up: "\f102"; 31 | @fa-var-angle-down: "\f107"; 32 | @fa-var-angle-left: "\f104"; 33 | @fa-var-angle-right: "\f105"; 34 | @fa-var-angle-up: "\f106"; 35 | @fa-var-apple: "\f179"; 36 | @fa-var-archive: "\f187"; 37 | @fa-var-area-chart: "\f1fe"; 38 | @fa-var-arrow-circle-down: "\f0ab"; 39 | @fa-var-arrow-circle-left: "\f0a8"; 40 | @fa-var-arrow-circle-o-down: "\f01a"; 41 | @fa-var-arrow-circle-o-left: "\f190"; 42 | @fa-var-arrow-circle-o-right: "\f18e"; 43 | @fa-var-arrow-circle-o-up: "\f01b"; 44 | @fa-var-arrow-circle-right: "\f0a9"; 45 | @fa-var-arrow-circle-up: "\f0aa"; 46 | @fa-var-arrow-down: "\f063"; 47 | @fa-var-arrow-left: "\f060"; 48 | @fa-var-arrow-right: "\f061"; 49 | @fa-var-arrow-up: "\f062"; 50 | @fa-var-arrows: "\f047"; 51 | @fa-var-arrows-alt: "\f0b2"; 52 | @fa-var-arrows-h: "\f07e"; 53 | @fa-var-arrows-v: "\f07d"; 54 | @fa-var-asl-interpreting: "\f2a3"; 55 | @fa-var-assistive-listening-systems: "\f2a2"; 56 | @fa-var-asterisk: "\f069"; 57 | @fa-var-at: "\f1fa"; 58 | @fa-var-audio-description: "\f29e"; 59 | @fa-var-automobile: "\f1b9"; 60 | @fa-var-backward: "\f04a"; 61 | @fa-var-balance-scale: "\f24e"; 62 | @fa-var-ban: "\f05e"; 63 | @fa-var-bank: "\f19c"; 64 | @fa-var-bar-chart: "\f080"; 65 | @fa-var-bar-chart-o: "\f080"; 66 | @fa-var-barcode: "\f02a"; 67 | @fa-var-bars: "\f0c9"; 68 | @fa-var-battery-0: "\f244"; 69 | @fa-var-battery-1: "\f243"; 70 | @fa-var-battery-2: "\f242"; 71 | @fa-var-battery-3: "\f241"; 72 | @fa-var-battery-4: "\f240"; 73 | @fa-var-battery-empty: "\f244"; 74 | @fa-var-battery-full: "\f240"; 75 | @fa-var-battery-half: "\f242"; 76 | @fa-var-battery-quarter: "\f243"; 77 | @fa-var-battery-three-quarters: "\f241"; 78 | @fa-var-bed: "\f236"; 79 | @fa-var-beer: "\f0fc"; 80 | @fa-var-behance: "\f1b4"; 81 | @fa-var-behance-square: "\f1b5"; 82 | @fa-var-bell: "\f0f3"; 83 | @fa-var-bell-o: "\f0a2"; 84 | @fa-var-bell-slash: "\f1f6"; 85 | @fa-var-bell-slash-o: "\f1f7"; 86 | @fa-var-bicycle: "\f206"; 87 | @fa-var-binoculars: "\f1e5"; 88 | @fa-var-birthday-cake: "\f1fd"; 89 | @fa-var-bitbucket: "\f171"; 90 | @fa-var-bitbucket-square: "\f172"; 91 | @fa-var-bitcoin: "\f15a"; 92 | @fa-var-black-tie: "\f27e"; 93 | @fa-var-blind: "\f29d"; 94 | @fa-var-bluetooth: "\f293"; 95 | @fa-var-bluetooth-b: "\f294"; 96 | @fa-var-bold: "\f032"; 97 | @fa-var-bolt: "\f0e7"; 98 | @fa-var-bomb: "\f1e2"; 99 | @fa-var-book: "\f02d"; 100 | @fa-var-bookmark: "\f02e"; 101 | @fa-var-bookmark-o: "\f097"; 102 | @fa-var-braille: "\f2a1"; 103 | @fa-var-briefcase: "\f0b1"; 104 | @fa-var-btc: "\f15a"; 105 | @fa-var-bug: "\f188"; 106 | @fa-var-building: "\f1ad"; 107 | @fa-var-building-o: "\f0f7"; 108 | @fa-var-bullhorn: "\f0a1"; 109 | @fa-var-bullseye: "\f140"; 110 | @fa-var-bus: "\f207"; 111 | @fa-var-buysellads: "\f20d"; 112 | @fa-var-cab: "\f1ba"; 113 | @fa-var-calculator: "\f1ec"; 114 | @fa-var-calendar: "\f073"; 115 | @fa-var-calendar-check-o: "\f274"; 116 | @fa-var-calendar-minus-o: "\f272"; 117 | @fa-var-calendar-o: "\f133"; 118 | @fa-var-calendar-plus-o: "\f271"; 119 | @fa-var-calendar-times-o: "\f273"; 120 | @fa-var-camera: "\f030"; 121 | @fa-var-camera-retro: "\f083"; 122 | @fa-var-car: "\f1b9"; 123 | @fa-var-caret-down: "\f0d7"; 124 | @fa-var-caret-left: "\f0d9"; 125 | @fa-var-caret-right: "\f0da"; 126 | @fa-var-caret-square-o-down: "\f150"; 127 | @fa-var-caret-square-o-left: "\f191"; 128 | @fa-var-caret-square-o-right: "\f152"; 129 | @fa-var-caret-square-o-up: "\f151"; 130 | @fa-var-caret-up: "\f0d8"; 131 | @fa-var-cart-arrow-down: "\f218"; 132 | @fa-var-cart-plus: "\f217"; 133 | @fa-var-cc: "\f20a"; 134 | @fa-var-cc-amex: "\f1f3"; 135 | @fa-var-cc-diners-club: "\f24c"; 136 | @fa-var-cc-discover: "\f1f2"; 137 | @fa-var-cc-jcb: "\f24b"; 138 | @fa-var-cc-mastercard: "\f1f1"; 139 | @fa-var-cc-paypal: "\f1f4"; 140 | @fa-var-cc-stripe: "\f1f5"; 141 | @fa-var-cc-visa: "\f1f0"; 142 | @fa-var-certificate: "\f0a3"; 143 | @fa-var-chain: "\f0c1"; 144 | @fa-var-chain-broken: "\f127"; 145 | @fa-var-check: "\f00c"; 146 | @fa-var-check-circle: "\f058"; 147 | @fa-var-check-circle-o: "\f05d"; 148 | @fa-var-check-square: "\f14a"; 149 | @fa-var-check-square-o: "\f046"; 150 | @fa-var-chevron-circle-down: "\f13a"; 151 | @fa-var-chevron-circle-left: "\f137"; 152 | @fa-var-chevron-circle-right: "\f138"; 153 | @fa-var-chevron-circle-up: "\f139"; 154 | @fa-var-chevron-down: "\f078"; 155 | @fa-var-chevron-left: "\f053"; 156 | @fa-var-chevron-right: "\f054"; 157 | @fa-var-chevron-up: "\f077"; 158 | @fa-var-child: "\f1ae"; 159 | @fa-var-chrome: "\f268"; 160 | @fa-var-circle: "\f111"; 161 | @fa-var-circle-o: "\f10c"; 162 | @fa-var-circle-o-notch: "\f1ce"; 163 | @fa-var-circle-thin: "\f1db"; 164 | @fa-var-clipboard: "\f0ea"; 165 | @fa-var-clock-o: "\f017"; 166 | @fa-var-clone: "\f24d"; 167 | @fa-var-close: "\f00d"; 168 | @fa-var-cloud: "\f0c2"; 169 | @fa-var-cloud-download: "\f0ed"; 170 | @fa-var-cloud-upload: "\f0ee"; 171 | @fa-var-cny: "\f157"; 172 | @fa-var-code: "\f121"; 173 | @fa-var-code-fork: "\f126"; 174 | @fa-var-codepen: "\f1cb"; 175 | @fa-var-codiepie: "\f284"; 176 | @fa-var-coffee: "\f0f4"; 177 | @fa-var-cog: "\f013"; 178 | @fa-var-cogs: "\f085"; 179 | @fa-var-columns: "\f0db"; 180 | @fa-var-comment: "\f075"; 181 | @fa-var-comment-o: "\f0e5"; 182 | @fa-var-commenting: "\f27a"; 183 | @fa-var-commenting-o: "\f27b"; 184 | @fa-var-comments: "\f086"; 185 | @fa-var-comments-o: "\f0e6"; 186 | @fa-var-compass: "\f14e"; 187 | @fa-var-compress: "\f066"; 188 | @fa-var-connectdevelop: "\f20e"; 189 | @fa-var-contao: "\f26d"; 190 | @fa-var-copy: "\f0c5"; 191 | @fa-var-copyright: "\f1f9"; 192 | @fa-var-creative-commons: "\f25e"; 193 | @fa-var-credit-card: "\f09d"; 194 | @fa-var-credit-card-alt: "\f283"; 195 | @fa-var-crop: "\f125"; 196 | @fa-var-crosshairs: "\f05b"; 197 | @fa-var-css3: "\f13c"; 198 | @fa-var-cube: "\f1b2"; 199 | @fa-var-cubes: "\f1b3"; 200 | @fa-var-cut: "\f0c4"; 201 | @fa-var-cutlery: "\f0f5"; 202 | @fa-var-dashboard: "\f0e4"; 203 | @fa-var-dashcube: "\f210"; 204 | @fa-var-database: "\f1c0"; 205 | @fa-var-deaf: "\f2a4"; 206 | @fa-var-deafness: "\f2a4"; 207 | @fa-var-dedent: "\f03b"; 208 | @fa-var-delicious: "\f1a5"; 209 | @fa-var-desktop: "\f108"; 210 | @fa-var-deviantart: "\f1bd"; 211 | @fa-var-diamond: "\f219"; 212 | @fa-var-digg: "\f1a6"; 213 | @fa-var-dollar: "\f155"; 214 | @fa-var-dot-circle-o: "\f192"; 215 | @fa-var-download: "\f019"; 216 | @fa-var-dribbble: "\f17d"; 217 | @fa-var-dropbox: "\f16b"; 218 | @fa-var-drupal: "\f1a9"; 219 | @fa-var-edge: "\f282"; 220 | @fa-var-edit: "\f044"; 221 | @fa-var-eject: "\f052"; 222 | @fa-var-ellipsis-h: "\f141"; 223 | @fa-var-ellipsis-v: "\f142"; 224 | @fa-var-empire: "\f1d1"; 225 | @fa-var-envelope: "\f0e0"; 226 | @fa-var-envelope-o: "\f003"; 227 | @fa-var-envelope-square: "\f199"; 228 | @fa-var-envira: "\f299"; 229 | @fa-var-eraser: "\f12d"; 230 | @fa-var-eur: "\f153"; 231 | @fa-var-euro: "\f153"; 232 | @fa-var-exchange: "\f0ec"; 233 | @fa-var-exclamation: "\f12a"; 234 | @fa-var-exclamation-circle: "\f06a"; 235 | @fa-var-exclamation-triangle: "\f071"; 236 | @fa-var-expand: "\f065"; 237 | @fa-var-expeditedssl: "\f23e"; 238 | @fa-var-external-link: "\f08e"; 239 | @fa-var-external-link-square: "\f14c"; 240 | @fa-var-eye: "\f06e"; 241 | @fa-var-eye-slash: "\f070"; 242 | @fa-var-eyedropper: "\f1fb"; 243 | @fa-var-fa: "\f2b4"; 244 | @fa-var-facebook: "\f09a"; 245 | @fa-var-facebook-f: "\f09a"; 246 | @fa-var-facebook-official: "\f230"; 247 | @fa-var-facebook-square: "\f082"; 248 | @fa-var-fast-backward: "\f049"; 249 | @fa-var-fast-forward: "\f050"; 250 | @fa-var-fax: "\f1ac"; 251 | @fa-var-feed: "\f09e"; 252 | @fa-var-female: "\f182"; 253 | @fa-var-fighter-jet: "\f0fb"; 254 | @fa-var-file: "\f15b"; 255 | @fa-var-file-archive-o: "\f1c6"; 256 | @fa-var-file-audio-o: "\f1c7"; 257 | @fa-var-file-code-o: "\f1c9"; 258 | @fa-var-file-excel-o: "\f1c3"; 259 | @fa-var-file-image-o: "\f1c5"; 260 | @fa-var-file-movie-o: "\f1c8"; 261 | @fa-var-file-o: "\f016"; 262 | @fa-var-file-pdf-o: "\f1c1"; 263 | @fa-var-file-photo-o: "\f1c5"; 264 | @fa-var-file-picture-o: "\f1c5"; 265 | @fa-var-file-powerpoint-o: "\f1c4"; 266 | @fa-var-file-sound-o: "\f1c7"; 267 | @fa-var-file-text: "\f15c"; 268 | @fa-var-file-text-o: "\f0f6"; 269 | @fa-var-file-video-o: "\f1c8"; 270 | @fa-var-file-word-o: "\f1c2"; 271 | @fa-var-file-zip-o: "\f1c6"; 272 | @fa-var-files-o: "\f0c5"; 273 | @fa-var-film: "\f008"; 274 | @fa-var-filter: "\f0b0"; 275 | @fa-var-fire: "\f06d"; 276 | @fa-var-fire-extinguisher: "\f134"; 277 | @fa-var-firefox: "\f269"; 278 | @fa-var-first-order: "\f2b0"; 279 | @fa-var-flag: "\f024"; 280 | @fa-var-flag-checkered: "\f11e"; 281 | @fa-var-flag-o: "\f11d"; 282 | @fa-var-flash: "\f0e7"; 283 | @fa-var-flask: "\f0c3"; 284 | @fa-var-flickr: "\f16e"; 285 | @fa-var-floppy-o: "\f0c7"; 286 | @fa-var-folder: "\f07b"; 287 | @fa-var-folder-o: "\f114"; 288 | @fa-var-folder-open: "\f07c"; 289 | @fa-var-folder-open-o: "\f115"; 290 | @fa-var-font: "\f031"; 291 | @fa-var-font-awesome: "\f2b4"; 292 | @fa-var-fonticons: "\f280"; 293 | @fa-var-fort-awesome: "\f286"; 294 | @fa-var-forumbee: "\f211"; 295 | @fa-var-forward: "\f04e"; 296 | @fa-var-foursquare: "\f180"; 297 | @fa-var-frown-o: "\f119"; 298 | @fa-var-futbol-o: "\f1e3"; 299 | @fa-var-gamepad: "\f11b"; 300 | @fa-var-gavel: "\f0e3"; 301 | @fa-var-gbp: "\f154"; 302 | @fa-var-ge: "\f1d1"; 303 | @fa-var-gear: "\f013"; 304 | @fa-var-gears: "\f085"; 305 | @fa-var-genderless: "\f22d"; 306 | @fa-var-get-pocket: "\f265"; 307 | @fa-var-gg: "\f260"; 308 | @fa-var-gg-circle: "\f261"; 309 | @fa-var-gift: "\f06b"; 310 | @fa-var-git: "\f1d3"; 311 | @fa-var-git-square: "\f1d2"; 312 | @fa-var-github: "\f09b"; 313 | @fa-var-github-alt: "\f113"; 314 | @fa-var-github-square: "\f092"; 315 | @fa-var-gitlab: "\f296"; 316 | @fa-var-gittip: "\f184"; 317 | @fa-var-glass: "\f000"; 318 | @fa-var-glide: "\f2a5"; 319 | @fa-var-glide-g: "\f2a6"; 320 | @fa-var-globe: "\f0ac"; 321 | @fa-var-google: "\f1a0"; 322 | @fa-var-google-plus: "\f0d5"; 323 | @fa-var-google-plus-circle: "\f2b3"; 324 | @fa-var-google-plus-official: "\f2b3"; 325 | @fa-var-google-plus-square: "\f0d4"; 326 | @fa-var-google-wallet: "\f1ee"; 327 | @fa-var-graduation-cap: "\f19d"; 328 | @fa-var-gratipay: "\f184"; 329 | @fa-var-group: "\f0c0"; 330 | @fa-var-h-square: "\f0fd"; 331 | @fa-var-hacker-news: "\f1d4"; 332 | @fa-var-hand-grab-o: "\f255"; 333 | @fa-var-hand-lizard-o: "\f258"; 334 | @fa-var-hand-o-down: "\f0a7"; 335 | @fa-var-hand-o-left: "\f0a5"; 336 | @fa-var-hand-o-right: "\f0a4"; 337 | @fa-var-hand-o-up: "\f0a6"; 338 | @fa-var-hand-paper-o: "\f256"; 339 | @fa-var-hand-peace-o: "\f25b"; 340 | @fa-var-hand-pointer-o: "\f25a"; 341 | @fa-var-hand-rock-o: "\f255"; 342 | @fa-var-hand-scissors-o: "\f257"; 343 | @fa-var-hand-spock-o: "\f259"; 344 | @fa-var-hand-stop-o: "\f256"; 345 | @fa-var-hard-of-hearing: "\f2a4"; 346 | @fa-var-hashtag: "\f292"; 347 | @fa-var-hdd-o: "\f0a0"; 348 | @fa-var-header: "\f1dc"; 349 | @fa-var-headphones: "\f025"; 350 | @fa-var-heart: "\f004"; 351 | @fa-var-heart-o: "\f08a"; 352 | @fa-var-heartbeat: "\f21e"; 353 | @fa-var-history: "\f1da"; 354 | @fa-var-home: "\f015"; 355 | @fa-var-hospital-o: "\f0f8"; 356 | @fa-var-hotel: "\f236"; 357 | @fa-var-hourglass: "\f254"; 358 | @fa-var-hourglass-1: "\f251"; 359 | @fa-var-hourglass-2: "\f252"; 360 | @fa-var-hourglass-3: "\f253"; 361 | @fa-var-hourglass-end: "\f253"; 362 | @fa-var-hourglass-half: "\f252"; 363 | @fa-var-hourglass-o: "\f250"; 364 | @fa-var-hourglass-start: "\f251"; 365 | @fa-var-houzz: "\f27c"; 366 | @fa-var-html5: "\f13b"; 367 | @fa-var-i-cursor: "\f246"; 368 | @fa-var-ils: "\f20b"; 369 | @fa-var-image: "\f03e"; 370 | @fa-var-inbox: "\f01c"; 371 | @fa-var-indent: "\f03c"; 372 | @fa-var-industry: "\f275"; 373 | @fa-var-info: "\f129"; 374 | @fa-var-info-circle: "\f05a"; 375 | @fa-var-inr: "\f156"; 376 | @fa-var-instagram: "\f16d"; 377 | @fa-var-institution: "\f19c"; 378 | @fa-var-internet-explorer: "\f26b"; 379 | @fa-var-intersex: "\f224"; 380 | @fa-var-ioxhost: "\f208"; 381 | @fa-var-italic: "\f033"; 382 | @fa-var-joomla: "\f1aa"; 383 | @fa-var-jpy: "\f157"; 384 | @fa-var-jsfiddle: "\f1cc"; 385 | @fa-var-key: "\f084"; 386 | @fa-var-keyboard-o: "\f11c"; 387 | @fa-var-krw: "\f159"; 388 | @fa-var-language: "\f1ab"; 389 | @fa-var-laptop: "\f109"; 390 | @fa-var-lastfm: "\f202"; 391 | @fa-var-lastfm-square: "\f203"; 392 | @fa-var-leaf: "\f06c"; 393 | @fa-var-leanpub: "\f212"; 394 | @fa-var-legal: "\f0e3"; 395 | @fa-var-lemon-o: "\f094"; 396 | @fa-var-level-down: "\f149"; 397 | @fa-var-level-up: "\f148"; 398 | @fa-var-life-bouy: "\f1cd"; 399 | @fa-var-life-buoy: "\f1cd"; 400 | @fa-var-life-ring: "\f1cd"; 401 | @fa-var-life-saver: "\f1cd"; 402 | @fa-var-lightbulb-o: "\f0eb"; 403 | @fa-var-line-chart: "\f201"; 404 | @fa-var-link: "\f0c1"; 405 | @fa-var-linkedin: "\f0e1"; 406 | @fa-var-linkedin-square: "\f08c"; 407 | @fa-var-linux: "\f17c"; 408 | @fa-var-list: "\f03a"; 409 | @fa-var-list-alt: "\f022"; 410 | @fa-var-list-ol: "\f0cb"; 411 | @fa-var-list-ul: "\f0ca"; 412 | @fa-var-location-arrow: "\f124"; 413 | @fa-var-lock: "\f023"; 414 | @fa-var-long-arrow-down: "\f175"; 415 | @fa-var-long-arrow-left: "\f177"; 416 | @fa-var-long-arrow-right: "\f178"; 417 | @fa-var-long-arrow-up: "\f176"; 418 | @fa-var-low-vision: "\f2a8"; 419 | @fa-var-magic: "\f0d0"; 420 | @fa-var-magnet: "\f076"; 421 | @fa-var-mail-forward: "\f064"; 422 | @fa-var-mail-reply: "\f112"; 423 | @fa-var-mail-reply-all: "\f122"; 424 | @fa-var-male: "\f183"; 425 | @fa-var-map: "\f279"; 426 | @fa-var-map-marker: "\f041"; 427 | @fa-var-map-o: "\f278"; 428 | @fa-var-map-pin: "\f276"; 429 | @fa-var-map-signs: "\f277"; 430 | @fa-var-mars: "\f222"; 431 | @fa-var-mars-double: "\f227"; 432 | @fa-var-mars-stroke: "\f229"; 433 | @fa-var-mars-stroke-h: "\f22b"; 434 | @fa-var-mars-stroke-v: "\f22a"; 435 | @fa-var-maxcdn: "\f136"; 436 | @fa-var-meanpath: "\f20c"; 437 | @fa-var-medium: "\f23a"; 438 | @fa-var-medkit: "\f0fa"; 439 | @fa-var-meh-o: "\f11a"; 440 | @fa-var-mercury: "\f223"; 441 | @fa-var-microphone: "\f130"; 442 | @fa-var-microphone-slash: "\f131"; 443 | @fa-var-minus: "\f068"; 444 | @fa-var-minus-circle: "\f056"; 445 | @fa-var-minus-square: "\f146"; 446 | @fa-var-minus-square-o: "\f147"; 447 | @fa-var-mixcloud: "\f289"; 448 | @fa-var-mobile: "\f10b"; 449 | @fa-var-mobile-phone: "\f10b"; 450 | @fa-var-modx: "\f285"; 451 | @fa-var-money: "\f0d6"; 452 | @fa-var-moon-o: "\f186"; 453 | @fa-var-mortar-board: "\f19d"; 454 | @fa-var-motorcycle: "\f21c"; 455 | @fa-var-mouse-pointer: "\f245"; 456 | @fa-var-music: "\f001"; 457 | @fa-var-navicon: "\f0c9"; 458 | @fa-var-neuter: "\f22c"; 459 | @fa-var-newspaper-o: "\f1ea"; 460 | @fa-var-object-group: "\f247"; 461 | @fa-var-object-ungroup: "\f248"; 462 | @fa-var-odnoklassniki: "\f263"; 463 | @fa-var-odnoklassniki-square: "\f264"; 464 | @fa-var-opencart: "\f23d"; 465 | @fa-var-openid: "\f19b"; 466 | @fa-var-opera: "\f26a"; 467 | @fa-var-optin-monster: "\f23c"; 468 | @fa-var-outdent: "\f03b"; 469 | @fa-var-pagelines: "\f18c"; 470 | @fa-var-paint-brush: "\f1fc"; 471 | @fa-var-paper-plane: "\f1d8"; 472 | @fa-var-paper-plane-o: "\f1d9"; 473 | @fa-var-paperclip: "\f0c6"; 474 | @fa-var-paragraph: "\f1dd"; 475 | @fa-var-paste: "\f0ea"; 476 | @fa-var-pause: "\f04c"; 477 | @fa-var-pause-circle: "\f28b"; 478 | @fa-var-pause-circle-o: "\f28c"; 479 | @fa-var-paw: "\f1b0"; 480 | @fa-var-paypal: "\f1ed"; 481 | @fa-var-pencil: "\f040"; 482 | @fa-var-pencil-square: "\f14b"; 483 | @fa-var-pencil-square-o: "\f044"; 484 | @fa-var-percent: "\f295"; 485 | @fa-var-phone: "\f095"; 486 | @fa-var-phone-square: "\f098"; 487 | @fa-var-photo: "\f03e"; 488 | @fa-var-picture-o: "\f03e"; 489 | @fa-var-pie-chart: "\f200"; 490 | @fa-var-pied-piper: "\f2ae"; 491 | @fa-var-pied-piper-alt: "\f1a8"; 492 | @fa-var-pied-piper-pp: "\f1a7"; 493 | @fa-var-pinterest: "\f0d2"; 494 | @fa-var-pinterest-p: "\f231"; 495 | @fa-var-pinterest-square: "\f0d3"; 496 | @fa-var-plane: "\f072"; 497 | @fa-var-play: "\f04b"; 498 | @fa-var-play-circle: "\f144"; 499 | @fa-var-play-circle-o: "\f01d"; 500 | @fa-var-plug: "\f1e6"; 501 | @fa-var-plus: "\f067"; 502 | @fa-var-plus-circle: "\f055"; 503 | @fa-var-plus-square: "\f0fe"; 504 | @fa-var-plus-square-o: "\f196"; 505 | @fa-var-power-off: "\f011"; 506 | @fa-var-print: "\f02f"; 507 | @fa-var-product-hunt: "\f288"; 508 | @fa-var-puzzle-piece: "\f12e"; 509 | @fa-var-qq: "\f1d6"; 510 | @fa-var-qrcode: "\f029"; 511 | @fa-var-question: "\f128"; 512 | @fa-var-question-circle: "\f059"; 513 | @fa-var-question-circle-o: "\f29c"; 514 | @fa-var-quote-left: "\f10d"; 515 | @fa-var-quote-right: "\f10e"; 516 | @fa-var-ra: "\f1d0"; 517 | @fa-var-random: "\f074"; 518 | @fa-var-rebel: "\f1d0"; 519 | @fa-var-recycle: "\f1b8"; 520 | @fa-var-reddit: "\f1a1"; 521 | @fa-var-reddit-alien: "\f281"; 522 | @fa-var-reddit-square: "\f1a2"; 523 | @fa-var-refresh: "\f021"; 524 | @fa-var-registered: "\f25d"; 525 | @fa-var-remove: "\f00d"; 526 | @fa-var-renren: "\f18b"; 527 | @fa-var-reorder: "\f0c9"; 528 | @fa-var-repeat: "\f01e"; 529 | @fa-var-reply: "\f112"; 530 | @fa-var-reply-all: "\f122"; 531 | @fa-var-resistance: "\f1d0"; 532 | @fa-var-retweet: "\f079"; 533 | @fa-var-rmb: "\f157"; 534 | @fa-var-road: "\f018"; 535 | @fa-var-rocket: "\f135"; 536 | @fa-var-rotate-left: "\f0e2"; 537 | @fa-var-rotate-right: "\f01e"; 538 | @fa-var-rouble: "\f158"; 539 | @fa-var-rss: "\f09e"; 540 | @fa-var-rss-square: "\f143"; 541 | @fa-var-rub: "\f158"; 542 | @fa-var-ruble: "\f158"; 543 | @fa-var-rupee: "\f156"; 544 | @fa-var-safari: "\f267"; 545 | @fa-var-save: "\f0c7"; 546 | @fa-var-scissors: "\f0c4"; 547 | @fa-var-scribd: "\f28a"; 548 | @fa-var-search: "\f002"; 549 | @fa-var-search-minus: "\f010"; 550 | @fa-var-search-plus: "\f00e"; 551 | @fa-var-sellsy: "\f213"; 552 | @fa-var-send: "\f1d8"; 553 | @fa-var-send-o: "\f1d9"; 554 | @fa-var-server: "\f233"; 555 | @fa-var-share: "\f064"; 556 | @fa-var-share-alt: "\f1e0"; 557 | @fa-var-share-alt-square: "\f1e1"; 558 | @fa-var-share-square: "\f14d"; 559 | @fa-var-share-square-o: "\f045"; 560 | @fa-var-shekel: "\f20b"; 561 | @fa-var-sheqel: "\f20b"; 562 | @fa-var-shield: "\f132"; 563 | @fa-var-ship: "\f21a"; 564 | @fa-var-shirtsinbulk: "\f214"; 565 | @fa-var-shopping-bag: "\f290"; 566 | @fa-var-shopping-basket: "\f291"; 567 | @fa-var-shopping-cart: "\f07a"; 568 | @fa-var-sign-in: "\f090"; 569 | @fa-var-sign-language: "\f2a7"; 570 | @fa-var-sign-out: "\f08b"; 571 | @fa-var-signal: "\f012"; 572 | @fa-var-signing: "\f2a7"; 573 | @fa-var-simplybuilt: "\f215"; 574 | @fa-var-sitemap: "\f0e8"; 575 | @fa-var-skyatlas: "\f216"; 576 | @fa-var-skype: "\f17e"; 577 | @fa-var-slack: "\f198"; 578 | @fa-var-sliders: "\f1de"; 579 | @fa-var-slideshare: "\f1e7"; 580 | @fa-var-smile-o: "\f118"; 581 | @fa-var-snapchat: "\f2ab"; 582 | @fa-var-snapchat-ghost: "\f2ac"; 583 | @fa-var-snapchat-square: "\f2ad"; 584 | @fa-var-soccer-ball-o: "\f1e3"; 585 | @fa-var-sort: "\f0dc"; 586 | @fa-var-sort-alpha-asc: "\f15d"; 587 | @fa-var-sort-alpha-desc: "\f15e"; 588 | @fa-var-sort-amount-asc: "\f160"; 589 | @fa-var-sort-amount-desc: "\f161"; 590 | @fa-var-sort-asc: "\f0de"; 591 | @fa-var-sort-desc: "\f0dd"; 592 | @fa-var-sort-down: "\f0dd"; 593 | @fa-var-sort-numeric-asc: "\f162"; 594 | @fa-var-sort-numeric-desc: "\f163"; 595 | @fa-var-sort-up: "\f0de"; 596 | @fa-var-soundcloud: "\f1be"; 597 | @fa-var-space-shuttle: "\f197"; 598 | @fa-var-spinner: "\f110"; 599 | @fa-var-spoon: "\f1b1"; 600 | @fa-var-spotify: "\f1bc"; 601 | @fa-var-square: "\f0c8"; 602 | @fa-var-square-o: "\f096"; 603 | @fa-var-stack-exchange: "\f18d"; 604 | @fa-var-stack-overflow: "\f16c"; 605 | @fa-var-star: "\f005"; 606 | @fa-var-star-half: "\f089"; 607 | @fa-var-star-half-empty: "\f123"; 608 | @fa-var-star-half-full: "\f123"; 609 | @fa-var-star-half-o: "\f123"; 610 | @fa-var-star-o: "\f006"; 611 | @fa-var-steam: "\f1b6"; 612 | @fa-var-steam-square: "\f1b7"; 613 | @fa-var-step-backward: "\f048"; 614 | @fa-var-step-forward: "\f051"; 615 | @fa-var-stethoscope: "\f0f1"; 616 | @fa-var-sticky-note: "\f249"; 617 | @fa-var-sticky-note-o: "\f24a"; 618 | @fa-var-stop: "\f04d"; 619 | @fa-var-stop-circle: "\f28d"; 620 | @fa-var-stop-circle-o: "\f28e"; 621 | @fa-var-street-view: "\f21d"; 622 | @fa-var-strikethrough: "\f0cc"; 623 | @fa-var-stumbleupon: "\f1a4"; 624 | @fa-var-stumbleupon-circle: "\f1a3"; 625 | @fa-var-subscript: "\f12c"; 626 | @fa-var-subway: "\f239"; 627 | @fa-var-suitcase: "\f0f2"; 628 | @fa-var-sun-o: "\f185"; 629 | @fa-var-superscript: "\f12b"; 630 | @fa-var-support: "\f1cd"; 631 | @fa-var-table: "\f0ce"; 632 | @fa-var-tablet: "\f10a"; 633 | @fa-var-tachometer: "\f0e4"; 634 | @fa-var-tag: "\f02b"; 635 | @fa-var-tags: "\f02c"; 636 | @fa-var-tasks: "\f0ae"; 637 | @fa-var-taxi: "\f1ba"; 638 | @fa-var-television: "\f26c"; 639 | @fa-var-tencent-weibo: "\f1d5"; 640 | @fa-var-terminal: "\f120"; 641 | @fa-var-text-height: "\f034"; 642 | @fa-var-text-width: "\f035"; 643 | @fa-var-th: "\f00a"; 644 | @fa-var-th-large: "\f009"; 645 | @fa-var-th-list: "\f00b"; 646 | @fa-var-themeisle: "\f2b2"; 647 | @fa-var-thumb-tack: "\f08d"; 648 | @fa-var-thumbs-down: "\f165"; 649 | @fa-var-thumbs-o-down: "\f088"; 650 | @fa-var-thumbs-o-up: "\f087"; 651 | @fa-var-thumbs-up: "\f164"; 652 | @fa-var-ticket: "\f145"; 653 | @fa-var-times: "\f00d"; 654 | @fa-var-times-circle: "\f057"; 655 | @fa-var-times-circle-o: "\f05c"; 656 | @fa-var-tint: "\f043"; 657 | @fa-var-toggle-down: "\f150"; 658 | @fa-var-toggle-left: "\f191"; 659 | @fa-var-toggle-off: "\f204"; 660 | @fa-var-toggle-on: "\f205"; 661 | @fa-var-toggle-right: "\f152"; 662 | @fa-var-toggle-up: "\f151"; 663 | @fa-var-trademark: "\f25c"; 664 | @fa-var-train: "\f238"; 665 | @fa-var-transgender: "\f224"; 666 | @fa-var-transgender-alt: "\f225"; 667 | @fa-var-trash: "\f1f8"; 668 | @fa-var-trash-o: "\f014"; 669 | @fa-var-tree: "\f1bb"; 670 | @fa-var-trello: "\f181"; 671 | @fa-var-tripadvisor: "\f262"; 672 | @fa-var-trophy: "\f091"; 673 | @fa-var-truck: "\f0d1"; 674 | @fa-var-try: "\f195"; 675 | @fa-var-tty: "\f1e4"; 676 | @fa-var-tumblr: "\f173"; 677 | @fa-var-tumblr-square: "\f174"; 678 | @fa-var-turkish-lira: "\f195"; 679 | @fa-var-tv: "\f26c"; 680 | @fa-var-twitch: "\f1e8"; 681 | @fa-var-twitter: "\f099"; 682 | @fa-var-twitter-square: "\f081"; 683 | @fa-var-umbrella: "\f0e9"; 684 | @fa-var-underline: "\f0cd"; 685 | @fa-var-undo: "\f0e2"; 686 | @fa-var-universal-access: "\f29a"; 687 | @fa-var-university: "\f19c"; 688 | @fa-var-unlink: "\f127"; 689 | @fa-var-unlock: "\f09c"; 690 | @fa-var-unlock-alt: "\f13e"; 691 | @fa-var-unsorted: "\f0dc"; 692 | @fa-var-upload: "\f093"; 693 | @fa-var-usb: "\f287"; 694 | @fa-var-usd: "\f155"; 695 | @fa-var-user: "\f007"; 696 | @fa-var-user-md: "\f0f0"; 697 | @fa-var-user-plus: "\f234"; 698 | @fa-var-user-secret: "\f21b"; 699 | @fa-var-user-times: "\f235"; 700 | @fa-var-users: "\f0c0"; 701 | @fa-var-venus: "\f221"; 702 | @fa-var-venus-double: "\f226"; 703 | @fa-var-venus-mars: "\f228"; 704 | @fa-var-viacoin: "\f237"; 705 | @fa-var-viadeo: "\f2a9"; 706 | @fa-var-viadeo-square: "\f2aa"; 707 | @fa-var-video-camera: "\f03d"; 708 | @fa-var-vimeo: "\f27d"; 709 | @fa-var-vimeo-square: "\f194"; 710 | @fa-var-vine: "\f1ca"; 711 | @fa-var-vk: "\f189"; 712 | @fa-var-volume-control-phone: "\f2a0"; 713 | @fa-var-volume-down: "\f027"; 714 | @fa-var-volume-off: "\f026"; 715 | @fa-var-volume-up: "\f028"; 716 | @fa-var-warning: "\f071"; 717 | @fa-var-wechat: "\f1d7"; 718 | @fa-var-weibo: "\f18a"; 719 | @fa-var-weixin: "\f1d7"; 720 | @fa-var-whatsapp: "\f232"; 721 | @fa-var-wheelchair: "\f193"; 722 | @fa-var-wheelchair-alt: "\f29b"; 723 | @fa-var-wifi: "\f1eb"; 724 | @fa-var-wikipedia-w: "\f266"; 725 | @fa-var-windows: "\f17a"; 726 | @fa-var-won: "\f159"; 727 | @fa-var-wordpress: "\f19a"; 728 | @fa-var-wpbeginner: "\f297"; 729 | @fa-var-wpforms: "\f298"; 730 | @fa-var-wrench: "\f0ad"; 731 | @fa-var-xing: "\f168"; 732 | @fa-var-xing-square: "\f169"; 733 | @fa-var-y-combinator: "\f23b"; 734 | @fa-var-y-combinator-square: "\f1d4"; 735 | @fa-var-yahoo: "\f19e"; 736 | @fa-var-yc: "\f23b"; 737 | @fa-var-yc-square: "\f1d4"; 738 | @fa-var-yelp: "\f1e9"; 739 | @fa-var-yen: "\f157"; 740 | @fa-var-yoast: "\f2b1"; 741 | @fa-var-youtube: "\f167"; 742 | @fa-var-youtube-play: "\f16a"; 743 | @fa-var-youtube-square: "\f166"; 744 | 745 | -------------------------------------------------------------------------------- /src/main/webapp/vendors/font-awesome-4.6.3/scss/_variables.scss: -------------------------------------------------------------------------------- 1 | // Variables 2 | // -------------------------- 3 | 4 | $fa-font-path: "../fonts" !default; 5 | $fa-font-size-base: 14px !default; 6 | $fa-line-height-base: 1 !default; 7 | //$fa-font-path: "//netdna.bootstrapcdn.com/font-awesome/4.6.3/fonts" !default; // for referencing Bootstrap CDN font files directly 8 | $fa-css-prefix: fa !default; 9 | $fa-version: "4.6.3" !default; 10 | $fa-border-color: #eee !default; 11 | $fa-inverse: #fff !default; 12 | $fa-li-width: (30em / 14) !default; 13 | 14 | $fa-var-500px: "\f26e"; 15 | $fa-var-adjust: "\f042"; 16 | $fa-var-adn: "\f170"; 17 | $fa-var-align-center: "\f037"; 18 | $fa-var-align-justify: "\f039"; 19 | $fa-var-align-left: "\f036"; 20 | $fa-var-align-right: "\f038"; 21 | $fa-var-amazon: "\f270"; 22 | $fa-var-ambulance: "\f0f9"; 23 | $fa-var-american-sign-language-interpreting: "\f2a3"; 24 | $fa-var-anchor: "\f13d"; 25 | $fa-var-android: "\f17b"; 26 | $fa-var-angellist: "\f209"; 27 | $fa-var-angle-double-down: "\f103"; 28 | $fa-var-angle-double-left: "\f100"; 29 | $fa-var-angle-double-right: "\f101"; 30 | $fa-var-angle-double-up: "\f102"; 31 | $fa-var-angle-down: "\f107"; 32 | $fa-var-angle-left: "\f104"; 33 | $fa-var-angle-right: "\f105"; 34 | $fa-var-angle-up: "\f106"; 35 | $fa-var-apple: "\f179"; 36 | $fa-var-archive: "\f187"; 37 | $fa-var-area-chart: "\f1fe"; 38 | $fa-var-arrow-circle-down: "\f0ab"; 39 | $fa-var-arrow-circle-left: "\f0a8"; 40 | $fa-var-arrow-circle-o-down: "\f01a"; 41 | $fa-var-arrow-circle-o-left: "\f190"; 42 | $fa-var-arrow-circle-o-right: "\f18e"; 43 | $fa-var-arrow-circle-o-up: "\f01b"; 44 | $fa-var-arrow-circle-right: "\f0a9"; 45 | $fa-var-arrow-circle-up: "\f0aa"; 46 | $fa-var-arrow-down: "\f063"; 47 | $fa-var-arrow-left: "\f060"; 48 | $fa-var-arrow-right: "\f061"; 49 | $fa-var-arrow-up: "\f062"; 50 | $fa-var-arrows: "\f047"; 51 | $fa-var-arrows-alt: "\f0b2"; 52 | $fa-var-arrows-h: "\f07e"; 53 | $fa-var-arrows-v: "\f07d"; 54 | $fa-var-asl-interpreting: "\f2a3"; 55 | $fa-var-assistive-listening-systems: "\f2a2"; 56 | $fa-var-asterisk: "\f069"; 57 | $fa-var-at: "\f1fa"; 58 | $fa-var-audio-description: "\f29e"; 59 | $fa-var-automobile: "\f1b9"; 60 | $fa-var-backward: "\f04a"; 61 | $fa-var-balance-scale: "\f24e"; 62 | $fa-var-ban: "\f05e"; 63 | $fa-var-bank: "\f19c"; 64 | $fa-var-bar-chart: "\f080"; 65 | $fa-var-bar-chart-o: "\f080"; 66 | $fa-var-barcode: "\f02a"; 67 | $fa-var-bars: "\f0c9"; 68 | $fa-var-battery-0: "\f244"; 69 | $fa-var-battery-1: "\f243"; 70 | $fa-var-battery-2: "\f242"; 71 | $fa-var-battery-3: "\f241"; 72 | $fa-var-battery-4: "\f240"; 73 | $fa-var-battery-empty: "\f244"; 74 | $fa-var-battery-full: "\f240"; 75 | $fa-var-battery-half: "\f242"; 76 | $fa-var-battery-quarter: "\f243"; 77 | $fa-var-battery-three-quarters: "\f241"; 78 | $fa-var-bed: "\f236"; 79 | $fa-var-beer: "\f0fc"; 80 | $fa-var-behance: "\f1b4"; 81 | $fa-var-behance-square: "\f1b5"; 82 | $fa-var-bell: "\f0f3"; 83 | $fa-var-bell-o: "\f0a2"; 84 | $fa-var-bell-slash: "\f1f6"; 85 | $fa-var-bell-slash-o: "\f1f7"; 86 | $fa-var-bicycle: "\f206"; 87 | $fa-var-binoculars: "\f1e5"; 88 | $fa-var-birthday-cake: "\f1fd"; 89 | $fa-var-bitbucket: "\f171"; 90 | $fa-var-bitbucket-square: "\f172"; 91 | $fa-var-bitcoin: "\f15a"; 92 | $fa-var-black-tie: "\f27e"; 93 | $fa-var-blind: "\f29d"; 94 | $fa-var-bluetooth: "\f293"; 95 | $fa-var-bluetooth-b: "\f294"; 96 | $fa-var-bold: "\f032"; 97 | $fa-var-bolt: "\f0e7"; 98 | $fa-var-bomb: "\f1e2"; 99 | $fa-var-book: "\f02d"; 100 | $fa-var-bookmark: "\f02e"; 101 | $fa-var-bookmark-o: "\f097"; 102 | $fa-var-braille: "\f2a1"; 103 | $fa-var-briefcase: "\f0b1"; 104 | $fa-var-btc: "\f15a"; 105 | $fa-var-bug: "\f188"; 106 | $fa-var-building: "\f1ad"; 107 | $fa-var-building-o: "\f0f7"; 108 | $fa-var-bullhorn: "\f0a1"; 109 | $fa-var-bullseye: "\f140"; 110 | $fa-var-bus: "\f207"; 111 | $fa-var-buysellads: "\f20d"; 112 | $fa-var-cab: "\f1ba"; 113 | $fa-var-calculator: "\f1ec"; 114 | $fa-var-calendar: "\f073"; 115 | $fa-var-calendar-check-o: "\f274"; 116 | $fa-var-calendar-minus-o: "\f272"; 117 | $fa-var-calendar-o: "\f133"; 118 | $fa-var-calendar-plus-o: "\f271"; 119 | $fa-var-calendar-times-o: "\f273"; 120 | $fa-var-camera: "\f030"; 121 | $fa-var-camera-retro: "\f083"; 122 | $fa-var-car: "\f1b9"; 123 | $fa-var-caret-down: "\f0d7"; 124 | $fa-var-caret-left: "\f0d9"; 125 | $fa-var-caret-right: "\f0da"; 126 | $fa-var-caret-square-o-down: "\f150"; 127 | $fa-var-caret-square-o-left: "\f191"; 128 | $fa-var-caret-square-o-right: "\f152"; 129 | $fa-var-caret-square-o-up: "\f151"; 130 | $fa-var-caret-up: "\f0d8"; 131 | $fa-var-cart-arrow-down: "\f218"; 132 | $fa-var-cart-plus: "\f217"; 133 | $fa-var-cc: "\f20a"; 134 | $fa-var-cc-amex: "\f1f3"; 135 | $fa-var-cc-diners-club: "\f24c"; 136 | $fa-var-cc-discover: "\f1f2"; 137 | $fa-var-cc-jcb: "\f24b"; 138 | $fa-var-cc-mastercard: "\f1f1"; 139 | $fa-var-cc-paypal: "\f1f4"; 140 | $fa-var-cc-stripe: "\f1f5"; 141 | $fa-var-cc-visa: "\f1f0"; 142 | $fa-var-certificate: "\f0a3"; 143 | $fa-var-chain: "\f0c1"; 144 | $fa-var-chain-broken: "\f127"; 145 | $fa-var-check: "\f00c"; 146 | $fa-var-check-circle: "\f058"; 147 | $fa-var-check-circle-o: "\f05d"; 148 | $fa-var-check-square: "\f14a"; 149 | $fa-var-check-square-o: "\f046"; 150 | $fa-var-chevron-circle-down: "\f13a"; 151 | $fa-var-chevron-circle-left: "\f137"; 152 | $fa-var-chevron-circle-right: "\f138"; 153 | $fa-var-chevron-circle-up: "\f139"; 154 | $fa-var-chevron-down: "\f078"; 155 | $fa-var-chevron-left: "\f053"; 156 | $fa-var-chevron-right: "\f054"; 157 | $fa-var-chevron-up: "\f077"; 158 | $fa-var-child: "\f1ae"; 159 | $fa-var-chrome: "\f268"; 160 | $fa-var-circle: "\f111"; 161 | $fa-var-circle-o: "\f10c"; 162 | $fa-var-circle-o-notch: "\f1ce"; 163 | $fa-var-circle-thin: "\f1db"; 164 | $fa-var-clipboard: "\f0ea"; 165 | $fa-var-clock-o: "\f017"; 166 | $fa-var-clone: "\f24d"; 167 | $fa-var-close: "\f00d"; 168 | $fa-var-cloud: "\f0c2"; 169 | $fa-var-cloud-download: "\f0ed"; 170 | $fa-var-cloud-upload: "\f0ee"; 171 | $fa-var-cny: "\f157"; 172 | $fa-var-code: "\f121"; 173 | $fa-var-code-fork: "\f126"; 174 | $fa-var-codepen: "\f1cb"; 175 | $fa-var-codiepie: "\f284"; 176 | $fa-var-coffee: "\f0f4"; 177 | $fa-var-cog: "\f013"; 178 | $fa-var-cogs: "\f085"; 179 | $fa-var-columns: "\f0db"; 180 | $fa-var-comment: "\f075"; 181 | $fa-var-comment-o: "\f0e5"; 182 | $fa-var-commenting: "\f27a"; 183 | $fa-var-commenting-o: "\f27b"; 184 | $fa-var-comments: "\f086"; 185 | $fa-var-comments-o: "\f0e6"; 186 | $fa-var-compass: "\f14e"; 187 | $fa-var-compress: "\f066"; 188 | $fa-var-connectdevelop: "\f20e"; 189 | $fa-var-contao: "\f26d"; 190 | $fa-var-copy: "\f0c5"; 191 | $fa-var-copyright: "\f1f9"; 192 | $fa-var-creative-commons: "\f25e"; 193 | $fa-var-credit-card: "\f09d"; 194 | $fa-var-credit-card-alt: "\f283"; 195 | $fa-var-crop: "\f125"; 196 | $fa-var-crosshairs: "\f05b"; 197 | $fa-var-css3: "\f13c"; 198 | $fa-var-cube: "\f1b2"; 199 | $fa-var-cubes: "\f1b3"; 200 | $fa-var-cut: "\f0c4"; 201 | $fa-var-cutlery: "\f0f5"; 202 | $fa-var-dashboard: "\f0e4"; 203 | $fa-var-dashcube: "\f210"; 204 | $fa-var-database: "\f1c0"; 205 | $fa-var-deaf: "\f2a4"; 206 | $fa-var-deafness: "\f2a4"; 207 | $fa-var-dedent: "\f03b"; 208 | $fa-var-delicious: "\f1a5"; 209 | $fa-var-desktop: "\f108"; 210 | $fa-var-deviantart: "\f1bd"; 211 | $fa-var-diamond: "\f219"; 212 | $fa-var-digg: "\f1a6"; 213 | $fa-var-dollar: "\f155"; 214 | $fa-var-dot-circle-o: "\f192"; 215 | $fa-var-download: "\f019"; 216 | $fa-var-dribbble: "\f17d"; 217 | $fa-var-dropbox: "\f16b"; 218 | $fa-var-drupal: "\f1a9"; 219 | $fa-var-edge: "\f282"; 220 | $fa-var-edit: "\f044"; 221 | $fa-var-eject: "\f052"; 222 | $fa-var-ellipsis-h: "\f141"; 223 | $fa-var-ellipsis-v: "\f142"; 224 | $fa-var-empire: "\f1d1"; 225 | $fa-var-envelope: "\f0e0"; 226 | $fa-var-envelope-o: "\f003"; 227 | $fa-var-envelope-square: "\f199"; 228 | $fa-var-envira: "\f299"; 229 | $fa-var-eraser: "\f12d"; 230 | $fa-var-eur: "\f153"; 231 | $fa-var-euro: "\f153"; 232 | $fa-var-exchange: "\f0ec"; 233 | $fa-var-exclamation: "\f12a"; 234 | $fa-var-exclamation-circle: "\f06a"; 235 | $fa-var-exclamation-triangle: "\f071"; 236 | $fa-var-expand: "\f065"; 237 | $fa-var-expeditedssl: "\f23e"; 238 | $fa-var-external-link: "\f08e"; 239 | $fa-var-external-link-square: "\f14c"; 240 | $fa-var-eye: "\f06e"; 241 | $fa-var-eye-slash: "\f070"; 242 | $fa-var-eyedropper: "\f1fb"; 243 | $fa-var-fa: "\f2b4"; 244 | $fa-var-facebook: "\f09a"; 245 | $fa-var-facebook-f: "\f09a"; 246 | $fa-var-facebook-official: "\f230"; 247 | $fa-var-facebook-square: "\f082"; 248 | $fa-var-fast-backward: "\f049"; 249 | $fa-var-fast-forward: "\f050"; 250 | $fa-var-fax: "\f1ac"; 251 | $fa-var-feed: "\f09e"; 252 | $fa-var-female: "\f182"; 253 | $fa-var-fighter-jet: "\f0fb"; 254 | $fa-var-file: "\f15b"; 255 | $fa-var-file-archive-o: "\f1c6"; 256 | $fa-var-file-audio-o: "\f1c7"; 257 | $fa-var-file-code-o: "\f1c9"; 258 | $fa-var-file-excel-o: "\f1c3"; 259 | $fa-var-file-image-o: "\f1c5"; 260 | $fa-var-file-movie-o: "\f1c8"; 261 | $fa-var-file-o: "\f016"; 262 | $fa-var-file-pdf-o: "\f1c1"; 263 | $fa-var-file-photo-o: "\f1c5"; 264 | $fa-var-file-picture-o: "\f1c5"; 265 | $fa-var-file-powerpoint-o: "\f1c4"; 266 | $fa-var-file-sound-o: "\f1c7"; 267 | $fa-var-file-text: "\f15c"; 268 | $fa-var-file-text-o: "\f0f6"; 269 | $fa-var-file-video-o: "\f1c8"; 270 | $fa-var-file-word-o: "\f1c2"; 271 | $fa-var-file-zip-o: "\f1c6"; 272 | $fa-var-files-o: "\f0c5"; 273 | $fa-var-film: "\f008"; 274 | $fa-var-filter: "\f0b0"; 275 | $fa-var-fire: "\f06d"; 276 | $fa-var-fire-extinguisher: "\f134"; 277 | $fa-var-firefox: "\f269"; 278 | $fa-var-first-order: "\f2b0"; 279 | $fa-var-flag: "\f024"; 280 | $fa-var-flag-checkered: "\f11e"; 281 | $fa-var-flag-o: "\f11d"; 282 | $fa-var-flash: "\f0e7"; 283 | $fa-var-flask: "\f0c3"; 284 | $fa-var-flickr: "\f16e"; 285 | $fa-var-floppy-o: "\f0c7"; 286 | $fa-var-folder: "\f07b"; 287 | $fa-var-folder-o: "\f114"; 288 | $fa-var-folder-open: "\f07c"; 289 | $fa-var-folder-open-o: "\f115"; 290 | $fa-var-font: "\f031"; 291 | $fa-var-font-awesome: "\f2b4"; 292 | $fa-var-fonticons: "\f280"; 293 | $fa-var-fort-awesome: "\f286"; 294 | $fa-var-forumbee: "\f211"; 295 | $fa-var-forward: "\f04e"; 296 | $fa-var-foursquare: "\f180"; 297 | $fa-var-frown-o: "\f119"; 298 | $fa-var-futbol-o: "\f1e3"; 299 | $fa-var-gamepad: "\f11b"; 300 | $fa-var-gavel: "\f0e3"; 301 | $fa-var-gbp: "\f154"; 302 | $fa-var-ge: "\f1d1"; 303 | $fa-var-gear: "\f013"; 304 | $fa-var-gears: "\f085"; 305 | $fa-var-genderless: "\f22d"; 306 | $fa-var-get-pocket: "\f265"; 307 | $fa-var-gg: "\f260"; 308 | $fa-var-gg-circle: "\f261"; 309 | $fa-var-gift: "\f06b"; 310 | $fa-var-git: "\f1d3"; 311 | $fa-var-git-square: "\f1d2"; 312 | $fa-var-github: "\f09b"; 313 | $fa-var-github-alt: "\f113"; 314 | $fa-var-github-square: "\f092"; 315 | $fa-var-gitlab: "\f296"; 316 | $fa-var-gittip: "\f184"; 317 | $fa-var-glass: "\f000"; 318 | $fa-var-glide: "\f2a5"; 319 | $fa-var-glide-g: "\f2a6"; 320 | $fa-var-globe: "\f0ac"; 321 | $fa-var-google: "\f1a0"; 322 | $fa-var-google-plus: "\f0d5"; 323 | $fa-var-google-plus-circle: "\f2b3"; 324 | $fa-var-google-plus-official: "\f2b3"; 325 | $fa-var-google-plus-square: "\f0d4"; 326 | $fa-var-google-wallet: "\f1ee"; 327 | $fa-var-graduation-cap: "\f19d"; 328 | $fa-var-gratipay: "\f184"; 329 | $fa-var-group: "\f0c0"; 330 | $fa-var-h-square: "\f0fd"; 331 | $fa-var-hacker-news: "\f1d4"; 332 | $fa-var-hand-grab-o: "\f255"; 333 | $fa-var-hand-lizard-o: "\f258"; 334 | $fa-var-hand-o-down: "\f0a7"; 335 | $fa-var-hand-o-left: "\f0a5"; 336 | $fa-var-hand-o-right: "\f0a4"; 337 | $fa-var-hand-o-up: "\f0a6"; 338 | $fa-var-hand-paper-o: "\f256"; 339 | $fa-var-hand-peace-o: "\f25b"; 340 | $fa-var-hand-pointer-o: "\f25a"; 341 | $fa-var-hand-rock-o: "\f255"; 342 | $fa-var-hand-scissors-o: "\f257"; 343 | $fa-var-hand-spock-o: "\f259"; 344 | $fa-var-hand-stop-o: "\f256"; 345 | $fa-var-hard-of-hearing: "\f2a4"; 346 | $fa-var-hashtag: "\f292"; 347 | $fa-var-hdd-o: "\f0a0"; 348 | $fa-var-header: "\f1dc"; 349 | $fa-var-headphones: "\f025"; 350 | $fa-var-heart: "\f004"; 351 | $fa-var-heart-o: "\f08a"; 352 | $fa-var-heartbeat: "\f21e"; 353 | $fa-var-history: "\f1da"; 354 | $fa-var-home: "\f015"; 355 | $fa-var-hospital-o: "\f0f8"; 356 | $fa-var-hotel: "\f236"; 357 | $fa-var-hourglass: "\f254"; 358 | $fa-var-hourglass-1: "\f251"; 359 | $fa-var-hourglass-2: "\f252"; 360 | $fa-var-hourglass-3: "\f253"; 361 | $fa-var-hourglass-end: "\f253"; 362 | $fa-var-hourglass-half: "\f252"; 363 | $fa-var-hourglass-o: "\f250"; 364 | $fa-var-hourglass-start: "\f251"; 365 | $fa-var-houzz: "\f27c"; 366 | $fa-var-html5: "\f13b"; 367 | $fa-var-i-cursor: "\f246"; 368 | $fa-var-ils: "\f20b"; 369 | $fa-var-image: "\f03e"; 370 | $fa-var-inbox: "\f01c"; 371 | $fa-var-indent: "\f03c"; 372 | $fa-var-industry: "\f275"; 373 | $fa-var-info: "\f129"; 374 | $fa-var-info-circle: "\f05a"; 375 | $fa-var-inr: "\f156"; 376 | $fa-var-instagram: "\f16d"; 377 | $fa-var-institution: "\f19c"; 378 | $fa-var-internet-explorer: "\f26b"; 379 | $fa-var-intersex: "\f224"; 380 | $fa-var-ioxhost: "\f208"; 381 | $fa-var-italic: "\f033"; 382 | $fa-var-joomla: "\f1aa"; 383 | $fa-var-jpy: "\f157"; 384 | $fa-var-jsfiddle: "\f1cc"; 385 | $fa-var-key: "\f084"; 386 | $fa-var-keyboard-o: "\f11c"; 387 | $fa-var-krw: "\f159"; 388 | $fa-var-language: "\f1ab"; 389 | $fa-var-laptop: "\f109"; 390 | $fa-var-lastfm: "\f202"; 391 | $fa-var-lastfm-square: "\f203"; 392 | $fa-var-leaf: "\f06c"; 393 | $fa-var-leanpub: "\f212"; 394 | $fa-var-legal: "\f0e3"; 395 | $fa-var-lemon-o: "\f094"; 396 | $fa-var-level-down: "\f149"; 397 | $fa-var-level-up: "\f148"; 398 | $fa-var-life-bouy: "\f1cd"; 399 | $fa-var-life-buoy: "\f1cd"; 400 | $fa-var-life-ring: "\f1cd"; 401 | $fa-var-life-saver: "\f1cd"; 402 | $fa-var-lightbulb-o: "\f0eb"; 403 | $fa-var-line-chart: "\f201"; 404 | $fa-var-link: "\f0c1"; 405 | $fa-var-linkedin: "\f0e1"; 406 | $fa-var-linkedin-square: "\f08c"; 407 | $fa-var-linux: "\f17c"; 408 | $fa-var-list: "\f03a"; 409 | $fa-var-list-alt: "\f022"; 410 | $fa-var-list-ol: "\f0cb"; 411 | $fa-var-list-ul: "\f0ca"; 412 | $fa-var-location-arrow: "\f124"; 413 | $fa-var-lock: "\f023"; 414 | $fa-var-long-arrow-down: "\f175"; 415 | $fa-var-long-arrow-left: "\f177"; 416 | $fa-var-long-arrow-right: "\f178"; 417 | $fa-var-long-arrow-up: "\f176"; 418 | $fa-var-low-vision: "\f2a8"; 419 | $fa-var-magic: "\f0d0"; 420 | $fa-var-magnet: "\f076"; 421 | $fa-var-mail-forward: "\f064"; 422 | $fa-var-mail-reply: "\f112"; 423 | $fa-var-mail-reply-all: "\f122"; 424 | $fa-var-male: "\f183"; 425 | $fa-var-map: "\f279"; 426 | $fa-var-map-marker: "\f041"; 427 | $fa-var-map-o: "\f278"; 428 | $fa-var-map-pin: "\f276"; 429 | $fa-var-map-signs: "\f277"; 430 | $fa-var-mars: "\f222"; 431 | $fa-var-mars-double: "\f227"; 432 | $fa-var-mars-stroke: "\f229"; 433 | $fa-var-mars-stroke-h: "\f22b"; 434 | $fa-var-mars-stroke-v: "\f22a"; 435 | $fa-var-maxcdn: "\f136"; 436 | $fa-var-meanpath: "\f20c"; 437 | $fa-var-medium: "\f23a"; 438 | $fa-var-medkit: "\f0fa"; 439 | $fa-var-meh-o: "\f11a"; 440 | $fa-var-mercury: "\f223"; 441 | $fa-var-microphone: "\f130"; 442 | $fa-var-microphone-slash: "\f131"; 443 | $fa-var-minus: "\f068"; 444 | $fa-var-minus-circle: "\f056"; 445 | $fa-var-minus-square: "\f146"; 446 | $fa-var-minus-square-o: "\f147"; 447 | $fa-var-mixcloud: "\f289"; 448 | $fa-var-mobile: "\f10b"; 449 | $fa-var-mobile-phone: "\f10b"; 450 | $fa-var-modx: "\f285"; 451 | $fa-var-money: "\f0d6"; 452 | $fa-var-moon-o: "\f186"; 453 | $fa-var-mortar-board: "\f19d"; 454 | $fa-var-motorcycle: "\f21c"; 455 | $fa-var-mouse-pointer: "\f245"; 456 | $fa-var-music: "\f001"; 457 | $fa-var-navicon: "\f0c9"; 458 | $fa-var-neuter: "\f22c"; 459 | $fa-var-newspaper-o: "\f1ea"; 460 | $fa-var-object-group: "\f247"; 461 | $fa-var-object-ungroup: "\f248"; 462 | $fa-var-odnoklassniki: "\f263"; 463 | $fa-var-odnoklassniki-square: "\f264"; 464 | $fa-var-opencart: "\f23d"; 465 | $fa-var-openid: "\f19b"; 466 | $fa-var-opera: "\f26a"; 467 | $fa-var-optin-monster: "\f23c"; 468 | $fa-var-outdent: "\f03b"; 469 | $fa-var-pagelines: "\f18c"; 470 | $fa-var-paint-brush: "\f1fc"; 471 | $fa-var-paper-plane: "\f1d8"; 472 | $fa-var-paper-plane-o: "\f1d9"; 473 | $fa-var-paperclip: "\f0c6"; 474 | $fa-var-paragraph: "\f1dd"; 475 | $fa-var-paste: "\f0ea"; 476 | $fa-var-pause: "\f04c"; 477 | $fa-var-pause-circle: "\f28b"; 478 | $fa-var-pause-circle-o: "\f28c"; 479 | $fa-var-paw: "\f1b0"; 480 | $fa-var-paypal: "\f1ed"; 481 | $fa-var-pencil: "\f040"; 482 | $fa-var-pencil-square: "\f14b"; 483 | $fa-var-pencil-square-o: "\f044"; 484 | $fa-var-percent: "\f295"; 485 | $fa-var-phone: "\f095"; 486 | $fa-var-phone-square: "\f098"; 487 | $fa-var-photo: "\f03e"; 488 | $fa-var-picture-o: "\f03e"; 489 | $fa-var-pie-chart: "\f200"; 490 | $fa-var-pied-piper: "\f2ae"; 491 | $fa-var-pied-piper-alt: "\f1a8"; 492 | $fa-var-pied-piper-pp: "\f1a7"; 493 | $fa-var-pinterest: "\f0d2"; 494 | $fa-var-pinterest-p: "\f231"; 495 | $fa-var-pinterest-square: "\f0d3"; 496 | $fa-var-plane: "\f072"; 497 | $fa-var-play: "\f04b"; 498 | $fa-var-play-circle: "\f144"; 499 | $fa-var-play-circle-o: "\f01d"; 500 | $fa-var-plug: "\f1e6"; 501 | $fa-var-plus: "\f067"; 502 | $fa-var-plus-circle: "\f055"; 503 | $fa-var-plus-square: "\f0fe"; 504 | $fa-var-plus-square-o: "\f196"; 505 | $fa-var-power-off: "\f011"; 506 | $fa-var-print: "\f02f"; 507 | $fa-var-product-hunt: "\f288"; 508 | $fa-var-puzzle-piece: "\f12e"; 509 | $fa-var-qq: "\f1d6"; 510 | $fa-var-qrcode: "\f029"; 511 | $fa-var-question: "\f128"; 512 | $fa-var-question-circle: "\f059"; 513 | $fa-var-question-circle-o: "\f29c"; 514 | $fa-var-quote-left: "\f10d"; 515 | $fa-var-quote-right: "\f10e"; 516 | $fa-var-ra: "\f1d0"; 517 | $fa-var-random: "\f074"; 518 | $fa-var-rebel: "\f1d0"; 519 | $fa-var-recycle: "\f1b8"; 520 | $fa-var-reddit: "\f1a1"; 521 | $fa-var-reddit-alien: "\f281"; 522 | $fa-var-reddit-square: "\f1a2"; 523 | $fa-var-refresh: "\f021"; 524 | $fa-var-registered: "\f25d"; 525 | $fa-var-remove: "\f00d"; 526 | $fa-var-renren: "\f18b"; 527 | $fa-var-reorder: "\f0c9"; 528 | $fa-var-repeat: "\f01e"; 529 | $fa-var-reply: "\f112"; 530 | $fa-var-reply-all: "\f122"; 531 | $fa-var-resistance: "\f1d0"; 532 | $fa-var-retweet: "\f079"; 533 | $fa-var-rmb: "\f157"; 534 | $fa-var-road: "\f018"; 535 | $fa-var-rocket: "\f135"; 536 | $fa-var-rotate-left: "\f0e2"; 537 | $fa-var-rotate-right: "\f01e"; 538 | $fa-var-rouble: "\f158"; 539 | $fa-var-rss: "\f09e"; 540 | $fa-var-rss-square: "\f143"; 541 | $fa-var-rub: "\f158"; 542 | $fa-var-ruble: "\f158"; 543 | $fa-var-rupee: "\f156"; 544 | $fa-var-safari: "\f267"; 545 | $fa-var-save: "\f0c7"; 546 | $fa-var-scissors: "\f0c4"; 547 | $fa-var-scribd: "\f28a"; 548 | $fa-var-search: "\f002"; 549 | $fa-var-search-minus: "\f010"; 550 | $fa-var-search-plus: "\f00e"; 551 | $fa-var-sellsy: "\f213"; 552 | $fa-var-send: "\f1d8"; 553 | $fa-var-send-o: "\f1d9"; 554 | $fa-var-server: "\f233"; 555 | $fa-var-share: "\f064"; 556 | $fa-var-share-alt: "\f1e0"; 557 | $fa-var-share-alt-square: "\f1e1"; 558 | $fa-var-share-square: "\f14d"; 559 | $fa-var-share-square-o: "\f045"; 560 | $fa-var-shekel: "\f20b"; 561 | $fa-var-sheqel: "\f20b"; 562 | $fa-var-shield: "\f132"; 563 | $fa-var-ship: "\f21a"; 564 | $fa-var-shirtsinbulk: "\f214"; 565 | $fa-var-shopping-bag: "\f290"; 566 | $fa-var-shopping-basket: "\f291"; 567 | $fa-var-shopping-cart: "\f07a"; 568 | $fa-var-sign-in: "\f090"; 569 | $fa-var-sign-language: "\f2a7"; 570 | $fa-var-sign-out: "\f08b"; 571 | $fa-var-signal: "\f012"; 572 | $fa-var-signing: "\f2a7"; 573 | $fa-var-simplybuilt: "\f215"; 574 | $fa-var-sitemap: "\f0e8"; 575 | $fa-var-skyatlas: "\f216"; 576 | $fa-var-skype: "\f17e"; 577 | $fa-var-slack: "\f198"; 578 | $fa-var-sliders: "\f1de"; 579 | $fa-var-slideshare: "\f1e7"; 580 | $fa-var-smile-o: "\f118"; 581 | $fa-var-snapchat: "\f2ab"; 582 | $fa-var-snapchat-ghost: "\f2ac"; 583 | $fa-var-snapchat-square: "\f2ad"; 584 | $fa-var-soccer-ball-o: "\f1e3"; 585 | $fa-var-sort: "\f0dc"; 586 | $fa-var-sort-alpha-asc: "\f15d"; 587 | $fa-var-sort-alpha-desc: "\f15e"; 588 | $fa-var-sort-amount-asc: "\f160"; 589 | $fa-var-sort-amount-desc: "\f161"; 590 | $fa-var-sort-asc: "\f0de"; 591 | $fa-var-sort-desc: "\f0dd"; 592 | $fa-var-sort-down: "\f0dd"; 593 | $fa-var-sort-numeric-asc: "\f162"; 594 | $fa-var-sort-numeric-desc: "\f163"; 595 | $fa-var-sort-up: "\f0de"; 596 | $fa-var-soundcloud: "\f1be"; 597 | $fa-var-space-shuttle: "\f197"; 598 | $fa-var-spinner: "\f110"; 599 | $fa-var-spoon: "\f1b1"; 600 | $fa-var-spotify: "\f1bc"; 601 | $fa-var-square: "\f0c8"; 602 | $fa-var-square-o: "\f096"; 603 | $fa-var-stack-exchange: "\f18d"; 604 | $fa-var-stack-overflow: "\f16c"; 605 | $fa-var-star: "\f005"; 606 | $fa-var-star-half: "\f089"; 607 | $fa-var-star-half-empty: "\f123"; 608 | $fa-var-star-half-full: "\f123"; 609 | $fa-var-star-half-o: "\f123"; 610 | $fa-var-star-o: "\f006"; 611 | $fa-var-steam: "\f1b6"; 612 | $fa-var-steam-square: "\f1b7"; 613 | $fa-var-step-backward: "\f048"; 614 | $fa-var-step-forward: "\f051"; 615 | $fa-var-stethoscope: "\f0f1"; 616 | $fa-var-sticky-note: "\f249"; 617 | $fa-var-sticky-note-o: "\f24a"; 618 | $fa-var-stop: "\f04d"; 619 | $fa-var-stop-circle: "\f28d"; 620 | $fa-var-stop-circle-o: "\f28e"; 621 | $fa-var-street-view: "\f21d"; 622 | $fa-var-strikethrough: "\f0cc"; 623 | $fa-var-stumbleupon: "\f1a4"; 624 | $fa-var-stumbleupon-circle: "\f1a3"; 625 | $fa-var-subscript: "\f12c"; 626 | $fa-var-subway: "\f239"; 627 | $fa-var-suitcase: "\f0f2"; 628 | $fa-var-sun-o: "\f185"; 629 | $fa-var-superscript: "\f12b"; 630 | $fa-var-support: "\f1cd"; 631 | $fa-var-table: "\f0ce"; 632 | $fa-var-tablet: "\f10a"; 633 | $fa-var-tachometer: "\f0e4"; 634 | $fa-var-tag: "\f02b"; 635 | $fa-var-tags: "\f02c"; 636 | $fa-var-tasks: "\f0ae"; 637 | $fa-var-taxi: "\f1ba"; 638 | $fa-var-television: "\f26c"; 639 | $fa-var-tencent-weibo: "\f1d5"; 640 | $fa-var-terminal: "\f120"; 641 | $fa-var-text-height: "\f034"; 642 | $fa-var-text-width: "\f035"; 643 | $fa-var-th: "\f00a"; 644 | $fa-var-th-large: "\f009"; 645 | $fa-var-th-list: "\f00b"; 646 | $fa-var-themeisle: "\f2b2"; 647 | $fa-var-thumb-tack: "\f08d"; 648 | $fa-var-thumbs-down: "\f165"; 649 | $fa-var-thumbs-o-down: "\f088"; 650 | $fa-var-thumbs-o-up: "\f087"; 651 | $fa-var-thumbs-up: "\f164"; 652 | $fa-var-ticket: "\f145"; 653 | $fa-var-times: "\f00d"; 654 | $fa-var-times-circle: "\f057"; 655 | $fa-var-times-circle-o: "\f05c"; 656 | $fa-var-tint: "\f043"; 657 | $fa-var-toggle-down: "\f150"; 658 | $fa-var-toggle-left: "\f191"; 659 | $fa-var-toggle-off: "\f204"; 660 | $fa-var-toggle-on: "\f205"; 661 | $fa-var-toggle-right: "\f152"; 662 | $fa-var-toggle-up: "\f151"; 663 | $fa-var-trademark: "\f25c"; 664 | $fa-var-train: "\f238"; 665 | $fa-var-transgender: "\f224"; 666 | $fa-var-transgender-alt: "\f225"; 667 | $fa-var-trash: "\f1f8"; 668 | $fa-var-trash-o: "\f014"; 669 | $fa-var-tree: "\f1bb"; 670 | $fa-var-trello: "\f181"; 671 | $fa-var-tripadvisor: "\f262"; 672 | $fa-var-trophy: "\f091"; 673 | $fa-var-truck: "\f0d1"; 674 | $fa-var-try: "\f195"; 675 | $fa-var-tty: "\f1e4"; 676 | $fa-var-tumblr: "\f173"; 677 | $fa-var-tumblr-square: "\f174"; 678 | $fa-var-turkish-lira: "\f195"; 679 | $fa-var-tv: "\f26c"; 680 | $fa-var-twitch: "\f1e8"; 681 | $fa-var-twitter: "\f099"; 682 | $fa-var-twitter-square: "\f081"; 683 | $fa-var-umbrella: "\f0e9"; 684 | $fa-var-underline: "\f0cd"; 685 | $fa-var-undo: "\f0e2"; 686 | $fa-var-universal-access: "\f29a"; 687 | $fa-var-university: "\f19c"; 688 | $fa-var-unlink: "\f127"; 689 | $fa-var-unlock: "\f09c"; 690 | $fa-var-unlock-alt: "\f13e"; 691 | $fa-var-unsorted: "\f0dc"; 692 | $fa-var-upload: "\f093"; 693 | $fa-var-usb: "\f287"; 694 | $fa-var-usd: "\f155"; 695 | $fa-var-user: "\f007"; 696 | $fa-var-user-md: "\f0f0"; 697 | $fa-var-user-plus: "\f234"; 698 | $fa-var-user-secret: "\f21b"; 699 | $fa-var-user-times: "\f235"; 700 | $fa-var-users: "\f0c0"; 701 | $fa-var-venus: "\f221"; 702 | $fa-var-venus-double: "\f226"; 703 | $fa-var-venus-mars: "\f228"; 704 | $fa-var-viacoin: "\f237"; 705 | $fa-var-viadeo: "\f2a9"; 706 | $fa-var-viadeo-square: "\f2aa"; 707 | $fa-var-video-camera: "\f03d"; 708 | $fa-var-vimeo: "\f27d"; 709 | $fa-var-vimeo-square: "\f194"; 710 | $fa-var-vine: "\f1ca"; 711 | $fa-var-vk: "\f189"; 712 | $fa-var-volume-control-phone: "\f2a0"; 713 | $fa-var-volume-down: "\f027"; 714 | $fa-var-volume-off: "\f026"; 715 | $fa-var-volume-up: "\f028"; 716 | $fa-var-warning: "\f071"; 717 | $fa-var-wechat: "\f1d7"; 718 | $fa-var-weibo: "\f18a"; 719 | $fa-var-weixin: "\f1d7"; 720 | $fa-var-whatsapp: "\f232"; 721 | $fa-var-wheelchair: "\f193"; 722 | $fa-var-wheelchair-alt: "\f29b"; 723 | $fa-var-wifi: "\f1eb"; 724 | $fa-var-wikipedia-w: "\f266"; 725 | $fa-var-windows: "\f17a"; 726 | $fa-var-won: "\f159"; 727 | $fa-var-wordpress: "\f19a"; 728 | $fa-var-wpbeginner: "\f297"; 729 | $fa-var-wpforms: "\f298"; 730 | $fa-var-wrench: "\f0ad"; 731 | $fa-var-xing: "\f168"; 732 | $fa-var-xing-square: "\f169"; 733 | $fa-var-y-combinator: "\f23b"; 734 | $fa-var-y-combinator-square: "\f1d4"; 735 | $fa-var-yahoo: "\f19e"; 736 | $fa-var-yc: "\f23b"; 737 | $fa-var-yc-square: "\f1d4"; 738 | $fa-var-yelp: "\f1e9"; 739 | $fa-var-yen: "\f157"; 740 | $fa-var-yoast: "\f2b1"; 741 | $fa-var-youtube: "\f167"; 742 | $fa-var-youtube-play: "\f16a"; 743 | $fa-var-youtube-square: "\f166"; 744 | 745 | --------------------------------------------------------------------------------