├── .babelrc ├── .gitignore ├── LICENSE ├── assets ├── icons │ └── favicon.ico └── templates │ ├── .debug.template.js │ └── manifest.template.xml.js ├── build-scripts ├── archive.js ├── build.js ├── deploy.js ├── utils.js ├── webpack.client.config.js └── webpack.session.config.js ├── kap.gif ├── package-lock.json ├── package.json ├── pluginrc.js ├── readme.md └── src ├── client-src ├── App.jsx ├── assets │ ├── css │ │ └── style.css │ └── fonts │ │ ├── material-icons │ │ ├── files │ │ │ └── icons.woff2 │ │ └── index.css │ │ └── typeface-roboto │ │ ├── README.md │ │ ├── files-hash.json │ │ ├── files │ │ ├── roboto-latin-300.woff │ │ ├── roboto-latin-300.woff2 │ │ ├── roboto-latin-400.woff │ │ ├── roboto-latin-400.woff2 │ │ ├── roboto-latin-500.woff │ │ └── roboto-latin-500.woff2 │ │ ├── index.css │ │ └── package.json ├── comps-bootstrap │ ├── AppBS.jsx │ ├── BrowseItem.jsx │ ├── Config.jsx │ ├── Debug.jsx │ ├── Decal.jsx │ ├── Home.jsx │ ├── Navigator.jsx │ ├── PluginItem.jsx │ ├── Upload.jsx │ └── backup │ │ ├── css │ │ ├── bootstrap-4.1.3-dist │ │ │ ├── css │ │ │ │ ├── bootstrap-grid.css │ │ │ │ ├── bootstrap-grid.css.map │ │ │ │ ├── bootstrap-grid.min.css │ │ │ │ ├── bootstrap-grid.min.css.map │ │ │ │ ├── bootstrap-reboot.css │ │ │ │ ├── bootstrap-reboot.css.map │ │ │ │ ├── bootstrap-reboot.min.css │ │ │ │ ├── bootstrap-reboot.min.css.map │ │ │ │ ├── bootstrap.css │ │ │ │ ├── bootstrap.css.map │ │ │ │ ├── bootstrap.min.css │ │ │ │ └── bootstrap.min.css.map │ │ │ └── js │ │ │ │ ├── bootstrap.bundle.js │ │ │ │ ├── bootstrap.bundle.js.map │ │ │ │ ├── bootstrap.bundle.min.js │ │ │ │ ├── bootstrap.bundle.min.js.map │ │ │ │ ├── bootstrap.js │ │ │ │ ├── bootstrap.js.map │ │ │ │ ├── bootstrap.min.js │ │ │ │ ├── bootstrap.min.js.map │ │ │ │ └── jquery-3.3.1.min.js │ │ ├── jquery-3.3.1.min.js │ │ ├── popper.min.js │ │ └── style.css │ │ └── index.html ├── comps │ ├── BrowseItem.jsx │ ├── Config.jsx │ ├── Debug.jsx │ ├── Hideable.jsx │ ├── Home.jsx │ ├── Navigator.jsx │ ├── PluginItem.jsx │ └── Upload.jsx ├── controller.js ├── index.js ├── index.server.template.html └── readme.md ├── host ├── JSON.jsx ├── index.jsx └── main.jsx ├── index.html ├── index.template.html ├── libs └── CSInterface.js └── session-src ├── .gitignore ├── index.js ├── package-lock.json ├── package.json └── src ├── ScriptLoader.js ├── Session.js └── managers ├── DataManagers.js └── LogManager.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "env", 4 | "react", 5 | "stage-2" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | /dist/ 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /assets/icons/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HendrixString/adobe-cep-react-create/a9c776cb721e9c23cc02a770a3295c96f4006466/assets/icons/favicon.ico -------------------------------------------------------------------------------- /assets/templates/.debug.template.js: -------------------------------------------------------------------------------- 1 | module.exports = (props) => 2 | ` 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | ` 19 | -------------------------------------------------------------------------------- /assets/templates/manifest.template.xml.js: -------------------------------------------------------------------------------- 1 | module.exports = (props) => 2 | ` 3 | 4 | 9 | 10 | 11 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | ./index.html 40 | ./host/index.jsx 41 | 42 | --enable-nodejs 43 | --allow-file-access 44 | --allow-file-access-from-files 45 | 46 | 47 | 48 | true 49 | 50 | 51 | Panel 52 | ${props.panelName} 53 | 54 | 55 | ${props.width} 56 | ${props.height} 57 | 58 | 59 | ${props.width} 60 | ${props.height} 61 | 62 | 63 | ${props.width} 64 | ${props.height} 65 | 66 | 67 | 68 | ./icons/favicon.ico 69 | ./icons/favicon.ico 70 | ./icons/favicon.ico 71 | ./icons/favicon.ico 72 | 73 | 74 | 75 | 76 | 77 | ` 78 | -------------------------------------------------------------------------------- /build-scripts/archive.js: -------------------------------------------------------------------------------- 1 | /** 2 | * archive, generates self signed certificate and signing a zxp package 3 | */ 4 | const fs = require('fs') 5 | const path = require('path') 6 | const zxpSignCmd = require('zxp-sign-cmd') 7 | const utils = require('./utils.js') 8 | const pluginConfig = require('../pluginrc.js') 9 | const isWindows = utils.resolveWindows() 10 | const distFolder = pluginConfig.destinationFolder 11 | const pluginFolder = path.join(distFolder, pluginConfig.extensionBundleId) 12 | const extensionBundleId = pluginConfig.extensionBundleId 13 | const certificate_options = pluginConfig.certificate 14 | const zxpFile = path.join(distFolder, pluginConfig.extensionBundleId + '.zxp') 15 | 16 | archive() 17 | 18 | function archive() { 19 | utils.log_progress('ARCHIVE', 'blue') 20 | 21 | prepareCert() 22 | .then(signPackage) 23 | .then(res => { 24 | utils.log_progress(`package is signed: ${zxpFile}`, 'green') 25 | utils.log_progress('DONE', 'blue') 26 | }) 27 | .catch(err => {utils.log_error(err)}) 28 | } 29 | 30 | /** 31 | * find a custom certificate or generate a self sign the certificate 32 | * 33 | * @return {Promise} a promise, that resolves the cert data {path, password} 34 | */ 35 | function prepareCert() { 36 | const options_custom_cert = certificate_options.customCert 37 | const options_self_sign = certificate_options.selfSign 38 | const isCustom = options_custom_cert && options_custom_cert.path.trim() !== '' 39 | var path='', password='' 40 | 41 | if(isCustom) { 42 | path = options_custom_cert.path 43 | password = options_custom_cert.password 44 | } else if(options_self_sign){ 45 | path = options_self_sign.output 46 | password = options_self_sign.password 47 | } 48 | 49 | const isValid = path!==undefined && path.trim()!=='' 50 | const data = {path, password} 51 | 52 | // on non windows, we need to change the permissions 53 | if(!isWindows) { 54 | var provider = require('zxp-provider').osx 55 | // for some reason the path returns quoted, so I un-quote 56 | var unquote = provider.substring(1, provider.length - 1) 57 | fs.chmodSync(unquote, '755') 58 | } 59 | 60 | return new Promise((resolve, reject) => { 61 | if(!isValid) { 62 | reject('no valid cert info') 63 | 64 | return 65 | } 66 | 67 | if(isCustom) { 68 | utils.log_progress('found a custom certificate') 69 | resolve(data) 70 | } else { 71 | utils.log_progress('generating a self signed certificate') 72 | zxpSignCmd.selfSignedCert(options_self_sign, function (error, result) { 73 | if(error) reject(error) 74 | else resolve(data) 75 | }) 76 | 77 | } 78 | 79 | }) 80 | 81 | } 82 | 83 | /** 84 | * sign the package 85 | * 86 | * @param {{path, password}} cert description 87 | * 88 | * @return {Promise} a promise 89 | */ 90 | function signPackage(cert) { 91 | const options = { 92 | input: pluginFolder, 93 | output: zxpFile, 94 | cert: cert.path, 95 | password: cert.password 96 | } 97 | 98 | return new Promise((resolve, reject) => { 99 | zxpSignCmd.sign(options, function (error, result) { 100 | if(error) reject(error) 101 | else resolve(result) 102 | 103 | }) 104 | 105 | }) 106 | 107 | } 108 | -------------------------------------------------------------------------------- /build-scripts/build.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * a minimalist non fancy build script 4 | */ 5 | 6 | const fs = require('fs') 7 | const path = require('path') 8 | const { execSync } = require('child_process') 9 | const utils = require('./utils.js') 10 | const webpack = require('webpack') 11 | const pluginConfig = require('../pluginrc.js') 12 | const env = utils.resolveEnv() 13 | const isDev = env==='development' 14 | const distFolder = pluginConfig.destinationFolder 15 | const pluginFolder = path.join(distFolder, pluginConfig.extensionBundleId) 16 | const srcFolder = pluginConfig.sourceFolder 17 | const rootFolder = pluginConfig.root 18 | const templatesFolder = path.join(fromRoot('assets'), 'templates') 19 | const webpack_client_config_path = path.join(__dirname, 'webpack.client.config.js') 20 | const webpack_session_config_path = path.join(__dirname, 'webpack.session.config.js') 21 | utils.log_progress(`BUILD for ${env}`, 'blue') 22 | 23 | build() 24 | 25 | function build() { 26 | try { 27 | // delete the dist 28 | utils.log_progress('cleaning dist...') 29 | utils.deleteFolderRecursive(distFolder) 30 | // create dist 31 | fs.mkdirSync(distFolder) 32 | fs.mkdirSync(pluginFolder) 33 | // bundle the client 34 | utils.log_progress('bundeling client...') 35 | // var config = require('../webpack.client.config.js') 36 | // webpack(config, (err, stats) => { 37 | // if (err) { 38 | // console.error(err); 39 | // return; 40 | // } 41 | // 42 | // console.log(stats.toString({ 43 | // chunks: false, // Makes the build much quieter 44 | // colors: true // Shows colors in the console 45 | // })); 46 | // }); 47 | 48 | execSync(`webpack --config ${webpack_client_config_path} --display minimal --display-chunks --env.target=node --mode ${env}`, {stdio:[0,1,2]}) 49 | // bundle the session 50 | utils.log_progress('bundeling session...') 51 | execSync(`webpack --config ${webpack_session_config_path} --display normal --display-chunks --env.target=node --mode ${env}`, {stdio:[0,1,2]}) 52 | // copy the host code 53 | utils.log_progress('copying host code...') 54 | utils.copyRecursiveSync(fromSrc('host'), fromPlugin('host')) 55 | // copy the session's node modules folder 56 | utils.log_progress('copying session node-modules...') 57 | utils.copyRecursiveSync(fromSrc('session-src/node_modules'), fromPlugin('node_modules')) 58 | // copying libs folder 59 | utils.log_progress('copying libs folder...') 60 | utils.copyRecursiveSync(fromSrc('libs'), fromPlugin('libs')) 61 | // copy the index.html 62 | // fs.createReadStream('./src/index.html').pipe(fs.createWriteStream('./dist/index.html')); 63 | utils.log_progress('copying index.html...') 64 | utils.copyRecursiveSync(fromSrc('index.html'), fromPlugin('index.html')) 65 | // copy other assets 66 | utils.log_progress('copying Adobe assets...') 67 | utils.copyRecursiveSync(fromRoot('assets'), pluginFolder, ['templates']) 68 | // render manifest.xml 69 | utils.log_progress('rendering manifest.xml ...') 70 | var manifest_template = require(path.join(templatesFolder, 'manifest.template.xml.js')) 71 | var rendered_xml = manifest_template(pluginConfig) 72 | 73 | var xml_out_dir = path.join(pluginFolder, 'CSXS') 74 | fs.mkdir(xml_out_dir, { recursive: true }, (err) => {if (err) throw err;}); 75 | 76 | var xml_out_file = path.join(pluginFolder, 'CSXS', 'manifest.xml') 77 | fs.writeFileSync(xml_out_file, rendered_xml, 'utf-8') 78 | 79 | // in dev, also render the .debug file template 80 | if(isDev) { 81 | // render .debug file 82 | utils.log_progress('rendering .debug file ...') 83 | var debug_template = require(path.join(templatesFolder, '.debug.template.js')) 84 | var rendered_debug = debug_template(pluginConfig) 85 | var debug_out_file = path.join(pluginFolder, '.debug') 86 | fs.writeFileSync(debug_out_file, rendered_debug, 'utf-8') 87 | } 88 | 89 | utils.log_progress('DONE', 'blue') 90 | } catch(err) { 91 | utils.log_progress(err, 'red') 92 | } 93 | } 94 | 95 | function fromDist(val) { 96 | return path.join(distFolder, val) 97 | } 98 | 99 | function fromPlugin(val) { 100 | return path.join(pluginFolder, val) 101 | } 102 | 103 | function fromSrc(val) { 104 | return path.join(srcFolder, val) 105 | } 106 | 107 | function fromRoot(val) { 108 | return path.join(rootFolder, val) 109 | } 110 | -------------------------------------------------------------------------------- /build-scripts/deploy.js: -------------------------------------------------------------------------------- 1 | /** 2 | * deploy in dev mode or production 3 | */ 4 | const { execSync } = require('child_process') 5 | const fs = require('fs') 6 | const os = require('os') 7 | const path = require('path') 8 | const utils = require('./utils.js') 9 | const pluginConfig = require('../pluginrc.js') 10 | const distFolder = path.join(pluginConfig.destinationFolder, pluginConfig.extensionBundleId) 11 | const env = utils.resolveEnv() 12 | const isDev = env === 'development' 13 | const isWindows = utils.resolveWindows() 14 | const extensionBundleId = pluginConfig.extensionBundleId 15 | const resolvedTargetFolder = resolveDeploymentFolder() 16 | 17 | deploy() 18 | 19 | /** 20 | * deploy 21 | * 22 | */ 23 | function deploy() { 24 | utils.log_progress(`DEPLOY for ${env}`, 'blue') 25 | 26 | cleanTarget(resolvedTargetFolder) 27 | 28 | if(isDev) 29 | deployDevMode() 30 | else 31 | deployProdMode() 32 | 33 | printDeploymentFolder() 34 | 35 | utils.log_progress('DONE', 'blue') 36 | } 37 | 38 | function printDeploymentFolder() { 39 | utils.log_progress(`deployed to folder ${resolvedTargetFolder}`, 'green') 40 | } 41 | 42 | /** 43 | * resolve the final deployment folder 44 | * 45 | */ 46 | function resolveDeploymentFolder() { 47 | return path.join(resolveExtensionFolder(), extensionBundleId) 48 | } 49 | 50 | /** 51 | * per os Adobe extension folder 52 | * 53 | */ 54 | function resolveExtensionFolder() { 55 | if (isWindows) { 56 | const extensionsPath = os.userInfo().homedir + '\\AppData\\Roaming\\Adobe\\CEP\\extensions' 57 | if (!fs.existsSync(extensionsPath)) 58 | fs.mkdirSync(extensionsPath, { recursive: true }) 59 | 60 | return extensionsPath; 61 | } else { 62 | return path.join(os.homedir(), 'Library/Application Support/Adobe/CEP/extensions') 63 | } 64 | 65 | } 66 | 67 | /** 68 | * cleanTarget - clean the target folder. if it is a 69 | * symlink then unlink, and if it is a folder then delete. 70 | * 71 | */ 72 | function cleanTarget(target) { 73 | utils.log_progress('cleaning target') 74 | 75 | try { 76 | if(fs.existsSync(target) && fs.lstatSync(target).isSymbolicLink()) 77 | fs.unlinkSync(target) 78 | utils.deleteFolderRecursive(target) 79 | } catch (err) { 80 | utils.log_error(err) 81 | } 82 | } 83 | 84 | /** 85 | * deployDevMode - just create a symlink 86 | * 87 | */ 88 | function deployDevMode() { 89 | try { 90 | utils.log_progress('patching') 91 | if (isWindows) { 92 | execSync('REG ADD HKEY_CURRENT_USER\\Software\\Adobe\\CSXS.8 /v PlayerDebugMode /t REG_SZ /d 1 /f') // CC 2018 93 | execSync('REG ADD HKEY_CURRENT_USER\\Software\\Adobe\\CSXS.9 /v PlayerDebugMode /t REG_SZ /d 1 /f') // CC 2019 & 2020 94 | } else { 95 | execSync('defaults write com.adobe.CSXS.8 PlayerDebugMode 1', { stdio: [0, 1, 2] }) // CC 2018 96 | execSync('defaults write com.adobe.CSXS.9 PlayerDebugMode 1', { stdio: [0, 1, 2] }) // CC 2019 & 2020 97 | } 98 | } catch(err) { 99 | utils.log_error(err) 100 | } 101 | 102 | utils.log_progress('creating symlink into extensions folder') 103 | try { 104 | var type = isWindows ? 'junction' : 'dir' 105 | 106 | fs.symlinkSync(distFolder, resolvedTargetFolder, type) 107 | } catch(err) { 108 | utils.log_error(err) 109 | } 110 | 111 | } 112 | 113 | /** 114 | * deployProdMode - copy the whole dist folder 115 | * 116 | */ 117 | function deployProdMode() { 118 | 119 | utils.log_progress('copying into extensions folder') 120 | try { 121 | utils.copyRecursiveSync(distFolder, resolvedTargetFolder) 122 | 123 | } catch(err) { 124 | utils.log_error(err) 125 | } 126 | 127 | } 128 | -------------------------------------------------------------------------------- /build-scripts/utils.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * the build script for the whole thing 4 | */ 5 | 6 | const { execSync } = require('child_process') 7 | var fs = require('fs') 8 | var path = require('path') 9 | const chalk = require('chalk'); 10 | 11 | function deleteFolderRecursive(src) { 12 | if (fs.existsSync(src)) { 13 | fs.readdirSync(src).forEach(function(file, index){ 14 | var curPath = path.join(src, file) 15 | if (fs.lstatSync(curPath).isDirectory()) { // recurse 16 | deleteFolderRecursive(curPath) 17 | } else { // delete file 18 | fs.unlinkSync(curPath) 19 | } 20 | }) 21 | fs.rmdirSync(src) 22 | } 23 | } 24 | 25 | function hasInList(item, list) { 26 | var res = false 27 | 28 | if(!list) 29 | return false 30 | 31 | list.forEach(one => { 32 | if(one===item) { 33 | res = true 34 | return 35 | } 36 | }) 37 | 38 | return res 39 | } 40 | 41 | /** 42 | * Look ma, it's cp -R. 43 | * @param {string} src The path to the thing to copy. 44 | * @param {string} dest The path to the new copy. 45 | * @param {ignore_list} folders to ignore in the first level only 46 | */ 47 | function copyRecursiveSync(src, dest, ignore_list) { 48 | var exists = fs.existsSync(src) 49 | var exists_dest = fs.existsSync(dest) 50 | var stats = exists && fs.statSync(src) 51 | var isDirectory = exists && stats.isDirectory() 52 | if (exists && isDirectory) { 53 | if(!exists_dest) 54 | fs.mkdirSync(dest) 55 | fs.readdirSync(src).filter(item => !hasInList(item, ignore_list)) 56 | .forEach(childItemName => { 57 | copyRecursiveSync(path.join(src, childItemName), path.join(dest, childItemName)) 58 | }) 59 | } else { 60 | fs.linkSync(src, dest) 61 | } 62 | } 63 | 64 | function log(val) { 65 | console.log(val) 66 | } 67 | 68 | function log_error(val) { 69 | log_progress(val, 'red') 70 | } 71 | 72 | function log_progress(val, color) { 73 | var c = color ? color : 'yellow' 74 | 75 | console.log(chalk[c](val)) 76 | } 77 | 78 | function resolveWindows() { 79 | return process.platform.startsWith('win') 80 | } 81 | 82 | /** 83 | * resolve env from process argumants 84 | * 85 | */ 86 | function resolveEnv() { 87 | var env = 'development' 88 | 89 | var args = process.argv 90 | 91 | // we have an argument 92 | if(args.length >= 3) { 93 | env = args[2] 94 | } 95 | 96 | return env 97 | } 98 | 99 | module.exports = { 100 | deleteFolderRecursive, 101 | copyRecursiveSync, 102 | log, 103 | log_error, 104 | log_progress, 105 | resolveWindows, 106 | resolveEnv 107 | } 108 | -------------------------------------------------------------------------------- /build-scripts/webpack.client.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 3 | const MiniCssExtractPlugin = require("mini-css-extract-plugin"); 4 | const pluginConfig = require('../pluginrc.js') 5 | // const distFolder = pluginConfig.destinationFolder 6 | const distFolder = path.join(pluginConfig.destinationFolder, pluginConfig.extensionBundleId) 7 | const srcFolder = pluginConfig.sourceFolder 8 | const CLIENT_DIST_PATH = path.resolve(distFolder, 'client-dist') 9 | const HTML_TEMPLATE_PATH = path.join(srcFolder, 'client-src/index.server.template.html') 10 | const ENTRY_POINT_CLIENT_PATH = path.join(srcFolder, 'client-src/index.js') 11 | 12 | module.exports = ({ 13 | entry: ENTRY_POINT_CLIENT_PATH, 14 | target: 'web', 15 | module: { 16 | rules: [ 17 | { 18 | test: /\.(js|jsx)$/, 19 | exclude: /node_modules/, 20 | loader: "babel-loader", 21 | options: { 22 | presets: ['env', 'react', 'stage-2'] 23 | } 24 | 25 | }, 26 | { 27 | test: /\.(woff|woff2|eot|ttf|svg)$/, 28 | loader: 'file-loader', 29 | options: { 30 | name: 'fonts/[name].[ext]' 31 | } 32 | }, 33 | { 34 | test:/\.css$/, 35 | use:[MiniCssExtractPlugin.loader,'css-loader'] 36 | }] 37 | }, 38 | resolve: { 39 | extensions: ['*', '.js', '.jsx'] 40 | }, 41 | output: { 42 | path: CLIENT_DIST_PATH, 43 | publicPath: '', 44 | filename: 'bundle.js' 45 | }, 46 | devtool: 'source-map', 47 | devServer: { 48 | contentBase: CLIENT_DIST_PATH 49 | }, 50 | plugins: [ 51 | new MiniCssExtractPlugin({ 52 | // Options similar to the same options in webpackOptions.output 53 | // both options are optional https://github.com/webpack-contrib/mini-css-extract-plugin 54 | filename: "[name].css", 55 | chunkFilename: "[id].css" 56 | }), 57 | new HtmlWebpackPlugin({ 58 | template: HTML_TEMPLATE_PATH, 59 | filename: 'index.html', 60 | inject: 'body', 61 | title: 'HTML Webpack Plugin', 62 | bar: 'bar' 63 | }) 64 | ] 65 | 66 | }) 67 | -------------------------------------------------------------------------------- /build-scripts/webpack.session.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | // const CopyWebpackPlugin = require('copy-webpack-plugin') 3 | const nodeExternals = require('webpack-node-externals'); 4 | const pluginConfig = require('../pluginrc.js') 5 | const distFolder = path.join(pluginConfig.destinationFolder, pluginConfig.extensionBundleId) 6 | const srcFolder = pluginConfig.sourceFolder 7 | const SESSION_DIST_PATH = path.join(distFolder, 'session-dist') 8 | const SESSION_SRC_PATH = path.join(srcFolder, 'session-src') 9 | const ENTRY_POINT_SESSION_PATH = path.join(SESSION_SRC_PATH, 'index.js') 10 | 11 | module.exports = (env) => ({ 12 | entry: ENTRY_POINT_SESSION_PATH, 13 | target: 'node', 14 | externals: [nodeExternals({modulesDir: path.join(SESSION_SRC_PATH, 'node_modules')})], 15 | module: { 16 | rules: [ 17 | { 18 | test: /\.(js|jsx)$/, 19 | exclude: /node_modules/, 20 | loader: "babel-loader", 21 | options: { 22 | presets: ['env', 'react', 'stage-2'] 23 | } 24 | 25 | }] 26 | }, 27 | resolve: { 28 | extensions: ['*', '.js', '.jsx'] 29 | }, 30 | output: { 31 | path: SESSION_DIST_PATH, 32 | publicPath: '', 33 | filename: 'bundle.js' 34 | }, 35 | devtool: 'source-map', 36 | plugins: [ 37 | // new CopyWebpackPlugin([ 38 | // { from: path.join(SESSION_SRC_PATH, 'node_modules'), to: '../node_modules' } 39 | // ]) 40 | ] 41 | 42 | }) 43 | -------------------------------------------------------------------------------- /kap.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HendrixString/adobe-cep-react-create/a9c776cb721e9c23cc02a770a3295c96f4006466/kap.gif -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cep-test", 3 | "version": "1.0.0", 4 | "description": "demo for CEP", 5 | "main": "./client-src/src/index.js", 6 | "scripts": { 7 | "build": "node ./scripts/build.js", 8 | "start": "npm run client:dev-server", 9 | "client:dev-server": "webpack-dev-server --config ./build-scripts/webpack.client.config.js --env.target=web --mode development --open", 10 | "build:dev": "node ./build-scripts/build.js development", 11 | "build:prod": "node ./build-scripts/build.js production", 12 | "deploy:dev": "node ./build-scripts/deploy.js development", 13 | "deploy:prod": "node ./build-scripts/deploy.js production", 14 | "archive": "node ./build-scripts/archive.js", 15 | "release:dev": "npm run build:dev && npm run deploy:dev", 16 | "release:prod": "npm run build:prod && npm run deploy:prod && npm run archive", 17 | "postinstall": "mkdirp src/session-src/node_modules", 18 | "b": "babel ./client/js --out-dir ./client/out", 19 | "test": "echo \"Error: no test specified\" && exit 1" 20 | }, 21 | "author": "Tomer Shalev (HendrixString)", 22 | "license": "ISC", 23 | "devDependencies": { 24 | "babel-cli": "^6.26.0", 25 | "babel-core": "^6.26.3", 26 | "babel-loader": "^7.1.5", 27 | "babel-preset-env": "^1.7.0", 28 | "babel-preset-react": "^6.24.1", 29 | "babel-preset-stage-2": "^6.24.1", 30 | "chalk": "^2.4.1", 31 | "copy-webpack-plugin": "^4.5.2", 32 | "css-loader": "^1.0.0", 33 | "extract-text-webpack-plugin": "^4.0.0-beta.0", 34 | "file-loader": "^1.1.11", 35 | "hard-source-webpack-plugin": "^0.12.0", 36 | "html-webpack-plugin": "^3.2.0", 37 | "mini-css-extract-plugin": "^0.4.1", 38 | "mkdirp": "^0.5.1", 39 | "style-loader": "^0.21.0", 40 | "webpack": "^4.16.2", 41 | "webpack-cli": "^3.1.0", 42 | "webpack-dev-server": "^3.7.2", 43 | "webpack-node-externals": "^1.7.2", 44 | "zxp-sign-cmd": "^1.0.0" 45 | }, 46 | "dependencies": { 47 | "@material-ui/core": "^1.4.2", 48 | "@material-ui/icons": "^2.0.0", 49 | "console-feed": "^2.8.3", 50 | "react": "^16.4.1", 51 | "react-dom": "^16.4.1" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /pluginrc.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const root = __dirname 3 | const srcFolder = path.join(root, "src") 4 | const destFolder = path.join(root, "dist") 5 | const certPath = path.join(destFolder, "cert.p12") 6 | module.exports = { 7 | extensionBundleId: 'com.hendrix.demo', 8 | extensionBundleName: 'demo', 9 | extensionBundleVersion: '1.0.1', 10 | cepVersion: '7.0', 11 | panelName: 'hendrix demo', 12 | width: '400', 13 | height: '600', 14 | root: root, 15 | sourceFolder: srcFolder, 16 | destinationFolder: destFolder, 17 | certificate : { 18 | customCert: { 19 | path: '', 20 | password: 'password' 21 | }, 22 | selfSign: { 23 | country: 'US', 24 | province: 'CA', 25 | org: 'org', 26 | name: 'name', 27 | password: 'password', 28 | locality: 'locality', 29 | orgUnit: 'orgUnit', 30 | email: 'your@email.com', 31 | output: certPath 32 | } 33 | 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ## Create Adobe-CEP extension 2 | ### Adobe-CEP with React, Material-UI, Native Node modules, Webpack, Babel and ExtendScript 3 | 4 | 5 | 6 | this Adobe-CEP extension creator bootstraps for creating Adobe CC extensions easily with 7 | modern web technologies and with native node.js modules for session logic 8 | and with support for extendscript (host app). It is built in a semi opinionated 9 | way so you can focus on writing your great extensions. 10 | 11 | #### how to build 12 | first run `npm install`, then choose 13 | - `npm run build:dev` / `npm run build:prod` - will build into `./dist` folder 14 | - `npm run deploy:dev` / `npm run deploy:prod` - will deploy `./dist` folder into the extension folder. 15 | if in dev mode, it will create a **symbolic link**, otherwise it will copy the entire folder. 16 | - `npm run archive` will create a self signed certificate and sign a **ZXP** package ready to publish 17 | - `npm run release:dev` / `npm run release:prod` - will build, deploy and archive (in production) 18 | 19 | the output is a `./dist` extension folder 20 | ``` 21 | dist 22 | com.package.name/ 23 | index.html 24 | .debug 25 | CSXS/ 26 | manifest.xml 27 | icons/ 28 | favicon.ico 29 | node_modules/ 30 | host/ 31 | index.js 32 | client-dist/ 33 | bundle.js 34 | main.css 35 | session-dist/ 36 | bundle.js 37 | host/ 38 | libs/ 39 | CSInterface.js 40 | ``` 41 | 42 | #### how to customize 43 | start with `./pluginrc.js`, this is the plugin config I created, here is an example 44 | ```javascript 45 | module.exports = { 46 | extensionBundleId: 'com.hendrix.demo', 47 | extensionBundleName: 'demo', 48 | extensionBundleVersion: '1.0.1', 49 | cepVersion: '7.0', 50 | panelName: 'hendrix demo', 51 | width: '400', 52 | height: '600', 53 | root: root, 54 | sourceFolder: srcFolder, 55 | destinationFolder: destFolder, 56 | certificate : { 57 | customCert: { 58 | path: '', 59 | password: 'password' 60 | }, 61 | selfSign: { 62 | country: 'US', 63 | province: 'CA', 64 | org: 'org', 65 | name: 'name', 66 | password: 'password', 67 | locality: 'locality', 68 | orgUnit: 'orgUnit', 69 | email: 'your@email.com', 70 | output: certPath 71 | } 72 | 73 | } 74 | 75 | } 76 | ``` 77 | when build is happening, then the build will pickup your package id and panel name 78 | and other configurations from this file and will use it against a template that will 79 | generate the `./dist/CSXS/manifest.xml` and `.debug` (in dev mode) file for you. 80 | also, I added support for a custom certificate and for a self-signed certificate. 81 | feel free to modify the contents of the `assets` folder for you own need. 82 | 83 | #### how to debug 84 | debugging is achieved through the chrome debugger 85 | - release a dev build with `npm run release:dev` 86 | - inside Adobe, open the extension, you may have to restart if this is the first time. 87 | - open a browser at the following location http://localhost:`PORT`/ (See port number in .debug file) 88 | 89 | ### what does this include ? 90 | this bootstrap is composed of three parts 91 | 92 | #### Front end side 93 | inside `src/client-src` you have the entry point for creating ReactJS application. 94 | installing modules is against the project root, see `/project.json`. 95 | a nice feature, that it has is that you can use `webpack-dev-server` to see 96 | your UI results with watching at the browser, simply use: 97 | - `npm start` or 98 | - `npm run client:dev-server` 99 | this will generate the template html for you and run it in your browser, 100 | and will rebuild on code changes which is nice to have. 101 | 102 | #### back end side / session (using node modules) 103 | inside `src/session-src` you have the entry point for using native node.js modules. 104 | Adobe-CEP supports instantiating Node.js runtime as well as Chromium but I believe 105 | most developers would like to use the power of Node.js for doing IO. 106 | 107 | Adobe-CEP does provide it's native IO for disk access and also using Chromium 108 | you can use the browser `Fetch` api, but it can lead to very bad code structuring. 109 | 110 | I inject the `session` object in the `window` object and therefore it is accessible 111 | even from the front end side. 112 | Also notice, that this folder has it's own `node-modules` separate from the root folder, 113 | this is because, they are built differently from the Front end side. 114 | 115 | using node modules can enhance the functionality. 116 | 117 | #### ExtendScript side 118 | inside `src/host`, you will put you `jsx` files, by default I will load `index.jsx`, 119 | but I highly advise to use the session to load a `jsx` file dynamically so it can pick 120 | up it's `#include` dependencies otherwise it won't (this is a known issue) 121 | 122 | #### Webpack side 123 | so why am I using **Webpack** ? 124 | without webpack, you will have to require modules by absolute path, which is not nice, 125 | also I wanted to enjoy a better ES6 syntax. 126 | 127 | why are there separate **Webpack** configs for `client` and `session`? 128 | very good question. It boils down to the following fact, the client/front-end side, uses 129 | pure web technologies and can be bundeled with all of it's dependencies and it has a classic 130 | `web` target for webpack. 131 | the `session` side uses native node.js modules and has a `node` target in it's config, they 132 | are not to be mixed together or else subtle configuration will not work. this is equivalent 133 | to other projects using `electron`, also, it is not advisable to bundle native node.js modules, 134 | this is not efficient. 135 | 136 | #### Build scripts 137 | inside `/build-scripts`, you will find the webpack configs and also the build and deploy 138 | scripts. They use no-fancy node modules to keep things simple (no libs like Gulp). 139 | 140 | you can find: 141 | - `/build-script/build.js development/production` this will build the entire thing 142 | - `/build-script/deploy.js development/production` this will deploy the entire thing into 143 | the adobe extensions folder in debug mode currently, I still need to sign the extension 144 | - `/build-script/archive.js` this will archive the distribution in **ZXP** format, ready to be published 145 | 146 | #### FAQ 147 | **Q:** how do I add more web development modules (like redux) ? 148 | **A:** simply `npm install redux` from the root `./` directory 149 | 150 | **Q:** how do I add more session native node modules (like fs-extra) ? 151 | **A:** simply `npm install fs-extra` from the `./src/session-src` directory, when building occurs, these 152 | modules will be copied to the `./dist` folder. 153 | 154 | **Q:** how do I add some js lib without npm ? 155 | **A:** simply edit `./src/index.html` 156 | 157 | **Q:** how do I add some extendscript files ? 158 | **A:** you must add them to `./src/host/` folder and then you have two choices. one, is to edit 159 | `./assets/CSXS/manifest.xml` file to declare them, or load them at runtime dynamically (better, you can read 160 | about it more later) 161 | 162 | #### How to install 163 | - for dev mode with chrome debugging, simply `npm run release:dev` 164 | - for prod mode with **zxp** signed package, simply `npm run release:prod`, to install the zxp package, 165 | i advise the following resource: 166 | - http://install.anastasiy.com/ 167 | - http://zxpinstaller.com/ 168 | - https://github.com/Adobe-CEP/Getting-Started-guides/tree/master/Package%20Distribute%20Install 169 | - http://uberplugins.cc/help/how-to-install-photoshop-extension/ 170 | -------------------------------------------------------------------------------- /src/client-src/App.jsx: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Tomer Riko Shalev 3 | */ 4 | 5 | import React from 'react' 6 | 7 | import Home from './comps/Home.jsx' 8 | import Debug from './comps/Debug.jsx' 9 | import Config from './comps/Config.jsx' 10 | import Upload from './comps/Upload.jsx' 11 | import Navigator from './comps/Navigator.jsx' 12 | 13 | import purple from '@material-ui/core/colors/purple'; 14 | import green from '@material-ui/core/colors/green'; 15 | import indigo from '@material-ui/core/colors/indigo'; 16 | import pink from '@material-ui/core/colors/pink'; 17 | import red from '@material-ui/core/colors/red'; 18 | import orange from '@material-ui/core/colors/amber'; 19 | import cyan from '@material-ui/core/colors/cyan'; 20 | 21 | import { MuiThemeProvider, createMuiTheme } from '@material-ui/core/styles' 22 | 23 | const theme = createMuiTheme({ 24 | palette: { 25 | type:'dark', 26 | primary: cyan, 27 | secondary: pink, 28 | error: red, 29 | contrastThreshold: 3, 30 | tonalOffset: 0.2, 31 | 32 | }, 33 | status: { 34 | danger: 'orange', 35 | }, 36 | typography: { 37 | // In Japanese the characters are usually larger. 38 | fontSize: 12, 39 | }, 40 | 41 | }) 42 | 43 | const styles = { 44 | root: { 45 | width: '100%', 46 | height: '100vh' 47 | } 48 | } 49 | 50 | /** 51 | * main app component 52 | * 53 | */ 54 | export default class App extends React.Component { 55 | _controller = undefined 56 | 57 | constructor(props) { 58 | super(props) 59 | 60 | // controller 61 | this._controller = props.controller 62 | // navigator data 63 | this.data = [ 64 | {title: 'Home', icon: 'home', comp: }, 65 | {title: 'Upload', icon: 'cloud_upload', comp: }, 66 | {title: 'Config', icon: 'settings', comp: }, 67 | {title: 'Debug', icon: 'bug_report', comp: } 68 | ] 69 | 70 | } 71 | 72 | onNavigateChange = (index, title) => { 73 | console.log(`onNavigateChange:: ${index}, ${title}`) 74 | } 75 | 76 | /** 77 | * get controller 78 | * 79 | * @return {Controller} 80 | */ 81 | get controller() { 82 | return this._controller 83 | } 84 | 85 | /** 86 | * execute the plugin 87 | * 88 | * @param {type} options description 89 | */ 90 | onExecutePlugin = (options) => { 91 | console.log('App:: onExecutePlugin') 92 | // here disable UI 93 | this._controller.invokePlugin(options) 94 | // here enable ui 95 | } 96 | 97 | render() { 98 | 99 | return ( 100 |
101 | 102 | 103 | 104 |
105 | ) 106 | 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /src/client-src/assets/css/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0px; 3 | overflow: hidden 4 | } 5 | -------------------------------------------------------------------------------- /src/client-src/assets/fonts/material-icons/files/icons.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HendrixString/adobe-cep-react-create/a9c776cb721e9c23cc02a770a3295c96f4006466/src/client-src/assets/fonts/material-icons/files/icons.woff2 -------------------------------------------------------------------------------- /src/client-src/assets/fonts/material-icons/index.css: -------------------------------------------------------------------------------- 1 | /* fallback */ 2 | @font-face { 3 | font-family: 'Material Icons'; 4 | font-style: normal; 5 | font-weight: 400; 6 | src: url('./files/icons.woff2') format('woff2'); 7 | } 8 | 9 | .material-icons { 10 | font-family: 'Material Icons'; 11 | font-weight: normal; 12 | font-style: normal; 13 | font-size: 24px; 14 | line-height: 1; 15 | letter-spacing: normal; 16 | text-transform: none; 17 | display: inline-block; 18 | white-space: nowrap; 19 | word-wrap: normal; 20 | direction: ltr; 21 | -webkit-font-feature-settings: 'liga'; 22 | -webkit-font-smoothing: antialiased; 23 | } 24 | -------------------------------------------------------------------------------- /src/client-src/assets/fonts/typeface-roboto/README.md: -------------------------------------------------------------------------------- 1 | 2 | # typeface-roboto 3 | 4 | The CSS and web font files to easily self-host “Roboto”. 5 | 6 | ## Install 7 | 8 | `npm install --save typeface-roboto` 9 | 10 | ## Use 11 | 12 | Typefaces assume you’re using webpack to process CSS and files. Each typeface 13 | package includes all necessary font files (woff2, woff, eot, ttf, svg) and 14 | a CSS file with font-face declarations pointing at these files. 15 | 16 | You will need to have webpack setup to load css and font files. Many tools built 17 | with Webpack will work out of the box with Typefaces such as [Gatsby](https://github.com/gatsbyjs/gatsby) 18 | and [Create React App](https://github.com/facebookincubator/create-react-app). 19 | 20 | To use, simply require the package in your project’s entry file e.g. 21 | 22 | ```javascript 23 | // Load Roboto typeface 24 | require('typeface-roboto') 25 | ``` 26 | 27 | ## About the Typefaces project. 28 | 29 | Our goal is to add all open source fonts to NPM to simplify using great fonts in 30 | our web projects. We’re currently maintaining 856 typeface packages 31 | including all typefaces on Google Fonts. 32 | 33 | If your favorite typeface isn’t published yet, [let us know](https://github.com/KyleAMathews/typefaces) 34 | and we’ll add it! 35 | -------------------------------------------------------------------------------- /src/client-src/assets/fonts/typeface-roboto/files-hash.json: -------------------------------------------------------------------------------- 1 | {"hash":"5da83aae9cc1b95ba5433e6a414145c8","updatedAt":"2018-01-24T02:15:56.414Z"} -------------------------------------------------------------------------------- /src/client-src/assets/fonts/typeface-roboto/files/roboto-latin-300.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HendrixString/adobe-cep-react-create/a9c776cb721e9c23cc02a770a3295c96f4006466/src/client-src/assets/fonts/typeface-roboto/files/roboto-latin-300.woff -------------------------------------------------------------------------------- /src/client-src/assets/fonts/typeface-roboto/files/roboto-latin-300.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HendrixString/adobe-cep-react-create/a9c776cb721e9c23cc02a770a3295c96f4006466/src/client-src/assets/fonts/typeface-roboto/files/roboto-latin-300.woff2 -------------------------------------------------------------------------------- /src/client-src/assets/fonts/typeface-roboto/files/roboto-latin-400.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HendrixString/adobe-cep-react-create/a9c776cb721e9c23cc02a770a3295c96f4006466/src/client-src/assets/fonts/typeface-roboto/files/roboto-latin-400.woff -------------------------------------------------------------------------------- /src/client-src/assets/fonts/typeface-roboto/files/roboto-latin-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HendrixString/adobe-cep-react-create/a9c776cb721e9c23cc02a770a3295c96f4006466/src/client-src/assets/fonts/typeface-roboto/files/roboto-latin-400.woff2 -------------------------------------------------------------------------------- /src/client-src/assets/fonts/typeface-roboto/files/roboto-latin-500.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HendrixString/adobe-cep-react-create/a9c776cb721e9c23cc02a770a3295c96f4006466/src/client-src/assets/fonts/typeface-roboto/files/roboto-latin-500.woff -------------------------------------------------------------------------------- /src/client-src/assets/fonts/typeface-roboto/files/roboto-latin-500.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HendrixString/adobe-cep-react-create/a9c776cb721e9c23cc02a770a3295c96f4006466/src/client-src/assets/fonts/typeface-roboto/files/roboto-latin-500.woff2 -------------------------------------------------------------------------------- /src/client-src/assets/fonts/typeface-roboto/index.css: -------------------------------------------------------------------------------- 1 | /* roboto-300normal - latin */ 2 | @font-face { 3 | font-family: 'Roboto'; 4 | font-style: normal; 5 | font-display: swap; 6 | font-weight: 300; 7 | src: 8 | local('Roboto Light '), 9 | local('Roboto-Light'), 10 | url('./files/roboto-latin-300.woff2') format('woff2'), /* Super Modern Browsers */ 11 | url('./files/roboto-latin-300.woff') format('woff'); /* Modern Browsers */ 12 | } 13 | 14 | /* roboto-400normal - latin */ 15 | @font-face { 16 | font-family: 'Roboto'; 17 | font-style: normal; 18 | font-display: swap; 19 | font-weight: 400; 20 | src: 21 | local('Roboto Regular '), 22 | local('Roboto-Regular'), 23 | url('./files/roboto-latin-400.woff2') format('woff2'), /* Super Modern Browsers */ 24 | url('./files/roboto-latin-400.woff') format('woff'); /* Modern Browsers */ 25 | } 26 | 27 | /* roboto-500normal - latin */ 28 | @font-face { 29 | font-family: 'Roboto'; 30 | font-style: normal; 31 | font-display: swap; 32 | font-weight: 500; 33 | src: 34 | local('Roboto Medium '), 35 | local('Roboto-Medium'), 36 | url('./files/roboto-latin-500.woff2') format('woff2'), /* Super Modern Browsers */ 37 | url('./files/roboto-latin-500.woff') format('woff'); /* Modern Browsers */ 38 | } 39 | -------------------------------------------------------------------------------- /src/client-src/assets/fonts/typeface-roboto/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "_from": "typeface-roboto", 3 | "_id": "typeface-roboto@0.0.54", 4 | "_inBundle": false, 5 | "_integrity": "sha512-sOFA1FXgP0gOgBYlS6irwq6hHYA370KE3dPlgYEJHL3PJd5X8gQE0RmL79ONif6fL5JZuGDj+rtOrFeOqz5IZQ==", 6 | "_location": "/typeface-roboto", 7 | "_phantomChildren": {}, 8 | "_requested": { 9 | "type": "tag", 10 | "registry": true, 11 | "raw": "typeface-roboto", 12 | "name": "typeface-roboto", 13 | "escapedName": "typeface-roboto", 14 | "rawSpec": "", 15 | "saveSpec": null, 16 | "fetchSpec": "latest" 17 | }, 18 | "_requiredBy": [ 19 | "#USER", 20 | "/" 21 | ], 22 | "_resolved": "https://registry.npmjs.org/typeface-roboto/-/typeface-roboto-0.0.54.tgz", 23 | "_shasum": "8f02c9a18d1cfa7f49381a6ff0d21ff061f38ad2", 24 | "_spec": "typeface-roboto", 25 | "_where": "/Users/tshalev/hendrix/sugar/plugin/cep/test2", 26 | "author": { 27 | "name": "Kyle Mathews", 28 | "email": "mathews.kyle@gmail.com" 29 | }, 30 | "bundleDependencies": false, 31 | "deprecated": false, 32 | "description": "Roboto typeface", 33 | "keywords": [ 34 | "typeface", 35 | "font", 36 | "font family", 37 | "google fonts", 38 | "roboto" 39 | ], 40 | "license": "MIT", 41 | "main": "index.css", 42 | "name": "typeface-roboto", 43 | "repository": { 44 | "type": "git", 45 | "url": "https://github.com/KyleAMathews/typefaces/tree/master/packages/roboto" 46 | }, 47 | "version": "0.0.54" 48 | } 49 | -------------------------------------------------------------------------------- /src/client-src/comps-bootstrap/AppBS.jsx: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Tomer Riko Shalev 3 | */ 4 | 5 | import React from 'react' 6 | 7 | import Home from './comps-bootstrap/Home.jsx' 8 | import Debug from './comps-bootstrap/Debug.jsx' 9 | import Config from './comps-bootstrap/Config.jsx' 10 | import Upload from './comps-bootstrap/Upload.jsx' 11 | import Navigator from './comps-bootstrap/Navigator.jsx' 12 | 13 | import session from './core/Session.js' 14 | 15 | /** 16 | * main app component 17 | * 18 | */ 19 | export default class App extends React.Component { 20 | 21 | constructor() { 22 | super() 23 | 24 | this.onExecutePlugin = this.onExecutePlugin.bind(this) 25 | 26 | // navigator data 27 | this.data = [ 28 | {title: 'Home', isActive: true, comp: }, 29 | {title: 'Upload', comp: }, 30 | {title: 'Config', comp: }, 31 | {title: 'Debug', comp: } 32 | ] 33 | 34 | } 35 | 36 | /** 37 | * execute the plugin 38 | * 39 | * @param {type} options description 40 | */ 41 | onExecutePlugin(options) { 42 | console.log('App:: onExecutePlugin') 43 | console.log(options) 44 | // here disable UI 45 | session.invokePlugin(options) 46 | // here enable ui 47 | } 48 | 49 | render() { 50 | 51 | return ( 52 |
53 | 54 |
55 | ) 56 | 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/client-src/comps-bootstrap/BrowseItem.jsx: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Tomer Riko Shalev 3 | */ 4 | 5 | import React from 'react' 6 | 7 | const INPUT_DEFAULT = "Choose folder" 8 | 9 | const lblStyle = { 10 | overflow:'hidden', 11 | display:'inline-block', 12 | textOverflow: 'ellipsis', 13 | whiteSpace: 'nowrap' 14 | } 15 | /** 16 | * general browse item 17 | * 18 | */ 19 | export default class BrowseItem extends React.Component { 20 | 21 | constructor() { 22 | super() 23 | 24 | this.onClick = this.onClick.bind(this) 25 | this.lblRef = React.createRef() 26 | } 27 | 28 | hello() { 29 | return 'hello from tomer' 30 | } 31 | 32 | 33 | /** 34 | * onClick - clicking the input 35 | * 36 | */ 37 | onClick(e) { 38 | console.log('BrowseItem::onClick') 39 | var path = 'path demo' 40 | 41 | // test if we are inside the adobe-cep runtime 42 | if(window.cep) { 43 | var result = window.cep.fs.showOpenDialog(false, true, 'Select a Folder', null, null) 44 | 45 | path = result.data 46 | } 47 | 48 | this.lblRef.current.innerHTML = path 49 | } 50 | 51 | /** 52 | * return path or undefined if it was not set 53 | * 54 | * @return {string|undefined} the path 55 | */ 56 | get path() { 57 | var raw = this.lblRef.current.innerHTML 58 | var isValid = raw.trim() !== INPUT_DEFAULT 59 | 60 | return isValid ? raw : undefined 61 | } 62 | 63 | render() { 64 | const {} = this.props; 65 | 66 | return ( 67 |
68 |
69 | 72 | 74 |
75 |
76 | ) 77 | 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /src/client-src/comps-bootstrap/Config.jsx: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Tomer Riko Shalev 3 | */ 4 | 5 | import React from 'react' 6 | 7 | export default class Config extends React.Component { 8 | 9 | render() { 10 | 11 | return ( 12 |
13 |

Config

14 |
15 | ) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/client-src/comps-bootstrap/Debug.jsx: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Tomer Riko Shalev 3 | */ 4 | 5 | import React from 'react' 6 | 7 | export default class Debug extends React.Component { 8 | 9 | render() { 10 | 11 | return ( 12 |
13 |

Debug

14 |
15 | ) 16 | 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/client-src/comps-bootstrap/Decal.jsx: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Tomer Riko Shalev 3 | */ 4 | 5 | import React from 'react' 6 | 7 | function colorToCss(color) { 8 | return { 9 | backgroundColor: color 10 | } 11 | } 12 | 13 | export default class Decal extends React.Component { 14 | 15 | render() { 16 | var {title, desc, imgSrc, color} = this.props 17 | 18 | return ( 19 |
21 | 23 |
24 |
{title}
25 | {desc} 26 |
27 |
28 | ) 29 | 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/client-src/comps-bootstrap/Home.jsx: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Tomer Riko Shalev 3 | */ 4 | 5 | import React from 'react' 6 | 7 | import Decal from './Decal.jsx' 8 | import PluginItem from './PluginItem.jsx' 9 | import BrowseItem from './BrowseItem.jsx' 10 | 11 | function colorToCss(color) { 12 | return { 13 | backgroundColor: color 14 | } 15 | } 16 | 17 | /** 18 | * Home tab content 19 | * 20 | */ 21 | export default class Home extends React.Component { 22 | 23 | constructor() { 24 | super() 25 | 26 | this.export_onClick = this.export_onClick.bind(this) 27 | this.textureItemRef = React.createRef() 28 | this.masksItemRef = React.createRef() 29 | this.infoItemRef = React.createRef() 30 | this.flattenItemRef = React.createRef() 31 | this.browseItemRef = React.createRef() 32 | } 33 | 34 | /** 35 | * export button was clicked 36 | * 37 | */ 38 | export_onClick(e) { 39 | var folderPath = this.browseItemRef.current.path 40 | var isTexturesChecked = this.textureItemRef.current.isChecked 41 | var isMasksChecked = this.masksItemRef.current.isChecked 42 | var isInfoChecked = this.infoItemRef.current.isChecked 43 | var isFlattenChecked = this.flattenItemRef.current.isChecked 44 | 45 | var { onExecutePlugin } = this.props 46 | 47 | onExecutePlugin({ 48 | folderPath, isTexturesChecked, isMasksChecked, isInfoChecked, isFlattenChecked 49 | }) 50 | } 51 | 52 | render() { 53 | return ( 54 |
55 | 57 | 58 |
59 |
configurations
60 | 61 | 64 | 67 | 70 | 73 | 74 | 75 | 76 | Export 77 | 78 |
79 | 80 |
81 | ) 82 | 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /src/client-src/comps-bootstrap/Navigator.jsx: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Tomer Riko Shalev 3 | */ 4 | 5 | import React from 'react' 6 | 7 | function colorToCss(color) { 8 | return { 9 | backgroundColor: color 10 | } 11 | } 12 | 13 | /** 14 | * react component for creating a bootstrap 4 tab navigator. 15 | * configurable with data. 16 | */ 17 | export default class Navigator extends React.Component { 18 | 19 | constructor() { 20 | super() 21 | } 22 | 23 | /** 24 | * tabFactory - create a tab item 25 | * 26 | * @param {type} data description 27 | * @return {type} description 28 | */ 29 | tabFactory(data, index) { 30 | var title = data.title.toLowerCase() 31 | var isActive = data.isActive ? true : false 32 | var className = `nav-item nav-link ${isActive ? 'active' : ''}` 33 | 34 | return {data.title} 37 | } 38 | 39 | /** 40 | * tabFactory - create a tab content container 41 | * 42 | * @param {type} data description 43 | * @return {type} description 44 | */ 45 | tabContentFactory(data, index) { 46 | var title = data.title.toLowerCase() 47 | var isActive = data.isActive ? true : false 48 | var className = `tab-pane fade ${isActive ? 'show active' : ''}` 49 | 50 | return ( 51 |
53 | {data.comp} 54 |
55 | ) 56 | } 57 | 58 | render() { 59 | const { data } = this.props 60 | console.log(data) 61 | 62 | return ( 63 |
64 | 69 | 72 |
73 | ) 74 | 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /src/client-src/comps-bootstrap/PluginItem.jsx: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Tomer Riko Shalev 3 | */ 4 | 5 | import React from 'react' 6 | 7 | var roundedStyle = { 8 | width: '32px', 9 | height: '32px', 10 | backgroundColor: 'rgb(69 122 251)' 11 | } 12 | 13 | function getStyle(color) { 14 | return { 15 | width: '32px', 16 | height: '32px', 17 | backgroundColor: color 18 | } 19 | } 20 | 21 | /** 22 | * general plugin item that lives inside thw Home content 23 | * 24 | */ 25 | export default class PluginItem extends React.Component { 26 | 27 | constructor() { 28 | super() 29 | 30 | this.cbRef = React.createRef() 31 | } 32 | 33 | /** 34 | * get isChecked 35 | * 36 | * @return {type} description 37 | */ 38 | get isChecked() { 39 | return this.cbRef.current.checked 40 | } 41 | 42 | set check(val) { 43 | this.cbRef.current.checked = val 44 | } 45 | 46 | render() { 47 | const { index, title, desc, color, checked } = this.props; 48 | var attr = {} 49 | var defaultChecked = checked==='true' ? true : false 50 | var checkId = `check${index}` 51 | var style = getStyle(color ? color : 'rgb(69, 122, 251)') 52 | 53 | return ( 54 |
55 |
56 |
57 |
58 | {title} 59 | 60 |
61 | 63 |
65 | 66 |
67 | {desc} 68 |
69 |
70 | ) 71 | 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/client-src/comps-bootstrap/Upload.jsx: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Tomer Riko Shalev 3 | */ 4 | 5 | import React from 'react' 6 | 7 | export default class Upload extends React.Component { 8 | 9 | render() { 10 | return ( 11 |
12 |

Upload

13 |
14 | ) 15 | 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/client-src/comps-bootstrap/backup/css/bootstrap-4.1.3-dist/css/bootstrap-grid.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Grid v4.1.3 (https://getbootstrap.com/) 3 | * Copyright 2011-2018 The Bootstrap Authors 4 | * Copyright 2011-2018 Twitter, Inc. 5 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 6 | */@-ms-viewport{width:device-width}html{box-sizing:border-box;-ms-overflow-style:scrollbar}*,::after,::before{box-sizing:inherit}.container{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:576px){.container{max-width:540px}}@media (min-width:768px){.container{max-width:720px}}@media (min-width:992px){.container{max-width:960px}}@media (min-width:1200px){.container{max-width:1140px}}.container-fluid{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-15px;margin-left:-15px}.no-gutters{margin-right:0;margin-left:0}.no-gutters>.col,.no-gutters>[class*=col-]{padding-right:0;padding-left:0}.col,.col-1,.col-10,.col-11,.col-12,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-auto,.col-lg,.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-auto,.col-md,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-auto,.col-sm,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-auto,.col-xl,.col-xl-1,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9,.col-xl-auto{position:relative;width:100%;min-height:1px;padding-right:15px;padding-left:15px}.col{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:none}.col-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-first{-ms-flex-order:-1;order:-1}.order-last{-ms-flex-order:13;order:13}.order-0{-ms-flex-order:0;order:0}.order-1{-ms-flex-order:1;order:1}.order-2{-ms-flex-order:2;order:2}.order-3{-ms-flex-order:3;order:3}.order-4{-ms-flex-order:4;order:4}.order-5{-ms-flex-order:5;order:5}.order-6{-ms-flex-order:6;order:6}.order-7{-ms-flex-order:7;order:7}.order-8{-ms-flex-order:8;order:8}.order-9{-ms-flex-order:9;order:9}.order-10{-ms-flex-order:10;order:10}.order-11{-ms-flex-order:11;order:11}.order-12{-ms-flex-order:12;order:12}.offset-1{margin-left:8.333333%}.offset-2{margin-left:16.666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.333333%}.offset-5{margin-left:41.666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.333333%}.offset-8{margin-left:66.666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.333333%}.offset-11{margin-left:91.666667%}@media (min-width:576px){.col-sm{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-sm-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:none}.col-sm-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-sm-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-sm-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-sm-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-sm-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-sm-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-sm-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-sm-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-sm-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-sm-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-sm-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-sm-first{-ms-flex-order:-1;order:-1}.order-sm-last{-ms-flex-order:13;order:13}.order-sm-0{-ms-flex-order:0;order:0}.order-sm-1{-ms-flex-order:1;order:1}.order-sm-2{-ms-flex-order:2;order:2}.order-sm-3{-ms-flex-order:3;order:3}.order-sm-4{-ms-flex-order:4;order:4}.order-sm-5{-ms-flex-order:5;order:5}.order-sm-6{-ms-flex-order:6;order:6}.order-sm-7{-ms-flex-order:7;order:7}.order-sm-8{-ms-flex-order:8;order:8}.order-sm-9{-ms-flex-order:9;order:9}.order-sm-10{-ms-flex-order:10;order:10}.order-sm-11{-ms-flex-order:11;order:11}.order-sm-12{-ms-flex-order:12;order:12}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.333333%}.offset-sm-2{margin-left:16.666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.333333%}.offset-sm-5{margin-left:41.666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.333333%}.offset-sm-8{margin-left:66.666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.333333%}.offset-sm-11{margin-left:91.666667%}}@media (min-width:768px){.col-md{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-md-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:none}.col-md-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-md-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-md-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-md-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-md-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-md-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-md-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-md-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-md-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-md-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-md-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-md-first{-ms-flex-order:-1;order:-1}.order-md-last{-ms-flex-order:13;order:13}.order-md-0{-ms-flex-order:0;order:0}.order-md-1{-ms-flex-order:1;order:1}.order-md-2{-ms-flex-order:2;order:2}.order-md-3{-ms-flex-order:3;order:3}.order-md-4{-ms-flex-order:4;order:4}.order-md-5{-ms-flex-order:5;order:5}.order-md-6{-ms-flex-order:6;order:6}.order-md-7{-ms-flex-order:7;order:7}.order-md-8{-ms-flex-order:8;order:8}.order-md-9{-ms-flex-order:9;order:9}.order-md-10{-ms-flex-order:10;order:10}.order-md-11{-ms-flex-order:11;order:11}.order-md-12{-ms-flex-order:12;order:12}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.333333%}.offset-md-2{margin-left:16.666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.333333%}.offset-md-5{margin-left:41.666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.333333%}.offset-md-8{margin-left:66.666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.333333%}.offset-md-11{margin-left:91.666667%}}@media (min-width:992px){.col-lg{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-lg-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:none}.col-lg-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-lg-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-lg-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-lg-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-lg-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-lg-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-lg-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-lg-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-lg-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-lg-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-lg-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-lg-first{-ms-flex-order:-1;order:-1}.order-lg-last{-ms-flex-order:13;order:13}.order-lg-0{-ms-flex-order:0;order:0}.order-lg-1{-ms-flex-order:1;order:1}.order-lg-2{-ms-flex-order:2;order:2}.order-lg-3{-ms-flex-order:3;order:3}.order-lg-4{-ms-flex-order:4;order:4}.order-lg-5{-ms-flex-order:5;order:5}.order-lg-6{-ms-flex-order:6;order:6}.order-lg-7{-ms-flex-order:7;order:7}.order-lg-8{-ms-flex-order:8;order:8}.order-lg-9{-ms-flex-order:9;order:9}.order-lg-10{-ms-flex-order:10;order:10}.order-lg-11{-ms-flex-order:11;order:11}.order-lg-12{-ms-flex-order:12;order:12}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.333333%}.offset-lg-2{margin-left:16.666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.333333%}.offset-lg-5{margin-left:41.666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.333333%}.offset-lg-8{margin-left:66.666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.333333%}.offset-lg-11{margin-left:91.666667%}}@media (min-width:1200px){.col-xl{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-xl-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:none}.col-xl-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-xl-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-xl-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-xl-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-xl-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-xl-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-xl-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-xl-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-xl-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-xl-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-xl-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-xl-first{-ms-flex-order:-1;order:-1}.order-xl-last{-ms-flex-order:13;order:13}.order-xl-0{-ms-flex-order:0;order:0}.order-xl-1{-ms-flex-order:1;order:1}.order-xl-2{-ms-flex-order:2;order:2}.order-xl-3{-ms-flex-order:3;order:3}.order-xl-4{-ms-flex-order:4;order:4}.order-xl-5{-ms-flex-order:5;order:5}.order-xl-6{-ms-flex-order:6;order:6}.order-xl-7{-ms-flex-order:7;order:7}.order-xl-8{-ms-flex-order:8;order:8}.order-xl-9{-ms-flex-order:9;order:9}.order-xl-10{-ms-flex-order:10;order:10}.order-xl-11{-ms-flex-order:11;order:11}.order-xl-12{-ms-flex-order:12;order:12}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.333333%}.offset-xl-2{margin-left:16.666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.333333%}.offset-xl-5{margin-left:41.666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.333333%}.offset-xl-8{margin-left:66.666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.333333%}.offset-xl-11{margin-left:91.666667%}}.d-none{display:none!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:-ms-flexbox!important;display:flex!important}.d-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}@media (min-width:576px){.d-sm-none{display:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:-ms-flexbox!important;display:flex!important}.d-sm-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:768px){.d-md-none{display:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:-ms-flexbox!important;display:flex!important}.d-md-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:992px){.d-lg-none{display:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:-ms-flexbox!important;display:flex!important}.d-lg-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:1200px){.d-xl-none{display:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:-ms-flexbox!important;display:flex!important}.d-xl-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media print{.d-print-none{display:none!important}.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:-ms-flexbox!important;display:flex!important}.d-print-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}.flex-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-center{-ms-flex-align:center!important;align-items:center!important}.align-items-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}@media (min-width:576px){.flex-sm-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-sm-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-sm-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-sm-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-sm-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-sm-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-sm-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-sm-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-sm-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-sm-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-sm-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-sm-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-sm-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-sm-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-sm-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-sm-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-sm-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-sm-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-sm-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-sm-center{-ms-flex-align:center!important;align-items:center!important}.align-items-sm-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-sm-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-sm-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-sm-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-sm-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-sm-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-sm-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-sm-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-sm-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-sm-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-sm-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-sm-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-sm-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-sm-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:768px){.flex-md-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-md-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-md-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-md-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-md-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-md-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-md-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-md-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-md-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-md-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-md-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-md-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-md-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-md-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-md-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-md-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-md-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-md-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-md-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-md-center{-ms-flex-align:center!important;align-items:center!important}.align-items-md-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-md-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-md-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-md-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-md-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-md-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-md-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-md-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-md-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-md-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-md-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-md-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-md-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-md-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:992px){.flex-lg-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-lg-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-lg-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-lg-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-lg-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-lg-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-lg-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-lg-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-lg-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-lg-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-lg-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-lg-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-lg-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-lg-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-lg-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-lg-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-lg-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-lg-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-lg-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-lg-center{-ms-flex-align:center!important;align-items:center!important}.align-items-lg-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-lg-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-lg-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-lg-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-lg-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-lg-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-lg-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-lg-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-lg-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-lg-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-lg-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-lg-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-lg-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-lg-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:1200px){.flex-xl-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-xl-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-xl-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-xl-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-xl-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-xl-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-xl-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-xl-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-xl-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-xl-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-xl-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-xl-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-xl-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-xl-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-xl-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-xl-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-xl-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-xl-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-xl-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-xl-center{-ms-flex-align:center!important;align-items:center!important}.align-items-xl-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-xl-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-xl-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-xl-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-xl-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-xl-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-xl-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-xl-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-xl-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-xl-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-xl-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-xl-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-xl-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-xl-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}} 7 | /*# sourceMappingURL=bootstrap-grid.min.css.map */ -------------------------------------------------------------------------------- /src/client-src/comps-bootstrap/backup/css/bootstrap-4.1.3-dist/css/bootstrap-reboot.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Reboot v4.1.3 (https://getbootstrap.com/) 3 | * Copyright 2011-2018 The Bootstrap Authors 4 | * Copyright 2011-2018 Twitter, Inc. 5 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 6 | * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md) 7 | */ 8 | *, 9 | *::before, 10 | *::after { 11 | box-sizing: border-box; 12 | } 13 | 14 | html { 15 | font-family: sans-serif; 16 | line-height: 1.15; 17 | -webkit-text-size-adjust: 100%; 18 | -ms-text-size-adjust: 100%; 19 | -ms-overflow-style: scrollbar; 20 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 21 | } 22 | 23 | @-ms-viewport { 24 | width: device-width; 25 | } 26 | 27 | article, aside, figcaption, figure, footer, header, hgroup, main, nav, section { 28 | display: block; 29 | } 30 | 31 | body { 32 | margin: 0; 33 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; 34 | font-size: 1rem; 35 | font-weight: 400; 36 | line-height: 1.5; 37 | color: #212529; 38 | text-align: left; 39 | background-color: #fff; 40 | } 41 | 42 | [tabindex="-1"]:focus { 43 | outline: 0 !important; 44 | } 45 | 46 | hr { 47 | box-sizing: content-box; 48 | height: 0; 49 | overflow: visible; 50 | } 51 | 52 | h1, h2, h3, h4, h5, h6 { 53 | margin-top: 0; 54 | margin-bottom: 0.5rem; 55 | } 56 | 57 | p { 58 | margin-top: 0; 59 | margin-bottom: 1rem; 60 | } 61 | 62 | abbr[title], 63 | abbr[data-original-title] { 64 | text-decoration: underline; 65 | -webkit-text-decoration: underline dotted; 66 | text-decoration: underline dotted; 67 | cursor: help; 68 | border-bottom: 0; 69 | } 70 | 71 | address { 72 | margin-bottom: 1rem; 73 | font-style: normal; 74 | line-height: inherit; 75 | } 76 | 77 | ol, 78 | ul, 79 | dl { 80 | margin-top: 0; 81 | margin-bottom: 1rem; 82 | } 83 | 84 | ol ol, 85 | ul ul, 86 | ol ul, 87 | ul ol { 88 | margin-bottom: 0; 89 | } 90 | 91 | dt { 92 | font-weight: 700; 93 | } 94 | 95 | dd { 96 | margin-bottom: .5rem; 97 | margin-left: 0; 98 | } 99 | 100 | blockquote { 101 | margin: 0 0 1rem; 102 | } 103 | 104 | dfn { 105 | font-style: italic; 106 | } 107 | 108 | b, 109 | strong { 110 | font-weight: bolder; 111 | } 112 | 113 | small { 114 | font-size: 80%; 115 | } 116 | 117 | sub, 118 | sup { 119 | position: relative; 120 | font-size: 75%; 121 | line-height: 0; 122 | vertical-align: baseline; 123 | } 124 | 125 | sub { 126 | bottom: -.25em; 127 | } 128 | 129 | sup { 130 | top: -.5em; 131 | } 132 | 133 | a { 134 | color: #007bff; 135 | text-decoration: none; 136 | background-color: transparent; 137 | -webkit-text-decoration-skip: objects; 138 | } 139 | 140 | a:hover { 141 | color: #0056b3; 142 | text-decoration: underline; 143 | } 144 | 145 | a:not([href]):not([tabindex]) { 146 | color: inherit; 147 | text-decoration: none; 148 | } 149 | 150 | a:not([href]):not([tabindex]):hover, a:not([href]):not([tabindex]):focus { 151 | color: inherit; 152 | text-decoration: none; 153 | } 154 | 155 | a:not([href]):not([tabindex]):focus { 156 | outline: 0; 157 | } 158 | 159 | pre, 160 | code, 161 | kbd, 162 | samp { 163 | font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; 164 | font-size: 1em; 165 | } 166 | 167 | pre { 168 | margin-top: 0; 169 | margin-bottom: 1rem; 170 | overflow: auto; 171 | -ms-overflow-style: scrollbar; 172 | } 173 | 174 | figure { 175 | margin: 0 0 1rem; 176 | } 177 | 178 | img { 179 | vertical-align: middle; 180 | border-style: none; 181 | } 182 | 183 | svg { 184 | overflow: hidden; 185 | vertical-align: middle; 186 | } 187 | 188 | table { 189 | border-collapse: collapse; 190 | } 191 | 192 | caption { 193 | padding-top: 0.75rem; 194 | padding-bottom: 0.75rem; 195 | color: #6c757d; 196 | text-align: left; 197 | caption-side: bottom; 198 | } 199 | 200 | th { 201 | text-align: inherit; 202 | } 203 | 204 | label { 205 | display: inline-block; 206 | margin-bottom: 0.5rem; 207 | } 208 | 209 | button { 210 | border-radius: 0; 211 | } 212 | 213 | button:focus { 214 | outline: 1px dotted; 215 | outline: 5px auto -webkit-focus-ring-color; 216 | } 217 | 218 | input, 219 | button, 220 | select, 221 | optgroup, 222 | textarea { 223 | margin: 0; 224 | font-family: inherit; 225 | font-size: inherit; 226 | line-height: inherit; 227 | } 228 | 229 | button, 230 | input { 231 | overflow: visible; 232 | } 233 | 234 | button, 235 | select { 236 | text-transform: none; 237 | } 238 | 239 | button, 240 | html [type="button"], 241 | [type="reset"], 242 | [type="submit"] { 243 | -webkit-appearance: button; 244 | } 245 | 246 | button::-moz-focus-inner, 247 | [type="button"]::-moz-focus-inner, 248 | [type="reset"]::-moz-focus-inner, 249 | [type="submit"]::-moz-focus-inner { 250 | padding: 0; 251 | border-style: none; 252 | } 253 | 254 | input[type="radio"], 255 | input[type="checkbox"] { 256 | box-sizing: border-box; 257 | padding: 0; 258 | } 259 | 260 | input[type="date"], 261 | input[type="time"], 262 | input[type="datetime-local"], 263 | input[type="month"] { 264 | -webkit-appearance: listbox; 265 | } 266 | 267 | textarea { 268 | overflow: auto; 269 | resize: vertical; 270 | } 271 | 272 | fieldset { 273 | min-width: 0; 274 | padding: 0; 275 | margin: 0; 276 | border: 0; 277 | } 278 | 279 | legend { 280 | display: block; 281 | width: 100%; 282 | max-width: 100%; 283 | padding: 0; 284 | margin-bottom: .5rem; 285 | font-size: 1.5rem; 286 | line-height: inherit; 287 | color: inherit; 288 | white-space: normal; 289 | } 290 | 291 | progress { 292 | vertical-align: baseline; 293 | } 294 | 295 | [type="number"]::-webkit-inner-spin-button, 296 | [type="number"]::-webkit-outer-spin-button { 297 | height: auto; 298 | } 299 | 300 | [type="search"] { 301 | outline-offset: -2px; 302 | -webkit-appearance: none; 303 | } 304 | 305 | [type="search"]::-webkit-search-cancel-button, 306 | [type="search"]::-webkit-search-decoration { 307 | -webkit-appearance: none; 308 | } 309 | 310 | ::-webkit-file-upload-button { 311 | font: inherit; 312 | -webkit-appearance: button; 313 | } 314 | 315 | output { 316 | display: inline-block; 317 | } 318 | 319 | summary { 320 | display: list-item; 321 | cursor: pointer; 322 | } 323 | 324 | template { 325 | display: none; 326 | } 327 | 328 | [hidden] { 329 | display: none !important; 330 | } 331 | /*# sourceMappingURL=bootstrap-reboot.css.map */ -------------------------------------------------------------------------------- /src/client-src/comps-bootstrap/backup/css/bootstrap-4.1.3-dist/css/bootstrap-reboot.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Reboot v4.1.3 (https://getbootstrap.com/) 3 | * Copyright 2011-2018 The Bootstrap Authors 4 | * Copyright 2011-2018 Twitter, Inc. 5 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 6 | * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md) 7 | */*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;-ms-overflow-style:scrollbar;-webkit-tap-highlight-color:transparent}@-ms-viewport{width:device-width}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus{outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}dfn{font-style:italic}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent;-webkit-text-decoration-skip:objects}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([tabindex]){color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus,a:not([href]):not([tabindex]):hover{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto;-ms-overflow-style:scrollbar}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important} 8 | /*# sourceMappingURL=bootstrap-reboot.min.css.map */ -------------------------------------------------------------------------------- /src/client-src/comps-bootstrap/backup/css/bootstrap-4.1.3-dist/css/bootstrap-reboot.min.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["../../scss/bootstrap-reboot.scss","../../scss/_reboot.scss","dist/css/bootstrap-reboot.css","bootstrap-reboot.css","../../scss/mixins/_hover.scss"],"names":[],"mappings":"AAAA;;;;;;ACoBA,ECXA,QADA,SDeE,WAAA,WAGF,KACE,YAAA,WACA,YAAA,KACA,yBAAA,KACA,qBAAA,KACA,mBAAA,UACA,4BAAA,YAKA,cACE,MAAA,aAMJ,QAAA,MAAA,WAAA,OAAA,OAAA,OAAA,OAAA,KAAA,IAAA,QACE,QAAA,MAWF,KACE,OAAA,EACA,YAAA,aAAA,CAAA,kBAAA,CAAA,UAAA,CAAA,MAAA,CAAA,gBAAA,CAAA,KAAA,CAAA,UAAA,CAAA,mBAAA,CAAA,gBAAA,CAAA,iBAAA,CAAA,mBACA,UAAA,KACA,YAAA,IACA,YAAA,IACA,MAAA,QACA,WAAA,KACA,iBAAA,KEvBF,sBFgCE,QAAA,YASF,GACE,WAAA,YACA,OAAA,EACA,SAAA,QAaF,GAAA,GAAA,GAAA,GAAA,GAAA,GACE,WAAA,EACA,cAAA,MAQF,EACE,WAAA,EACA,cAAA,KChDF,0BD0DA,YAEE,gBAAA,UACA,wBAAA,UAAA,OAAA,gBAAA,UAAA,OACA,OAAA,KACA,cAAA,EAGF,QACE,cAAA,KACA,WAAA,OACA,YAAA,QCrDF,GDwDA,GCzDA,GD4DE,WAAA,EACA,cAAA,KAGF,MCxDA,MACA,MAFA,MD6DE,cAAA,EAGF,GACE,YAAA,IAGF,GACE,cAAA,MACA,YAAA,EAGF,WACE,OAAA,EAAA,EAAA,KAGF,IACE,WAAA,OAIF,EC1DA,OD4DE,YAAA,OAIF,MACE,UAAA,IAQF,IChEA,IDkEE,SAAA,SACA,UAAA,IACA,YAAA,EACA,eAAA,SAGF,IAAM,OAAA,OACN,IAAM,IAAA,MAON,EACE,MAAA,QACA,gBAAA,KACA,iBAAA,YACA,6BAAA,QG7LA,QHgME,MAAA,QACA,gBAAA,UAUJ,8BACE,MAAA,QACA,gBAAA,KGzMA,oCAAA,oCH4ME,MAAA,QACA,gBAAA,KANJ,oCAUI,QAAA,EClEJ,KACA,ID0EA,ICzEA,KD6EE,YAAA,cAAA,CAAA,KAAA,CAAA,MAAA,CAAA,QAAA,CAAA,iBAAA,CAAA,aAAA,CAAA,UACA,UAAA,IAGF,IAEE,WAAA,EAEA,cAAA,KAEA,SAAA,KAGA,mBAAA,UAQF,OAEE,OAAA,EAAA,EAAA,KAQF,IACE,eAAA,OACA,aAAA,KAGF,IAGE,SAAA,OACA,eAAA,OAQF,MACE,gBAAA,SAGF,QACE,YAAA,OACA,eAAA,OACA,MAAA,QACA,WAAA,KACA,aAAA,OAGF,GAGE,WAAA,QAQF,MAEE,QAAA,aACA,cAAA,MAMF,OACE,cAAA,EAOF,aACE,QAAA,IAAA,OACA,QAAA,IAAA,KAAA,yBC9GF,ODiHA,MC/GA,SADA,OAEA,SDmHE,OAAA,EACA,YAAA,QACA,UAAA,QACA,YAAA,QAGF,OCjHA,MDmHE,SAAA,QAGF,OCjHA,ODmHE,eAAA,KC7GF,aACA,cDkHA,OCpHA,mBDwHE,mBAAA,OCjHF,gCACA,+BACA,gCDmHA,yBAIE,QAAA,EACA,aAAA,KClHF,qBDqHA,kBAEE,WAAA,WACA,QAAA,EAIF,iBCrHA,2BACA,kBAFA,iBD+HE,mBAAA,QAGF,SACE,SAAA,KAEA,OAAA,SAGF,SAME,UAAA,EAEA,QAAA,EACA,OAAA,EACA,OAAA,EAKF,OACE,QAAA,MACA,MAAA,KACA,UAAA,KACA,QAAA,EACA,cAAA,MACA,UAAA,OACA,YAAA,QACA,MAAA,QACA,YAAA,OAGF,SACE,eAAA,SEnIF,yCDEA,yCDuIE,OAAA,KEpIF,cF4IE,eAAA,KACA,mBAAA,KExIF,4CDEA,yCD+IE,mBAAA,KAQF,6BACE,KAAA,QACA,mBAAA,OAOF,OACE,QAAA,aAGF,QACE,QAAA,UACA,OAAA,QAGF,SACE,QAAA,KErJF,SF2JE,QAAA","sourcesContent":["/*!\n * Bootstrap Reboot v4.1.3 (https://getbootstrap.com/)\n * Copyright 2011-2018 The Bootstrap Authors\n * Copyright 2011-2018 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)\n */\n\n@import \"functions\";\n@import \"variables\";\n@import \"mixins\";\n@import \"reboot\";\n","// stylelint-disable at-rule-no-vendor-prefix, declaration-no-important, selector-no-qualifying-type, property-no-vendor-prefix\n\n// Reboot\n//\n// Normalization of HTML elements, manually forked from Normalize.css to remove\n// styles targeting irrelevant browsers while applying new styles.\n//\n// Normalize is licensed MIT. https://github.com/necolas/normalize.css\n\n\n// Document\n//\n// 1. Change from `box-sizing: content-box` so that `width` is not affected by `padding` or `border`.\n// 2. Change the default font family in all browsers.\n// 3. Correct the line height in all browsers.\n// 4. Prevent adjustments of font size after orientation changes in IE on Windows Phone and in iOS.\n// 5. Setting @viewport causes scrollbars to overlap content in IE11 and Edge, so\n// we force a non-overlapping, non-auto-hiding scrollbar to counteract.\n// 6. Change the default tap highlight to be completely transparent in iOS.\n\n*,\n*::before,\n*::after {\n box-sizing: border-box; // 1\n}\n\nhtml {\n font-family: sans-serif; // 2\n line-height: 1.15; // 3\n -webkit-text-size-adjust: 100%; // 4\n -ms-text-size-adjust: 100%; // 4\n -ms-overflow-style: scrollbar; // 5\n -webkit-tap-highlight-color: rgba($black, 0); // 6\n}\n\n// IE10+ doesn't honor `` in some cases.\n@at-root {\n @-ms-viewport {\n width: device-width;\n }\n}\n\n// stylelint-disable selector-list-comma-newline-after\n// Shim for \"new\" HTML5 structural elements to display correctly (IE10, older browsers)\narticle, aside, figcaption, figure, footer, header, hgroup, main, nav, section {\n display: block;\n}\n// stylelint-enable selector-list-comma-newline-after\n\n// Body\n//\n// 1. Remove the margin in all browsers.\n// 2. As a best practice, apply a default `background-color`.\n// 3. Set an explicit initial text-align value so that we can later use the\n// the `inherit` value on things like `` elements.\n\nbody {\n margin: 0; // 1\n font-family: $font-family-base;\n font-size: $font-size-base;\n font-weight: $font-weight-base;\n line-height: $line-height-base;\n color: $body-color;\n text-align: left; // 3\n background-color: $body-bg; // 2\n}\n\n// Suppress the focus outline on elements that cannot be accessed via keyboard.\n// This prevents an unwanted focus outline from appearing around elements that\n// might still respond to pointer events.\n//\n// Credit: https://github.com/suitcss/base\n[tabindex=\"-1\"]:focus {\n outline: 0 !important;\n}\n\n\n// Content grouping\n//\n// 1. Add the correct box sizing in Firefox.\n// 2. Show the overflow in Edge and IE.\n\nhr {\n box-sizing: content-box; // 1\n height: 0; // 1\n overflow: visible; // 2\n}\n\n\n//\n// Typography\n//\n\n// Remove top margins from headings\n//\n// By default, `

`-`

` all receive top and bottom margins. We nuke the top\n// margin for easier control within type scales as it avoids margin collapsing.\n// stylelint-disable selector-list-comma-newline-after\nh1, h2, h3, h4, h5, h6 {\n margin-top: 0;\n margin-bottom: $headings-margin-bottom;\n}\n// stylelint-enable selector-list-comma-newline-after\n\n// Reset margins on paragraphs\n//\n// Similarly, the top margin on `

`s get reset. However, we also reset the\n// bottom margin to use `rem` units instead of `em`.\np {\n margin-top: 0;\n margin-bottom: $paragraph-margin-bottom;\n}\n\n// Abbreviations\n//\n// 1. Remove the bottom border in Firefox 39-.\n// 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.\n// 3. Add explicit cursor to indicate changed behavior.\n// 4. Duplicate behavior to the data-* attribute for our tooltip plugin\n\nabbr[title],\nabbr[data-original-title] { // 4\n text-decoration: underline; // 2\n text-decoration: underline dotted; // 2\n cursor: help; // 3\n border-bottom: 0; // 1\n}\n\naddress {\n margin-bottom: 1rem;\n font-style: normal;\n line-height: inherit;\n}\n\nol,\nul,\ndl {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nol ol,\nul ul,\nol ul,\nul ol {\n margin-bottom: 0;\n}\n\ndt {\n font-weight: $dt-font-weight;\n}\n\ndd {\n margin-bottom: .5rem;\n margin-left: 0; // Undo browser default\n}\n\nblockquote {\n margin: 0 0 1rem;\n}\n\ndfn {\n font-style: italic; // Add the correct font style in Android 4.3-\n}\n\n// stylelint-disable font-weight-notation\nb,\nstrong {\n font-weight: bolder; // Add the correct font weight in Chrome, Edge, and Safari\n}\n// stylelint-enable font-weight-notation\n\nsmall {\n font-size: 80%; // Add the correct font size in all browsers\n}\n\n//\n// Prevent `sub` and `sup` elements from affecting the line height in\n// all browsers.\n//\n\nsub,\nsup {\n position: relative;\n font-size: 75%;\n line-height: 0;\n vertical-align: baseline;\n}\n\nsub { bottom: -.25em; }\nsup { top: -.5em; }\n\n\n//\n// Links\n//\n\na {\n color: $link-color;\n text-decoration: $link-decoration;\n background-color: transparent; // Remove the gray background on active links in IE 10.\n -webkit-text-decoration-skip: objects; // Remove gaps in links underline in iOS 8+ and Safari 8+.\n\n @include hover {\n color: $link-hover-color;\n text-decoration: $link-hover-decoration;\n }\n}\n\n// And undo these styles for placeholder links/named anchors (without href)\n// which have not been made explicitly keyboard-focusable (without tabindex).\n// It would be more straightforward to just use a[href] in previous block, but that\n// causes specificity issues in many other styles that are too complex to fix.\n// See https://github.com/twbs/bootstrap/issues/19402\n\na:not([href]):not([tabindex]) {\n color: inherit;\n text-decoration: none;\n\n @include hover-focus {\n color: inherit;\n text-decoration: none;\n }\n\n &:focus {\n outline: 0;\n }\n}\n\n\n//\n// Code\n//\n\npre,\ncode,\nkbd,\nsamp {\n font-family: $font-family-monospace;\n font-size: 1em; // Correct the odd `em` font sizing in all browsers.\n}\n\npre {\n // Remove browser default top margin\n margin-top: 0;\n // Reset browser default of `1em` to use `rem`s\n margin-bottom: 1rem;\n // Don't allow content to break outside\n overflow: auto;\n // We have @viewport set which causes scrollbars to overlap content in IE11 and Edge, so\n // we force a non-overlapping, non-auto-hiding scrollbar to counteract.\n -ms-overflow-style: scrollbar;\n}\n\n\n//\n// Figures\n//\n\nfigure {\n // Apply a consistent margin strategy (matches our type styles).\n margin: 0 0 1rem;\n}\n\n\n//\n// Images and content\n//\n\nimg {\n vertical-align: middle;\n border-style: none; // Remove the border on images inside links in IE 10-.\n}\n\nsvg {\n // Workaround for the SVG overflow bug in IE10/11 is still required.\n // See https://github.com/twbs/bootstrap/issues/26878\n overflow: hidden;\n vertical-align: middle;\n}\n\n\n//\n// Tables\n//\n\ntable {\n border-collapse: collapse; // Prevent double borders\n}\n\ncaption {\n padding-top: $table-cell-padding;\n padding-bottom: $table-cell-padding;\n color: $table-caption-color;\n text-align: left;\n caption-side: bottom;\n}\n\nth {\n // Matches default `` alignment by inheriting from the ``, or the\n // closest parent with a set `text-align`.\n text-align: inherit;\n}\n\n\n//\n// Forms\n//\n\nlabel {\n // Allow labels to use `margin` for spacing.\n display: inline-block;\n margin-bottom: $label-margin-bottom;\n}\n\n// Remove the default `border-radius` that macOS Chrome adds.\n//\n// Details at https://github.com/twbs/bootstrap/issues/24093\nbutton {\n border-radius: 0;\n}\n\n// Work around a Firefox/IE bug where the transparent `button` background\n// results in a loss of the default `button` focus styles.\n//\n// Credit: https://github.com/suitcss/base/\nbutton:focus {\n outline: 1px dotted;\n outline: 5px auto -webkit-focus-ring-color;\n}\n\ninput,\nbutton,\nselect,\noptgroup,\ntextarea {\n margin: 0; // Remove the margin in Firefox and Safari\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n}\n\nbutton,\ninput {\n overflow: visible; // Show the overflow in Edge\n}\n\nbutton,\nselect {\n text-transform: none; // Remove the inheritance of text transform in Firefox\n}\n\n// 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`\n// controls in Android 4.\n// 2. Correct the inability to style clickable types in iOS and Safari.\nbutton,\nhtml [type=\"button\"], // 1\n[type=\"reset\"],\n[type=\"submit\"] {\n -webkit-appearance: button; // 2\n}\n\n// Remove inner border and padding from Firefox, but don't restore the outline like Normalize.\nbutton::-moz-focus-inner,\n[type=\"button\"]::-moz-focus-inner,\n[type=\"reset\"]::-moz-focus-inner,\n[type=\"submit\"]::-moz-focus-inner {\n padding: 0;\n border-style: none;\n}\n\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n box-sizing: border-box; // 1. Add the correct box sizing in IE 10-\n padding: 0; // 2. Remove the padding in IE 10-\n}\n\n\ninput[type=\"date\"],\ninput[type=\"time\"],\ninput[type=\"datetime-local\"],\ninput[type=\"month\"] {\n // Remove the default appearance of temporal inputs to avoid a Mobile Safari\n // bug where setting a custom line-height prevents text from being vertically\n // centered within the input.\n // See https://bugs.webkit.org/show_bug.cgi?id=139848\n // and https://github.com/twbs/bootstrap/issues/11266\n -webkit-appearance: listbox;\n}\n\ntextarea {\n overflow: auto; // Remove the default vertical scrollbar in IE.\n // Textareas should really only resize vertically so they don't break their (horizontal) containers.\n resize: vertical;\n}\n\nfieldset {\n // Browsers set a default `min-width: min-content;` on fieldsets,\n // unlike e.g. `

`s, which have `min-width: 0;` by default.\n // So we reset that to ensure fieldsets behave more like a standard block element.\n // See https://github.com/twbs/bootstrap/issues/12359\n // and https://html.spec.whatwg.org/multipage/#the-fieldset-and-legend-elements\n min-width: 0;\n // Reset the default outline behavior of fieldsets so they don't affect page layout.\n padding: 0;\n margin: 0;\n border: 0;\n}\n\n// 1. Correct the text wrapping in Edge and IE.\n// 2. Correct the color inheritance from `fieldset` elements in IE.\nlegend {\n display: block;\n width: 100%;\n max-width: 100%; // 1\n padding: 0;\n margin-bottom: .5rem;\n font-size: 1.5rem;\n line-height: inherit;\n color: inherit; // 2\n white-space: normal; // 1\n}\n\nprogress {\n vertical-align: baseline; // Add the correct vertical alignment in Chrome, Firefox, and Opera.\n}\n\n// Correct the cursor style of increment and decrement buttons in Chrome.\n[type=\"number\"]::-webkit-inner-spin-button,\n[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n[type=\"search\"] {\n // This overrides the extra rounded corners on search inputs in iOS so that our\n // `.form-control` class can properly style them. Note that this cannot simply\n // be added to `.form-control` as it's not specific enough. For details, see\n // https://github.com/twbs/bootstrap/issues/11586.\n outline-offset: -2px; // 2. Correct the outline style in Safari.\n -webkit-appearance: none;\n}\n\n//\n// Remove the inner padding and cancel buttons in Chrome and Safari on macOS.\n//\n\n[type=\"search\"]::-webkit-search-cancel-button,\n[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n//\n// 1. Correct the inability to style clickable types in iOS and Safari.\n// 2. Change font properties to `inherit` in Safari.\n//\n\n::-webkit-file-upload-button {\n font: inherit; // 2\n -webkit-appearance: button; // 1\n}\n\n//\n// Correct element displays\n//\n\noutput {\n display: inline-block;\n}\n\nsummary {\n display: list-item; // Add the correct display in all browsers\n cursor: pointer;\n}\n\ntemplate {\n display: none; // Add the correct display in IE\n}\n\n// Always hide an element with the `hidden` HTML attribute (from PureCSS).\n// Needed for proper display in IE 10-.\n[hidden] {\n display: none !important;\n}\n","/*!\n * Bootstrap Reboot v4.1.3 (https://getbootstrap.com/)\n * Copyright 2011-2018 The Bootstrap Authors\n * Copyright 2011-2018 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)\n */\n*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\nhtml {\n font-family: sans-serif;\n line-height: 1.15;\n -webkit-text-size-adjust: 100%;\n -ms-text-size-adjust: 100%;\n -ms-overflow-style: scrollbar;\n -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n}\n\n@-ms-viewport {\n width: device-width;\n}\n\narticle, aside, figcaption, figure, footer, header, hgroup, main, nav, section {\n display: block;\n}\n\nbody {\n margin: 0;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\";\n font-size: 1rem;\n font-weight: 400;\n line-height: 1.5;\n color: #212529;\n text-align: left;\n background-color: #fff;\n}\n\n[tabindex=\"-1\"]:focus {\n outline: 0 !important;\n}\n\nhr {\n box-sizing: content-box;\n height: 0;\n overflow: visible;\n}\n\nh1, h2, h3, h4, h5, h6 {\n margin-top: 0;\n margin-bottom: 0.5rem;\n}\n\np {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nabbr[title],\nabbr[data-original-title] {\n text-decoration: underline;\n -webkit-text-decoration: underline dotted;\n text-decoration: underline dotted;\n cursor: help;\n border-bottom: 0;\n}\n\naddress {\n margin-bottom: 1rem;\n font-style: normal;\n line-height: inherit;\n}\n\nol,\nul,\ndl {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nol ol,\nul ul,\nol ul,\nul ol {\n margin-bottom: 0;\n}\n\ndt {\n font-weight: 700;\n}\n\ndd {\n margin-bottom: .5rem;\n margin-left: 0;\n}\n\nblockquote {\n margin: 0 0 1rem;\n}\n\ndfn {\n font-style: italic;\n}\n\nb,\nstrong {\n font-weight: bolder;\n}\n\nsmall {\n font-size: 80%;\n}\n\nsub,\nsup {\n position: relative;\n font-size: 75%;\n line-height: 0;\n vertical-align: baseline;\n}\n\nsub {\n bottom: -.25em;\n}\n\nsup {\n top: -.5em;\n}\n\na {\n color: #007bff;\n text-decoration: none;\n background-color: transparent;\n -webkit-text-decoration-skip: objects;\n}\n\na:hover {\n color: #0056b3;\n text-decoration: underline;\n}\n\na:not([href]):not([tabindex]) {\n color: inherit;\n text-decoration: none;\n}\n\na:not([href]):not([tabindex]):hover, a:not([href]):not([tabindex]):focus {\n color: inherit;\n text-decoration: none;\n}\n\na:not([href]):not([tabindex]):focus {\n outline: 0;\n}\n\npre,\ncode,\nkbd,\nsamp {\n font-family: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n font-size: 1em;\n}\n\npre {\n margin-top: 0;\n margin-bottom: 1rem;\n overflow: auto;\n -ms-overflow-style: scrollbar;\n}\n\nfigure {\n margin: 0 0 1rem;\n}\n\nimg {\n vertical-align: middle;\n border-style: none;\n}\n\nsvg {\n overflow: hidden;\n vertical-align: middle;\n}\n\ntable {\n border-collapse: collapse;\n}\n\ncaption {\n padding-top: 0.75rem;\n padding-bottom: 0.75rem;\n color: #6c757d;\n text-align: left;\n caption-side: bottom;\n}\n\nth {\n text-align: inherit;\n}\n\nlabel {\n display: inline-block;\n margin-bottom: 0.5rem;\n}\n\nbutton {\n border-radius: 0;\n}\n\nbutton:focus {\n outline: 1px dotted;\n outline: 5px auto -webkit-focus-ring-color;\n}\n\ninput,\nbutton,\nselect,\noptgroup,\ntextarea {\n margin: 0;\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n}\n\nbutton,\ninput {\n overflow: visible;\n}\n\nbutton,\nselect {\n text-transform: none;\n}\n\nbutton,\nhtml [type=\"button\"],\n[type=\"reset\"],\n[type=\"submit\"] {\n -webkit-appearance: button;\n}\n\nbutton::-moz-focus-inner,\n[type=\"button\"]::-moz-focus-inner,\n[type=\"reset\"]::-moz-focus-inner,\n[type=\"submit\"]::-moz-focus-inner {\n padding: 0;\n border-style: none;\n}\n\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n box-sizing: border-box;\n padding: 0;\n}\n\ninput[type=\"date\"],\ninput[type=\"time\"],\ninput[type=\"datetime-local\"],\ninput[type=\"month\"] {\n -webkit-appearance: listbox;\n}\n\ntextarea {\n overflow: auto;\n resize: vertical;\n}\n\nfieldset {\n min-width: 0;\n padding: 0;\n margin: 0;\n border: 0;\n}\n\nlegend {\n display: block;\n width: 100%;\n max-width: 100%;\n padding: 0;\n margin-bottom: .5rem;\n font-size: 1.5rem;\n line-height: inherit;\n color: inherit;\n white-space: normal;\n}\n\nprogress {\n vertical-align: baseline;\n}\n\n[type=\"number\"]::-webkit-inner-spin-button,\n[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n[type=\"search\"] {\n outline-offset: -2px;\n -webkit-appearance: none;\n}\n\n[type=\"search\"]::-webkit-search-cancel-button,\n[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n::-webkit-file-upload-button {\n font: inherit;\n -webkit-appearance: button;\n}\n\noutput {\n display: inline-block;\n}\n\nsummary {\n display: list-item;\n cursor: pointer;\n}\n\ntemplate {\n display: none;\n}\n\n[hidden] {\n display: none !important;\n}\n/*# sourceMappingURL=bootstrap-reboot.css.map */","/*!\n * Bootstrap Reboot v4.1.3 (https://getbootstrap.com/)\n * Copyright 2011-2018 The Bootstrap Authors\n * Copyright 2011-2018 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)\n */\n*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\nhtml {\n font-family: sans-serif;\n line-height: 1.15;\n -webkit-text-size-adjust: 100%;\n -ms-text-size-adjust: 100%;\n -ms-overflow-style: scrollbar;\n -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n}\n\n@-ms-viewport {\n width: device-width;\n}\n\narticle, aside, figcaption, figure, footer, header, hgroup, main, nav, section {\n display: block;\n}\n\nbody {\n margin: 0;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\";\n font-size: 1rem;\n font-weight: 400;\n line-height: 1.5;\n color: #212529;\n text-align: left;\n background-color: #fff;\n}\n\n[tabindex=\"-1\"]:focus {\n outline: 0 !important;\n}\n\nhr {\n box-sizing: content-box;\n height: 0;\n overflow: visible;\n}\n\nh1, h2, h3, h4, h5, h6 {\n margin-top: 0;\n margin-bottom: 0.5rem;\n}\n\np {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nabbr[title],\nabbr[data-original-title] {\n text-decoration: underline;\n text-decoration: underline dotted;\n cursor: help;\n border-bottom: 0;\n}\n\naddress {\n margin-bottom: 1rem;\n font-style: normal;\n line-height: inherit;\n}\n\nol,\nul,\ndl {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nol ol,\nul ul,\nol ul,\nul ol {\n margin-bottom: 0;\n}\n\ndt {\n font-weight: 700;\n}\n\ndd {\n margin-bottom: .5rem;\n margin-left: 0;\n}\n\nblockquote {\n margin: 0 0 1rem;\n}\n\ndfn {\n font-style: italic;\n}\n\nb,\nstrong {\n font-weight: bolder;\n}\n\nsmall {\n font-size: 80%;\n}\n\nsub,\nsup {\n position: relative;\n font-size: 75%;\n line-height: 0;\n vertical-align: baseline;\n}\n\nsub {\n bottom: -.25em;\n}\n\nsup {\n top: -.5em;\n}\n\na {\n color: #007bff;\n text-decoration: none;\n background-color: transparent;\n -webkit-text-decoration-skip: objects;\n}\n\na:hover {\n color: #0056b3;\n text-decoration: underline;\n}\n\na:not([href]):not([tabindex]) {\n color: inherit;\n text-decoration: none;\n}\n\na:not([href]):not([tabindex]):hover, a:not([href]):not([tabindex]):focus {\n color: inherit;\n text-decoration: none;\n}\n\na:not([href]):not([tabindex]):focus {\n outline: 0;\n}\n\npre,\ncode,\nkbd,\nsamp {\n font-family: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n font-size: 1em;\n}\n\npre {\n margin-top: 0;\n margin-bottom: 1rem;\n overflow: auto;\n -ms-overflow-style: scrollbar;\n}\n\nfigure {\n margin: 0 0 1rem;\n}\n\nimg {\n vertical-align: middle;\n border-style: none;\n}\n\nsvg {\n overflow: hidden;\n vertical-align: middle;\n}\n\ntable {\n border-collapse: collapse;\n}\n\ncaption {\n padding-top: 0.75rem;\n padding-bottom: 0.75rem;\n color: #6c757d;\n text-align: left;\n caption-side: bottom;\n}\n\nth {\n text-align: inherit;\n}\n\nlabel {\n display: inline-block;\n margin-bottom: 0.5rem;\n}\n\nbutton {\n border-radius: 0;\n}\n\nbutton:focus {\n outline: 1px dotted;\n outline: 5px auto -webkit-focus-ring-color;\n}\n\ninput,\nbutton,\nselect,\noptgroup,\ntextarea {\n margin: 0;\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n}\n\nbutton,\ninput {\n overflow: visible;\n}\n\nbutton,\nselect {\n text-transform: none;\n}\n\nbutton,\nhtml [type=\"button\"],\n[type=\"reset\"],\n[type=\"submit\"] {\n -webkit-appearance: button;\n}\n\nbutton::-moz-focus-inner,\n[type=\"button\"]::-moz-focus-inner,\n[type=\"reset\"]::-moz-focus-inner,\n[type=\"submit\"]::-moz-focus-inner {\n padding: 0;\n border-style: none;\n}\n\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n box-sizing: border-box;\n padding: 0;\n}\n\ninput[type=\"date\"],\ninput[type=\"time\"],\ninput[type=\"datetime-local\"],\ninput[type=\"month\"] {\n -webkit-appearance: listbox;\n}\n\ntextarea {\n overflow: auto;\n resize: vertical;\n}\n\nfieldset {\n min-width: 0;\n padding: 0;\n margin: 0;\n border: 0;\n}\n\nlegend {\n display: block;\n width: 100%;\n max-width: 100%;\n padding: 0;\n margin-bottom: .5rem;\n font-size: 1.5rem;\n line-height: inherit;\n color: inherit;\n white-space: normal;\n}\n\nprogress {\n vertical-align: baseline;\n}\n\n[type=\"number\"]::-webkit-inner-spin-button,\n[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n[type=\"search\"] {\n outline-offset: -2px;\n -webkit-appearance: none;\n}\n\n[type=\"search\"]::-webkit-search-cancel-button,\n[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n::-webkit-file-upload-button {\n font: inherit;\n -webkit-appearance: button;\n}\n\noutput {\n display: inline-block;\n}\n\nsummary {\n display: list-item;\n cursor: pointer;\n}\n\ntemplate {\n display: none;\n}\n\n[hidden] {\n display: none !important;\n}\n\n/*# sourceMappingURL=bootstrap-reboot.css.map */","// Hover mixin and `$enable-hover-media-query` are deprecated.\n//\n// Originally added during our alphas and maintained during betas, this mixin was\n// designed to prevent `:hover` stickiness on iOS-an issue where hover styles\n// would persist after initial touch.\n//\n// For backward compatibility, we've kept these mixins and updated them to\n// always return their regular pseudo-classes instead of a shimmed media query.\n//\n// Issue: https://github.com/twbs/bootstrap/issues/25195\n\n@mixin hover {\n &:hover { @content; }\n}\n\n@mixin hover-focus {\n &:hover,\n &:focus {\n @content;\n }\n}\n\n@mixin plain-hover-focus {\n &,\n &:hover,\n &:focus {\n @content;\n }\n}\n\n@mixin hover-focus-active {\n &:hover,\n &:focus,\n &:active {\n @content;\n }\n}\n"]} -------------------------------------------------------------------------------- /src/client-src/comps-bootstrap/backup/css/popper.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) Federico Zivolo 2018 3 | Distributed under the MIT License (license terms are at http://opensource.org/licenses/MIT). 4 | */(function(e,t){'object'==typeof exports&&'undefined'!=typeof module?module.exports=t():'function'==typeof define&&define.amd?define(t):e.Popper=t()})(this,function(){'use strict';function e(e){return e&&'[object Function]'==={}.toString.call(e)}function t(e,t){if(1!==e.nodeType)return[];var o=getComputedStyle(e,null);return t?o[t]:o}function o(e){return'HTML'===e.nodeName?e:e.parentNode||e.host}function n(e){if(!e)return document.body;switch(e.nodeName){case'HTML':case'BODY':return e.ownerDocument.body;case'#document':return e.body;}var i=t(e),r=i.overflow,p=i.overflowX,s=i.overflowY;return /(auto|scroll|overlay)/.test(r+s+p)?e:n(o(e))}function r(e){return 11===e?re:10===e?pe:re||pe}function p(e){if(!e)return document.documentElement;for(var o=r(10)?document.body:null,n=e.offsetParent;n===o&&e.nextElementSibling;)n=(e=e.nextElementSibling).offsetParent;var i=n&&n.nodeName;return i&&'BODY'!==i&&'HTML'!==i?-1!==['TD','TABLE'].indexOf(n.nodeName)&&'static'===t(n,'position')?p(n):n:e?e.ownerDocument.documentElement:document.documentElement}function s(e){var t=e.nodeName;return'BODY'!==t&&('HTML'===t||p(e.firstElementChild)===e)}function d(e){return null===e.parentNode?e:d(e.parentNode)}function a(e,t){if(!e||!e.nodeType||!t||!t.nodeType)return document.documentElement;var o=e.compareDocumentPosition(t)&Node.DOCUMENT_POSITION_FOLLOWING,n=o?e:t,i=o?t:e,r=document.createRange();r.setStart(n,0),r.setEnd(i,0);var l=r.commonAncestorContainer;if(e!==l&&t!==l||n.contains(i))return s(l)?l:p(l);var f=d(e);return f.host?a(f.host,t):a(e,d(t).host)}function l(e){var t=1=o.clientWidth&&n>=o.clientHeight}),l=0a[e]&&!t.escapeWithReference&&(n=J(f[o],a[e]-('right'===e?f.width:f.height))),ae({},o,n)}};return l.forEach(function(e){var t=-1===['left','top'].indexOf(e)?'secondary':'primary';f=le({},f,m[t](e))}),e.offsets.popper=f,e},priority:['left','right','top','bottom'],padding:5,boundariesElement:'scrollParent'},keepTogether:{order:400,enabled:!0,fn:function(e){var t=e.offsets,o=t.popper,n=t.reference,i=e.placement.split('-')[0],r=Z,p=-1!==['top','bottom'].indexOf(i),s=p?'right':'bottom',d=p?'left':'top',a=p?'width':'height';return o[s]r(n[s])&&(e.offsets.popper[d]=r(n[s])),e}},arrow:{order:500,enabled:!0,fn:function(e,o){var n;if(!q(e.instance.modifiers,'arrow','keepTogether'))return e;var i=o.element;if('string'==typeof i){if(i=e.instance.popper.querySelector(i),!i)return e;}else if(!e.instance.popper.contains(i))return console.warn('WARNING: `arrow.element` must be child of its popper element!'),e;var r=e.placement.split('-')[0],p=e.offsets,s=p.popper,d=p.reference,a=-1!==['left','right'].indexOf(r),l=a?'height':'width',f=a?'Top':'Left',m=f.toLowerCase(),h=a?'left':'top',c=a?'bottom':'right',u=S(i)[l];d[c]-us[c]&&(e.offsets.popper[m]+=d[m]+u-s[c]),e.offsets.popper=g(e.offsets.popper);var b=d[m]+d[l]/2-u/2,y=t(e.instance.popper),w=parseFloat(y['margin'+f],10),E=parseFloat(y['border'+f+'Width'],10),v=b-e.offsets.popper[m]-w-E;return v=$(J(s[l]-u,v),0),e.arrowElement=i,e.offsets.arrow=(n={},ae(n,m,Q(v)),ae(n,h,''),n),e},element:'[x-arrow]'},flip:{order:600,enabled:!0,fn:function(e,t){if(W(e.instance.modifiers,'inner'))return e;if(e.flipped&&e.placement===e.originalPlacement)return e;var o=v(e.instance.popper,e.instance.reference,t.padding,t.boundariesElement,e.positionFixed),n=e.placement.split('-')[0],i=T(n),r=e.placement.split('-')[1]||'',p=[];switch(t.behavior){case he.FLIP:p=[n,i];break;case he.CLOCKWISE:p=z(n);break;case he.COUNTERCLOCKWISE:p=z(n,!0);break;default:p=t.behavior;}return p.forEach(function(s,d){if(n!==s||p.length===d+1)return e;n=e.placement.split('-')[0],i=T(n);var a=e.offsets.popper,l=e.offsets.reference,f=Z,m='left'===n&&f(a.right)>f(l.left)||'right'===n&&f(a.left)f(l.top)||'bottom'===n&&f(a.top)f(o.right),g=f(a.top)f(o.bottom),b='left'===n&&h||'right'===n&&c||'top'===n&&g||'bottom'===n&&u,y=-1!==['top','bottom'].indexOf(n),w=!!t.flipVariations&&(y&&'start'===r&&h||y&&'end'===r&&c||!y&&'start'===r&&g||!y&&'end'===r&&u);(m||b||w)&&(e.flipped=!0,(m||b)&&(n=p[d+1]),w&&(r=G(r)),e.placement=n+(r?'-'+r:''),e.offsets.popper=le({},e.offsets.popper,C(e.instance.popper,e.offsets.reference,e.placement)),e=P(e.instance.modifiers,e,'flip'))}),e},behavior:'flip',padding:5,boundariesElement:'viewport'},inner:{order:700,enabled:!1,fn:function(e){var t=e.placement,o=t.split('-')[0],n=e.offsets,i=n.popper,r=n.reference,p=-1!==['left','right'].indexOf(o),s=-1===['top','left'].indexOf(o);return i[p?'left':'top']=r[o]-(s?i[p?'width':'height']:0),e.placement=T(t),e.offsets.popper=g(i),e}},hide:{order:800,enabled:!0,fn:function(e){if(!q(e.instance.modifiers,'hide','preventOverflow'))return e;var t=e.offsets.reference,o=D(e.instance.modifiers,function(e){return'preventOverflow'===e.name}).boundaries;if(t.bottomo.right||t.top>o.bottom||t.right 2 | 3 | 4 | 5 | Export Example App 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/client-src/comps/BrowseItem.jsx: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Tomer Riko Shalev 3 | */ 4 | 5 | import React from 'react' 6 | import InputLabel from '@material-ui/core/InputLabel' 7 | import Input from '@material-ui/core/Input' 8 | import TextField from '@material-ui/core/TextField' 9 | import Icon from '@material-ui/core/Icon' 10 | import IconButton from '@material-ui/core/IconButton' 11 | import FormControl from '@material-ui/core/FormControl' 12 | import withStyles from '@material-ui/core/styles/withStyles' 13 | import purple from '@material-ui/core/colors/purple' 14 | 15 | import ListItem from "@material-ui/core/ListItem" 16 | import ListItemSecondaryAction from "@material-ui/core/ListItemSecondaryAction" 17 | import ListItemText from "@material-ui/core/ListItemText" 18 | 19 | const styles = theme => ({ 20 | margin: { 21 | margin: theme.spacing.unit, 22 | }, 23 | // cssLabel: { 24 | // '&$cssFocused': { 25 | // color: purple[500], 26 | // }, 27 | // }, 28 | // cssFocused: {}, 29 | // cssUnderline: { 30 | // '&:after': { 31 | // borderBottomColor: purple[500], 32 | // }, 33 | // }, 34 | folder: { 35 | marginRight: theme.spacing.unit * 3.5 36 | } 37 | }) 38 | 39 | /** 40 | * general browse item 41 | * 42 | */ 43 | class BrowseItem extends React.Component { 44 | 45 | constructor(props) { 46 | super(props) 47 | 48 | this.lblRef = React.createRef() 49 | } 50 | 51 | /** 52 | * onClick - clicking the input 53 | * 54 | */ 55 | onClick = (e) => { 56 | this.lblRef.current.focus() 57 | var path = this.lblRef.current.value 58 | 59 | // test if we are inside the adobe-cep runtime 60 | if(window.cep) { 61 | var result = window.cep.fs.showOpenDialog(false, 62 | true, 'Select a Folder', null, null) 63 | 64 | path = result.data 65 | } 66 | 67 | this.lblRef.current.value = path 68 | } 69 | 70 | /** 71 | * return path or undefined if it was not set 72 | * 73 | * @return {string|undefined} the path 74 | */ 75 | get path() { 76 | return this.lblRef.current.value 77 | } 78 | 79 | render() { 80 | const {classes} = this.props; 81 | 82 | return ( 83 | 86 | 87 | 88 | 92 | Choose Folder... 93 | 94 | 97 | 98 | 99 | 100 | 101 | folder_open 102 | 103 | 104 | 105 | 106 | 107 | ) 108 | 109 | } 110 | 111 | } 112 | 113 | export default withStyles(styles)(BrowseItem); 114 | -------------------------------------------------------------------------------- /src/client-src/comps/Config.jsx: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Tomer Riko Shalev 3 | */ 4 | 5 | import React from 'react' 6 | 7 | export default class Config extends React.Component { 8 | 9 | render() { 10 | 11 | return ( 12 |
13 |

Config

14 |
15 | ) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/client-src/comps/Debug.jsx: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Tomer Riko Shalev 3 | */ 4 | 5 | import React from 'react' 6 | import { Hook, Console, Decode } from 'console-feed' 7 | 8 | const styles = { 9 | root: { 10 | width: '100%', 11 | height: '100%', 12 | } 13 | } 14 | 15 | /** 16 | * display chrome debugger tools console 17 | */ 18 | export default class Debug extends React.Component { 19 | constructor(props) { 20 | super(props) 21 | } 22 | 23 | state = { 24 | logs: [] 25 | } 26 | 27 | componentDidMount() { 28 | // iniitial logz 29 | const { rawLogz } = this.props 30 | 31 | // also listen 32 | Hook(window.console, (log) => { 33 | this.setState((state) => { 34 | var logz = state.logs.slice() 35 | logz.push(Decode(log)) 36 | return { 37 | logs: logz 38 | } 39 | 40 | }) 41 | 42 | }) 43 | 44 | // replay initial logs 45 | console.log(...rawLogz) 46 | } 47 | 48 | render() { 49 | 50 | return ( 51 |
52 | 53 |
54 | ) 55 | 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/client-src/comps/Hideable.jsx: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Tomer Riko Shalev 3 | */ 4 | 5 | import React from 'react'; 6 | 7 | /** 8 | * simple wrapper component that supports visibility 9 | */ 10 | export default class Hideable extends React.Component { 11 | constructor(props) { 12 | super(props) 13 | } 14 | 15 | invisibleStyle = { 16 | display: 'none' 17 | } 18 | 19 | visibleStyle = { 20 | display: 'block' 21 | } 22 | 23 | visStyle(on) { 24 | return on ? this.visibleStyle : this.invisibleStyle 25 | } 26 | 27 | render() { 28 | const { visible, invisible, style, className} = this.props 29 | const resolved = visible ? true : false 30 | 31 | var mergedStyle = this.visStyle(resolved) 32 | 33 | if(style) 34 | mergedStyle = Object.assign(style, mergedStyle) 35 | 36 | return ( 37 |
38 | {this.props.children} 39 |
40 | ) 41 | 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/client-src/comps/Home.jsx: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Tomer Riko Shalev 3 | */ 4 | 5 | import React from 'react' 6 | import Paper from "@material-ui/core/Paper" 7 | import List from "@material-ui/core/List" 8 | import Button from "@material-ui/core/Button" 9 | import withStyles from '@material-ui/core/styles/withStyles' 10 | 11 | import PluginItem from './PluginItem.jsx' 12 | import BrowseItem from './BrowseItem.jsx' 13 | 14 | const styles = theme => ({ 15 | root: { 16 | paddingTop:theme.spacing.unit * 1 17 | }, 18 | paper: { 19 | display:'flex', 20 | flexDirection: 'column' 21 | }, 22 | export: { 23 | margin: theme.spacing.unit * 3, 24 | alignSelf: 'flex-end' 25 | } 26 | }) 27 | 28 | /** 29 | * Home tab content 30 | * 31 | */ 32 | class Home extends React.Component { 33 | 34 | constructor() { 35 | super() 36 | 37 | this.textureItemRef = React.createRef() 38 | this.masksItemRef = React.createRef() 39 | this.infoItemRef = React.createRef() 40 | this.hierarchicalItemRef = React.createRef() 41 | this.inspectVisibleItemRef = React.createRef() 42 | this.browseItemRef = React.createRef() 43 | this.namesItemRef = React.createRef() 44 | } 45 | 46 | /** 47 | * export button was clicked 48 | * 49 | */ 50 | export_onClick = (e) => { 51 | var folderPath = this.browseItemRef.current.path 52 | var isTexturesChecked = this.textureItemRef.current.isChecked 53 | var isMasksChecked = this.masksItemRef.current.isChecked 54 | var isInfoChecked = this.infoItemRef.current.isChecked 55 | var isHierarchicalChecked = this.hierarchicalItemRef.current.isChecked 56 | var isInspectVisibleChecked = this.inspectVisibleItemRef.current.isChecked 57 | var isMeaningfulNamesChecked = this.namesItemRef.current.isChecked 58 | 59 | var { onExecutePlugin } = this.props 60 | 61 | onExecutePlugin({ 62 | folderPath, 63 | isTexturesChecked, 64 | isMasksChecked, 65 | isInfoChecked, 66 | isMeaningfulNamesChecked, 67 | isHierarchicalChecked, 68 | isInspectVisibleChecked 69 | }) 70 | } 71 | 72 | render() { 73 | const { classes } = this.props; 74 | 75 | return ( 76 |
77 | 78 | 79 | 81 | 83 | 85 | 87 | 89 | 91 | 92 | 93 | 97 | 98 | 99 |
100 | ) 101 | 102 | } 103 | 104 | } 105 | 106 | export default withStyles(styles)(Home); 107 | -------------------------------------------------------------------------------- /src/client-src/comps/Navigator.jsx: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Tomer Riko Shalev 3 | */ 4 | 5 | import React from 'react' 6 | import Paper from '@material-ui/core/Paper' 7 | import Tabs from '@material-ui/core/Tabs' 8 | import Tab from '@material-ui/core/Tab' 9 | import Icon from '@material-ui/core/Icon' 10 | import AppBar from '@material-ui/core/AppBar'; 11 | import withStyles from '@material-ui/core/styles/withStyles'; 12 | 13 | import Hideable from './Hideable.jsx' 14 | 15 | const styles = theme => ({ 16 | root: { 17 | flexDirection: 'column', 18 | width: '100%', 19 | height: '100%', 20 | display: 'flex', 21 | backgroundColor: theme.palette.background.paper 22 | }, 23 | contentContainer: { 24 | height: '100%', 25 | overflow: 'scroll', 26 | overflowX: 'hidden' 27 | } 28 | }) 29 | 30 | /** 31 | * react component for creating a material-ui tab navigator. 32 | * configurable with data. 33 | */ 34 | class Navigator extends React.Component { 35 | 36 | constructor(props) { 37 | super(props) 38 | } 39 | 40 | state = { 41 | activeTabIndex: 0, 42 | } 43 | 44 | handleChange = (event, value) => { 45 | this.setState({ activeTabIndex: value }) 46 | 47 | // notidy observer 48 | const {data, onNavigateChange} = this.props 49 | 50 | if(onNavigateChange) 51 | onNavigateChange(value, data[value].title) 52 | } 53 | 54 | /** 55 | * tabFactory - create a tab item 56 | * 57 | * @param {type} data description 58 | * @return {type} description 59 | */ 60 | tabFactory(data, index) { 61 | var title = data.title.toUpperCase() 62 | var icon = data.icon 63 | 64 | return {icon}} label={title}/> 65 | } 66 | 67 | /** 68 | * tabFactory - create a tab content container 69 | *{data.comp} 70 | * @param {type} data description 71 | * @return {type} description 72 | */ 73 | tabContentFactory(data, index, active, classes) { 74 | var title = data.title.toLowerCase() 75 | var comp = data.comp 76 | 77 | return ( 78 | {comp} 79 | ) 80 | } 81 | 82 | render() { 83 | const { data , classes} = this.props 84 | const { activeTabIndex } = this.state; 85 | 86 | return ( 87 |
88 | 89 | 93 | 94 | {data.map((item,i) => this.tabFactory(item, i))} 95 | 96 | 97 |
98 | {data.map((item,i) => this.tabContentFactory(item, i, activeTabIndex===i, classes))} 99 |
100 |
101 | ) 102 | 103 | } 104 | 105 | } 106 | 107 | export default withStyles(styles)(Navigator); 108 | -------------------------------------------------------------------------------- /src/client-src/comps/PluginItem.jsx: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Tomer Riko Shalev 3 | */ 4 | 5 | import React from 'react' 6 | import ListItem from "@material-ui/core/ListItem"; 7 | import ListItemSecondaryAction from "@material-ui/core/ListItemSecondaryAction"; 8 | import ListItemText from "@material-ui/core/ListItemText"; 9 | import Checkbox from "@material-ui/core/Checkbox"; 10 | import IconButton from "@material-ui/core/IconButton"; 11 | import Icon from '@material-ui/core/Icon'; 12 | 13 | /** 14 | * general plugin item that lives inside thw Home content 15 | * 16 | */ 17 | export default class PluginItem extends React.Component { 18 | 19 | constructor() { 20 | super() 21 | 22 | this.cbRef = React.createRef() 23 | } 24 | 25 | state = { 26 | checked: true 27 | } 28 | 29 | internal_check(on) { 30 | this.setState({ 31 | checked: on 32 | }) 33 | } 34 | 35 | handleToggle = () => { 36 | const { checked } = this.state 37 | 38 | this.setState({ 39 | checked: !checked 40 | }) 41 | 42 | } 43 | 44 | /** 45 | * get isChecked 46 | * 47 | * @return {type} description 48 | */ 49 | get isChecked() { 50 | return this.state.checked 51 | } 52 | 53 | set check(val) { 54 | this.internal_check(val) 55 | } 56 | 57 | render() { 58 | const { index, title, desc, icon } = this.props 59 | const { checked } = this.state 60 | 61 | return ( 62 | 69 | 73 | 74 | 75 | {icon} 76 | 77 | 78 | ) 79 | 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /src/client-src/comps/Upload.jsx: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Tomer Riko Shalev 3 | */ 4 | 5 | import React from 'react' 6 | 7 | export default class Upload extends React.Component { 8 | 9 | render() { 10 | return ( 11 |
12 |

Upload

13 |
14 | ) 15 | 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/client-src/controller.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Tomer Riko Shalev 3 | */ 4 | 5 | /** 6 | * the main plugin session. This can enter the node modules as 7 | * well as the host 8 | * 9 | */ 10 | class Controller { 11 | 12 | constructor() { 13 | //super() 14 | 15 | this.init() 16 | } 17 | 18 | /** 19 | * init - session 20 | * 21 | */ 22 | init() { 23 | this.log('client controller is initing...') 24 | this.log(`do we have session ? ${this.hasSession()}`) 25 | 26 | this.log('client controller has inited') 27 | } 28 | 29 | /** 30 | * invoke the plugin 31 | * 32 | * @param {{textures:boolean, masks:boolean, info: boolean, flatten:boolean}} options for plugin 33 | * 34 | * @return {object} describes how well the execution of plugin was 35 | */ 36 | invokePlugin(options) { 37 | this.log('invokePlugin') 38 | console.log(options) 39 | 40 | if(!this.hasSession()) 41 | return 42 | 43 | session.invokePlugin(options) 44 | .then(res => console.log(res)) 45 | .catch(err => console.log(err)) 46 | } 47 | 48 | 49 | /** 50 | * get logz - get raw logz from log manager 51 | * 52 | * @return {array} description 53 | */ 54 | get logz() { 55 | if(!this.hasSession()) 56 | return [] 57 | 58 | return session.managers.log.rawLogs 59 | } 60 | 61 | /** 62 | * do we have access to session services ? 63 | * 64 | * @return {boolean} true/false 65 | */ 66 | hasSession() { 67 | return window.session!==undefined 68 | } 69 | 70 | /** 71 | * log some info with session prefix 72 | * 73 | * @param {string} val what to log 74 | */ 75 | log(val) { 76 | console.log(`${this.name} ${val}`) 77 | } 78 | 79 | get name() { 80 | return 'Client Controller:: ' 81 | } 82 | 83 | } 84 | 85 | var controller = new Controller() 86 | 87 | export default controller 88 | -------------------------------------------------------------------------------- /src/client-src/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Tomer Riko Shalev 3 | */ 4 | 5 | import React from 'react' 6 | import ReactDOM from 'react-dom' 7 | import App from './App.jsx' 8 | import controller from './Controller.js' 9 | 10 | import './assets/fonts/typeface-roboto/index.css'; 11 | import './assets/fonts/material-icons/index.css'; 12 | import './assets/css/style.css'; 13 | 14 | ReactDOM.render(, 15 | document.getElementById('root') 16 | ) 17 | -------------------------------------------------------------------------------- /src/client-src/index.server.template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Export Example App 6 | 7 | 8 |
9 | 10 | 11 | -------------------------------------------------------------------------------- /src/client-src/readme.md: -------------------------------------------------------------------------------- 1 | #### UI 2 | 3 | right now I am using `Material-UI` 4 | I also have a `Bootstrap` version (but you will have to setup it's dependencies like 5 | css and js.) 6 | 7 | #### two app skins 8 | I wrote one app with `Bootstrap` and the other with `Material-UI` 9 | -------------------------------------------------------------------------------- /src/host/JSON.jsx: -------------------------------------------------------------------------------- 1 | /** 2 | * JSON - from: https://github.com/douglascrockford/JSON-js 3 | */ 4 | if(typeof JSON!=='object'){JSON={};}(function(){'use strict';function f(n){return n<10?'0'+n:n;}function this_value(){return this.valueOf();}if(typeof Date.prototype.toJSON!=='function'){Date.prototype.toJSON=function(){return isFinite(this.valueOf())?this.getUTCFullYear()+'-'+f(this.getUTCMonth()+1)+'-'+f(this.getUTCDate())+'T'+f(this.getUTCHours())+':'+f(this.getUTCMinutes())+':'+f(this.getUTCSeconds())+'Z':null;};Boolean.prototype.toJSON=this_value;Number.prototype.toJSON=this_value;String.prototype.toJSON=this_value;}var cx,escapable,gap,indent,meta,rep;function quote(string){escapable.lastIndex=0;return escapable.test(string)?'"'+string.replace(escapable,function(a){var c=meta[a];return typeof c==='string'?c:'\\u'+('0000'+a.charCodeAt(0).toString(16)).slice(-4);})+'"':'"'+string+'"';}function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value==='object'&&typeof value.toJSON==='function'){value=value.toJSON(key);}if(typeof rep==='function'){value=rep.call(holder,key,value);}switch(typeof value){case'string':return quote(value);case'number':return isFinite(value)?String(value):'null';case'boolean':case'null':return String(value);case'object':if(!value){return'null';}gap+=indent;partial=[];if(Object.prototype.toString.apply(value)==='[object Array]'){length=value.length;for(i=0;i 2 | 3 | 4 | 5 | Export Example App 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/index.template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Export Example App 6 | 7 | 8 |
9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/session-src/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /src/session-src/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Tomer Riko Shalev 3 | */ 4 | 5 | import session from './src/Session.js' 6 | 7 | window.session = session 8 | -------------------------------------------------------------------------------- /src/session-src/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "session", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1 5 | } 6 | -------------------------------------------------------------------------------- /src/session-src/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "session", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "postinstall": "mkdir -p node_modules" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": {} 13 | } 14 | -------------------------------------------------------------------------------- /src/session-src/src/ScriptLoader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Tomer Riko Shalev 3 | */ 4 | 5 | /** 6 | * load jsx scripts dynamically 7 | */ 8 | class ScriptLoader { 9 | EvalScript_ErrMessage = "EvalScript error." 10 | 11 | constructor() { 12 | this.cs = new CSInterface() 13 | } 14 | 15 | get cs() { 16 | return this._cs 17 | } 18 | 19 | set cs(val) { 20 | this._cs = val 21 | } 22 | 23 | /** 24 | * loadJSX - load a jsx file dynamically, this 25 | * will also load all of it's includes which is desirable 26 | * 27 | * @param {type} fileName the file name 28 | * @return {type} description 29 | */ 30 | loadJSX(fileName) { 31 | var cs = this.cs 32 | var extensionRoot = cs.getSystemPath(SystemPath.EXTENSION) + "/host/"; 33 | 34 | cs.evalScript('$.evalFile("' + extensionRoot + fileName + '")'); 35 | } 36 | 37 | /** 38 | * evalScript - evaluate a JSX script 39 | * 40 | * @param {type} functionName the string name of the function to invoke 41 | * @param {type} params the params object 42 | * @return {Promise} a promise 43 | */ 44 | evalScript(functionName, params) { 45 | var params_string = params ? JSON.stringify(params) : '' 46 | var eval_string = `${functionName}('${params_string}')` 47 | var that = this 48 | 49 | return new Promise((resolve, reject) => { 50 | 51 | var callback = function(eval_res) { 52 | // console.log('weird' + eval_res) 53 | if(typeof eval_res === 'string') { 54 | // console.log(eval_res) 55 | if(eval_res.toLowerCase().indexOf('error') != -1) { 56 | that.log('err eval') 57 | reject(that.createScriptError(eval_res)) 58 | 59 | return 60 | } 61 | } 62 | 63 | that.log('success eval') 64 | 65 | resolve(eval_res) 66 | 67 | return 68 | } 69 | 70 | that.cs.evalScript(eval_string, callback) 71 | }) 72 | 73 | } 74 | 75 | createScriptError(reason, data) { 76 | return {reason, data} 77 | } 78 | 79 | /** 80 | * log some info with session prefix 81 | * 82 | * @param {string} val what to log 83 | */ 84 | log(val) { 85 | console.log(`${this.name} ${val}`) 86 | } 87 | 88 | get name() { 89 | return 'ScriptLoader:: ' 90 | } 91 | 92 | } 93 | 94 | var scriptLoader = new ScriptLoader() 95 | 96 | export default scriptLoader 97 | -------------------------------------------------------------------------------- /src/session-src/src/Session.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Tomer Riko Shalev 3 | */ 4 | 5 | import EventEmitter from 'events' 6 | import scriptLoader from './ScriptLoader' 7 | import DataManagers from './managers/DataManagers.js' 8 | /** 9 | * the main plugin session. This can enter the node modules as 10 | * well as the host 11 | * 12 | */ 13 | class Session { 14 | 15 | _managers = new DataManagers() 16 | 17 | constructor() { 18 | //super() 19 | 20 | this.init() 21 | } 22 | 23 | /** 24 | * init - session 25 | * 26 | */ 27 | init() { 28 | // init before everything so I can intercept console.log 29 | this._managers.init() 30 | this.log('session is initing...') 31 | // load jsx file dynamically 32 | this.log('loading the main jsx file') 33 | scriptLoader.loadJSX('main.jsx') 34 | 35 | // some testing 36 | this.test() 37 | // var fs = require('fs-extra') 38 | //console.log(fs) 39 | 40 | this.log('session is inited') 41 | } 42 | 43 | 44 | /** 45 | * get data managers 46 | * 47 | * @return {type} description 48 | */ 49 | get managers() { 50 | return this._managers 51 | } 52 | 53 | /** 54 | * scriptLoader - get the script loader 55 | * 56 | */ 57 | scriptLoader() { 58 | return scriptLoader 59 | } 60 | 61 | /** 62 | * test - let's test things 63 | * 64 | */ 65 | test() { 66 | var obj = { 67 | name: 'tomer' 68 | } 69 | 70 | scriptLoader.evalScript('test_host', obj).then((res) => { 71 | this.log('result is ' + res) 72 | }) 73 | } 74 | 75 | /** 76 | * invoke the plugin 77 | * 78 | * @param {{textures:boolean, masks:boolean, info: boolean, flatten:boolean}} options for plugin 79 | * 80 | * @return {object} describes how well the execution of plugin was 81 | */ 82 | invokePlugin(options) { 83 | const { folderPath, isFlattenChecked, 84 | isInfoChecked, isInspectVisibleChecked, 85 | isMasksChecked, isTexturesChecked, 86 | isMeaningfulNamesChecked, isHierarchicalChecked} = options 87 | 88 | // i reparse everything to detect failures 89 | const pluginData = { 90 | destinationFolder: folderPath, 91 | exportInfoJson: isInfoChecked, 92 | inspectOnlyVisibleLayers: isInspectVisibleChecked, 93 | exportMasks: isMasksChecked, 94 | exportTextures: isTexturesChecked, 95 | flatten: !isHierarchicalChecked, 96 | namePrefix: isMeaningfulNamesChecked ? 'layer' : undefined 97 | } 98 | 99 | var that = this 100 | 101 | return new Promise((resolve, reject) => { 102 | 103 | scriptLoader.evalScript('invoke_document_worker', pluginData) 104 | .then((res) => { 105 | resolve(JSON.parse(res)) 106 | }) 107 | .catch(err => { 108 | reject(err) 109 | }) 110 | 111 | }) 112 | 113 | } 114 | 115 | /** 116 | * log some info with session prefix 117 | * 118 | * @param {string} val what to log 119 | */ 120 | log(val) { 121 | console.log(`${this.name} ${val}`) 122 | } 123 | 124 | get name() { 125 | return 'Session:: ' 126 | } 127 | 128 | } 129 | 130 | var session = new Session() 131 | 132 | export default session 133 | -------------------------------------------------------------------------------- /src/session-src/src/managers/DataManagers.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Tomer Riko Shalev 3 | */ 4 | 5 | import LogManager from './LogManager.js' 6 | 7 | export default class DataManagers { 8 | _manager_log = undefined 9 | 10 | constructor() { 11 | 12 | } 13 | 14 | init() { 15 | this._manager_log = new LogManager() 16 | 17 | this._manager_log.init() 18 | } 19 | 20 | /** 21 | * get log - the log manager 22 | * 23 | * @return {LogManager} the log manager 24 | */ 25 | get log() { 26 | return this._manager_log 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/session-src/src/managers/LogManager.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Tomer Riko Shalev 3 | */ 4 | 5 | 6 | /** 7 | * log management 8 | * 9 | */ 10 | export default class LogManager { 11 | _logs = [] 12 | 13 | constructor() { 14 | 15 | } 16 | 17 | init() { 18 | this.log('initing...') 19 | 20 | var log = console.log 21 | 22 | if(console === undefined) 23 | return 24 | var that = this 25 | // override the console.log method 26 | console.log = function () { 27 | // log.call(this, 'My Console!!!') 28 | // log.apply(this, Array.prototype.slice.call(arguments)) 29 | // retain older console.log functionality 30 | log.call(this, ...arguments) 31 | // save the log internally 32 | that.addRawLog(...arguments) 33 | } 34 | 35 | } 36 | 37 | /** 38 | * addLog - collect log 39 | * 40 | * @param {Object} val anything 41 | * 42 | */ 43 | addRawLog(val) { 44 | this._logs.push(val) 45 | } 46 | 47 | get rawLogs() { 48 | return this._logs 49 | } 50 | 51 | get name() { 52 | return 'LogManager:: ' 53 | } 54 | 55 | log(val) { 56 | return `${this.name} ${val}` 57 | } 58 | } 59 | --------------------------------------------------------------------------------