├── .eslintrc.json ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .jsbeautifyrc ├── Gruntfile.js ├── LICENSE ├── README.md ├── app ├── __init__.py ├── static │ ├── 16.png │ ├── 32.png │ ├── about.html │ ├── cm │ │ ├── Readme.md │ │ ├── active-line.js │ │ ├── clike.js │ │ ├── codemirror.css │ │ ├── codemirror.js │ │ ├── matchbrackets.js │ │ ├── show-hint.css │ │ └── show-hint.js │ ├── cookie-policy.html │ ├── css │ │ └── s.css │ ├── examples.html │ ├── favicon.ico │ ├── img │ │ ├── ce.svg │ │ ├── clb.svg │ │ ├── logo.svg │ │ ├── nav-dark.svg │ │ ├── nav.svg │ │ ├── qb.svg │ │ └── twcard.png │ ├── js │ │ ├── cookie.js │ │ ├── main.js │ │ └── settings.js │ ├── ms │ │ ├── Readme.md │ │ ├── jquery-3.3.1.js │ │ └── multiple-select.js │ ├── privacy-policy.html │ ├── robots.txt │ ├── settings.html │ └── sitemap.xml └── templates │ ├── 404.html │ ├── index.html │ └── version.html ├── deploy_rsa.enc ├── insights.wsgi ├── local.py ├── package.json ├── pics ├── window-maximize.svg └── window-minimize.svg ├── requirements.txt ├── requirements_gh.txt ├── test.py └── test └── cookie.test.js /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "node": true, 5 | "commonjs": true, 6 | "es6": true, 7 | "jquery": true 8 | }, 9 | "extends": "eslint:recommended", 10 | "parserOptions": { 11 | "ecmaVersion": 2018 12 | }, 13 | "rules": { 14 | "indent": [ 15 | "error", 16 | 2 17 | ], 18 | "linebreak-style": [ 19 | "error", 20 | "unix" 21 | ], 22 | "quotes": [ 23 | "error", 24 | "single" 25 | ], 26 | "semi": [ 27 | "error", 28 | "always" 29 | ] 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Scheduled 2 | on: 3 | push: 4 | branches-ignore: 5 | - 'continuous' 6 | 7 | pull_request: 8 | branches-ignore: 9 | - 'continuous' 10 | 11 | jobs: 12 | build: 13 | name: Build C++ Insights Web 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v4 17 | with: 18 | fetch-depth: 0 19 | - name: Setup Node.js for use with actions 20 | uses: actions/setup-node@v4 21 | with: 22 | node-version: '18' 23 | cache: 'npm' 24 | cache-dependency-path: '**/package.json' 25 | 26 | - name: Setup Python environment 27 | uses: actions/setup-python@v5 28 | with: 29 | python-version: '3.7' # Server has 3.5 30 | 31 | - name: Install SSH Key 32 | uses: shimataro/ssh-key-action@v1.3.0 33 | with: 34 | private-key: ${{ secrets.SSH_PRIVATE_KEY }} 35 | public-key: ${{ secrets.SSH_PUBLIC_KEY }} 36 | known-hosts: ${{ secrets.SSH_KNOWN_HOSTS }} 37 | 38 | - name: Install python 39 | run: pip install -r requirements_gh.txt 40 | 41 | - name: Install node modules 42 | run: | 43 | npm set progress=false 44 | npm install -g grunt-cli 45 | npm install 46 | 47 | - name: Generate 48 | run: grunt 49 | 50 | - name: Run coverage 51 | run: coverage run test.py 52 | 53 | - name: Upload code coverage info 54 | run: bash <(curl -s https://codecov.io/bash) -cF python 55 | 56 | #------------------------------------------------------------------------------ 57 | # DEPLOY 58 | #------------------------------------------------------------------------------ 59 | deploy: 60 | needs: [build] 61 | if: github.ref == 'refs/heads/main' 62 | name: Final Deploy 63 | runs-on: ubuntu-latest 64 | 65 | steps: 66 | - uses: actions/checkout@v4 67 | with: 68 | fetch-depth: 0 69 | - name: Setup Node.js for use with actions 70 | uses: actions/setup-node@v4 71 | with: 72 | node-version: '18' 73 | cache: 'npm' 74 | cache-dependency-path: '**/package.json' 75 | 76 | - name: Setup Python environment 77 | uses: actions/setup-python@v5 78 | with: 79 | python-version: '3.7' # Server has 3.5 80 | 81 | - name: Install SSH Key 82 | uses: shimataro/ssh-key-action@v1.3.0 83 | with: 84 | private-key: ${{ secrets.SSH_PRIVATE_KEY }} 85 | public-key: ${{ secrets.SSH_PUBLIC_KEY }} 86 | known-hosts: ${{ secrets.SSH_KNOWN_HOSTS }} 87 | 88 | - name: Install python 89 | run: pip install -r requirements_gh.txt 90 | 91 | - name: Install node modules 92 | run: | 93 | npm set progress=false 94 | npm install -g grunt-cli 95 | npm install 96 | 97 | - name: Generate 98 | run: grunt 99 | 100 | - name: Update latest tag 101 | uses: richardsimko/update-tag@v1 102 | with: 103 | tag_name: latest 104 | env: 105 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 106 | 107 | - name: Publish 108 | run: rsync -avcz --delete --exclude=.DS_Store --exclude=__pycache__ --exclude='google*.html' --exclude='urls.db' ${GITHUB_WORKSPACE}/dist/app/ ${{ secrets.SSH_HOST_NEW }} 109 | 110 | - name: Notify listeners 111 | run: | 112 | curl -X POST -H "Accept: application/vnd.github.v3+json" -H "Authorization: token ${{ secrets.GH_TRIGGER }}" https://api.github.com/repos/andreasfertig/cppinsights-webfrontend-container/dispatches -d '{"event_type":"rebuild_trigger"}' &> /dev/null 113 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | /dist 3 | /node_modules 4 | -------------------------------------------------------------------------------- /.jsbeautifyrc: -------------------------------------------------------------------------------- 1 | { 2 | "indent_size": 2, 3 | "indent_char": " ", 4 | "indent_with_tabs": false, 5 | "preserve_newlines": true, 6 | "max_preserve_newlines": 2, 7 | "space_after_anon_function": false, 8 | "brace_style": "collapse", 9 | "eol": "\n", 10 | "keep_function_indentation": false, 11 | "space_before_conditional": true, 12 | "break_chained_methods": false, 13 | "eval_code": false, 14 | "unescape_strings": false, 15 | "wrap_attributes": "auto", 16 | "wrap_attributes_indent_size": 4, 17 | "end_with_newline": true, 18 | "wrap_line_length": 120 19 | } 20 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | 3 | grunt.initConfig({ 4 | svgmin: { 5 | options: { 6 | plugins: [{ 7 | name: 'preset-default', 8 | params: { 9 | overrides: { 10 | removeViewBox: false, 11 | removeEmptyAttrs: false, 12 | removeUselessStrokeAndFill: false, 13 | }, 14 | }, 15 | }] 16 | }, 17 | dist: { 18 | files: [{ 19 | expand: true, 20 | cwd: 'app/static/img/', 21 | src: ['*.svg'], 22 | dest: 'dist/app/static/img', 23 | ext: '.svg' 24 | }] 25 | } 26 | }, 27 | 28 | cssmin: { 29 | target: { 30 | files: [{ 31 | expand: true, 32 | cwd: 'app/static/css', 33 | src: ['*.css', '!*.min.css'], 34 | dest: 'dist/app/static/css', 35 | ext: '.min.css' 36 | }, 37 | { 38 | 'dist/app/static/cm/cm.min.css': ['app/static/cm/codemirror.css', 'app/static/cm/show-hint.css'] 39 | }, 40 | ] 41 | } 42 | }, 43 | 44 | uglify: { 45 | options: { 46 | mangle: false 47 | }, 48 | cmjs: { 49 | files: { 50 | 'dist/app/static/cm/cm.min.js': ['app/static/cm/codemirror.js', 'app/static/cm/active-line.js', 51 | 'app/static/cm/clike.js', 'app/static/cm/matchbrackets.js', 'app/static/cm/show-hint.js' 52 | ], 53 | 'dist/app/static/js/main.min.js': ['app/static/js/main.js'], 54 | 'dist/app/static/js/cookie.min.js': ['app/static/js/cookie.js'], 55 | 'dist/app/static/js/settings.min.js': ['app/static/js/settings.js'], 56 | 'dist/app/static/ms/jquery-3.3.1.min.js': ['app/static/ms/jquery-3.3.1.js'], 57 | 'dist/app/static/ms/multiple-select.min.js': ['app/static/ms/multiple-select.js'] 58 | } 59 | } 60 | }, 61 | 62 | 'string-replace': { 63 | inline: { 64 | files: [{ 65 | 'dist/tmp/': 'app/templates/*.html' 66 | }, 67 | { 68 | 'dist/tmp/': 'app/static/*.html' 69 | }, 70 | ], 71 | options: { 72 | replacements: [{ 73 | pattern: /([\s\S]*?)/g, 74 | replacement: '' 75 | }, 76 | { 77 | pattern: /s\.css/ig, 78 | replacement: 's.min.css' 79 | }, 80 | { 81 | pattern: /gh\.css/ig, 82 | replacement: 'gh.min.css' 83 | }, 84 | { 85 | pattern: /main\.js/ig, 86 | replacement: 'main.min.js' 87 | }, 88 | { 89 | pattern: /cookie\.js/ig, 90 | replacement: 'cookie.min.js' 91 | }, 92 | { 93 | pattern: /settings\.js/ig, 94 | replacement: 'settings.min.js' 95 | }, 96 | { 97 | pattern: /jquery-3.3.1\.js/ig, 98 | replacement: 'jquery-3.3.1.min.js' 99 | }, 100 | { 101 | pattern: /multiple-select\.js/ig, 102 | replacement: 'multiple-select.min.js' 103 | }, 104 | ] 105 | } 106 | } 107 | }, 108 | 109 | htmlmin: { 110 | dist: { 111 | options: { 112 | removeComments: true, 113 | collapseWhitespace: true, 114 | conservativeCollapse: true 115 | }, 116 | files: [{ 117 | expand: true, 118 | cwd: 'dist/tmp/app/templates/', 119 | src: ['*.html', '*.html'], 120 | dest: 'dist/app/templates/' 121 | }, 122 | 123 | { 124 | expand: true, 125 | cwd: 'dist/tmp/app/static/', 126 | src: ['*.html', '*.html'], 127 | dest: 'dist/app/static/' 128 | }, 129 | 130 | { 131 | expand: true, 132 | cwd: 'app/static/', 133 | src: ['*.xml', '*.xml'], 134 | dest: 'dist/app/static/' 135 | }, 136 | ] 137 | }, 138 | }, 139 | 140 | copy: { 141 | main: { 142 | options: { 143 | mode: true, 144 | }, 145 | files: [{ 146 | expand: true, 147 | filter: 'isFile', 148 | src: [ 149 | 'app/*.py', 150 | 'app/static/favicon.ico', 151 | 'app/static/robots.txt', 152 | ], 153 | dest: 'dist/', 154 | }, ], 155 | }, 156 | }, 157 | 158 | imagemin: { 159 | compile: { 160 | options: { 161 | ext: '.png', 162 | quality: '80-90', 163 | force: true 164 | }, 165 | files: [{ 166 | expand: true, 167 | src: ['**/*.png'], 168 | cwd: 'app/static//', 169 | dest: 'dist/app/static/' 170 | }] 171 | } 172 | }, 173 | 174 | eslint: { 175 | target: ['app/static/js/*.js'] 176 | }, 177 | jsbeautifier: { 178 | dist: { 179 | src: ['app/static/js/*.js', 'app/static/*.html', 'app/templates/*.html', 'app/static/css/*.css', 180 | 'test/*.js' 181 | ], 182 | options: { 183 | config: ".jsbeautifyrc", 184 | mode: "VERIFY_ONLY" 185 | }, 186 | }, 187 | 188 | format: { 189 | src: ['app/static/js/*.js', 'app/static/*.html', 'app/templates/*.html', 'app/static/css/*.css', 190 | 'test/*.js', 'Gruntfile.js' 191 | ], 192 | options: { 193 | config: ".jsbeautifyrc", 194 | mode: "VERIFY_AND_WRITE" 195 | }, 196 | }, 197 | }, 198 | shell: { 199 | npm_test_mocha: { 200 | command: 'npm run test', 201 | } 202 | } 203 | }); 204 | 205 | grunt.loadNpmTasks('grunt-svgmin'); 206 | grunt.loadNpmTasks('grunt-contrib-cssmin'); 207 | grunt.loadNpmTasks('grunt-contrib-uglify-es'); 208 | grunt.loadNpmTasks('grunt-string-replace'); 209 | grunt.loadNpmTasks('grunt-contrib-htmlmin'); 210 | grunt.loadNpmTasks('grunt-contrib-copy'); 211 | grunt.loadNpmTasks('grunt-contrib-imagemin'); 212 | grunt.loadNpmTasks('grunt-eslint'); 213 | grunt.loadNpmTasks('grunt-jsbeautifier'); 214 | grunt.loadNpmTasks('grunt-shell'); 215 | 216 | grunt.registerTask('default', [ /*'jsbeautifier:dist',*/ 'eslint', /*'shell:npm_test_mocha',*/ 'svgmin', 'cssmin', 217 | 'uglify', 218 | 'string-replace', 'htmlmin', 'copy', 'imagemin' 219 | ]); 220 | grunt.registerTask('format', ['jsbeautifier:format']); 221 | }; 222 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Andreas Fertig 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # C++ Insights - Web Front-End 2 | 3 | [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://opensource.org/licenses/MIT) 4 | [![download](https://img.shields.io/badge/latest-download-blue.svg)](https://github.com/andreasfertig/cppinsights-web/releases) 5 | [![Build Status](https://github.com/andreasfertig/cppinsights-web/workflows/ci/badge.svg)](https://github.com/andreasfertig/cppinsights-web/actions/) 6 | [![codecov](https://codecov.io/gh/andreasfertig/cppinsights-web/branch/master/graph/badge.svg)](https://codecov.io/gh/andreasfertig/cppinsights-web) 7 | [![Try online](https://img.shields.io/badge/try-online-blue.svg)](https://cppinsights.io) 8 | 9 | 10 | 11 | [cppinsights.io](https://cppinsights.io/) is the web front-end of C++ Insights. 12 | 13 | 14 | ``` 15 | pip3 install --user virtualenv 16 | python3 -m virtualenv env 17 | source env/bin/activate 18 | pip3 install -r requirements.txt 19 | 20 | python3 -m pytest test.py 21 | pytest test.py --cov=app 22 | pytest test.py --cov=app --cov-report=html 23 | ``` 24 | 25 | ## Short Links 26 | 27 | There is now a short-link option. Via the usual way to obtain a link there is now a 28 | 'Request Short-Link' button. This requests a new short link from the back-end. 29 | 30 | Short links in C++ Insights capture the currently selected options and the code entered. They do not preserve the 31 | C++ Insights version used at the time of creation. This implies that the resulting transformation can change over time. 32 | 33 | What a user gets back is a part of a SHA1 hash from all the captured values. This is also stored in the database and 34 | used during lookup to prevent having the same code multiple times in the database. 35 | 36 | For future use, the link creation time is also stored. 37 | 38 | Please be advised to not store any confidential data in a short-link! You have no guarantees that at some point I will 39 | not loose the database (security breach, misshapening...). 40 | 41 | The primary use for short-links should be easy sharing (twitter, stack overflow, etc.). There are some cases of large code samples which do not work 42 | with long links. This is a secondary issue addressed by short-links. 43 | 44 | ## Other logos 45 | 46 | [Compiler Explorer](https://raw.githubusercontent.com/mattgodbolt/compiler-explorer-image/master/logo/icon/CompilerExplorer%20Logo%20Icon%20SVG.svg) 47 | [QuickBench](https://github.com/FredTingaud/quick-bench-front-end/blob/master/public/ico/favicon-32x32.png), SVG version 48 | from [here](https://github.com/mattgodbolt/compiler-explorer/blob/master/views/resources/quickbench.svg) 49 | -------------------------------------------------------------------------------- /app/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # C++ Insights Web, copyright (c) by Andreas Fertig 5 | # Distributed under an MIT license. See /LICENSE 6 | 7 | from flask import Flask, make_response, render_template, request, send_from_directory, jsonify, url_for, redirect 8 | from werkzeug.exceptions import HTTPException 9 | import subprocess 10 | import base64 11 | import os 12 | import tempfile 13 | import sqlite3 14 | import hashlib 15 | from datetime import date, datetime 16 | #------------------------------------------------------------------------------ 17 | 18 | app = Flask(__name__, static_folder='static', static_url_path='') 19 | app.config['MAX_CONTENT_LENGTH'] = 50 * 1024 # 50 kB 20 | app.config['USE_DOCKER'] = True # Set to False to use a local binary. 21 | app.config['USE_SUDO'] = True 22 | app.config['USE_MAC'] = False 23 | #------------------------------------------------------------------------------ 24 | 25 | @app.route('/favicon.ico') 26 | def favicon(): 27 | return send_from_directory(os.path.join(app.root_path, 'static'), 28 | 'favicon.ico', mimetype='image/vnd.microsoft.icon') 29 | #------------------------------------------------------------------------------ 30 | 31 | def getDefaultStandard(): 32 | return 'cpp20' 33 | #------------------------------------------------------------------------------ 34 | 35 | def getCommunityEventFileName(): 36 | return 'communityevent.txt' 37 | #------------------------------------------------------------------------------ 38 | 39 | def getCommunityEvent(): 40 | fName = getCommunityEventFileName() 41 | 42 | link = None 43 | title = None 44 | 45 | if os.path.exists(fName): 46 | communityEvents = open(fName, 'r').read() 47 | 48 | if 0 != len(communityEvents): 49 | link, title = communityEvents.split(';') 50 | 51 | return link, title 52 | #------------------------------------------------------------------------------ 53 | 54 | def runDocker(code, insightsOptions, cppStd, versionOnly=False): 55 | fd, fileName = tempfile.mkstemp(suffix='.cpp') 56 | try: 57 | if not versionOnly: 58 | with os.fdopen(fd, 'wb') as tmp: 59 | # write the data into the file 60 | tmp.write(code.encode('utf-8')) 61 | 62 | # FIXME (2018-04-28): workaround as docker user cannot read file without this 63 | os.chmod(fileName, 436) 64 | 65 | fileParam = [] 66 | if not versionOnly: 67 | basePath = '' 68 | 69 | # on mac for docker file must be under /private where we also find var 70 | if app.config['USE_MAC']: 71 | basePath = '/private' 72 | 73 | fileParam = [ '-v', '%s%s:/home/insights/insights.cpp' %(basePath, fileName) ] 74 | 75 | if app.config['USE_DOCKER']: 76 | # Prepend the command line with sudo 77 | if app.config['USE_SUDO']: 78 | cmd = [ 'sudo', '-u', 'pfes' ] 79 | else: 80 | cmd = [] 81 | 82 | cmd.extend([ 'docker', 'run', '--net=none' ]) 83 | cmd.extend(fileParam) 84 | cmd.extend(['--rm', '-i', 'cppinsights-container']) 85 | else: 86 | cmd = ['insights', fileName] 87 | 88 | 89 | if None != insightsOptions: 90 | cmd.extend(insightsOptions) 91 | 92 | if (None != insightsOptions or None != cppStd) or versionOnly: 93 | cmd.append('--') 94 | 95 | if None != cppStd: 96 | cppStd = cppStd.replace('2a', '20') # for transitioning 97 | cmd.append(cppStd) 98 | 99 | p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 100 | 101 | stdout, stderr = p.communicate(timeout=20) 102 | returncode = p.returncode 103 | 104 | finally: 105 | os.remove(fileName) 106 | 107 | return stdout.decode('utf-8'), stderr.decode('utf-8'), returncode 108 | #------------------------------------------------------------------------------ 109 | 110 | def buildResponse(code, stdout, stderr, insightsOptions, errCode, twcard=False, desc=''): 111 | if twcard: 112 | twdesc = code[: 100] 113 | 114 | if desc: 115 | desc = ' - ' + desc 116 | 117 | communitylink, communitytitle = getCommunityEvent() 118 | communityEventHide = '' 119 | 120 | if None == communitylink or None == communitytitle: 121 | communityEventHide = 'nocommunityevent' 122 | 123 | selectedInsightsOptions = getInsightsSelections(insightsOptions) 124 | response = make_response(render_template('index.html', **locals())) 125 | 126 | return response, errCode 127 | #------------------------------------------------------------------------------ 128 | 129 | def error_handler(errCode, code): 130 | stderr = 'Failed' 131 | stdout = '// Sorry, but your request failed due to a server error:\n// %s\n\n// Sorry for the inconvenience.\n// Please feel free to report this error.' %(errCode) 132 | 133 | return buildResponse('// ' + code, stdout, stderr, [] , errCode) 134 | #------------------------------------------------------------------------------ 135 | 136 | def getSupportedOptions(): 137 | opts = [ {'desc': 'C++ Standard' , 'flag' : '', 'name' : 'C++ Standard', 'selected' : False, 'label' : True, 'single' : False , 'ccopt' : False, 'cppStd' : False }, 138 | {'desc': 'cpp98' , 'flag' : '-std=c++98', 'name' : 'C++ 98', 'selected' : False, 'label' : False, 'single' : True , 'ccopt' : True, 'cppStd' : True }, 139 | {'desc': 'cpp11' , 'flag' : '-std=c++11', 'name' : 'C++ 11', 'selected' : False, 'label' : False, 'single' : True , 'ccopt' : True, 'cppStd' : True }, 140 | {'desc': 'cpp14' , 'flag' : '-std=c++14', 'name' : 'C++ 14', 'selected' : False, 'label' : False, 'single' : True , 'ccopt' : True, 'cppStd' : True }, 141 | {'desc': 'cpp17' , 'flag' : '-std=c++17', 'name' : 'C++ 17', 'selected' : False, 'label' : False, 'single' : True , 'ccopt' : True, 'cppStd' : True }, 142 | {'desc': 'cpp20' , 'flag' : '-std=c++20', 'name' : 'C++ 20', 'selected' : False, 'label' : False, 'single' : True , 'ccopt' : True, 'cppStd' : True }, 143 | {'desc': 'cpp23' , 'flag' : '-std=c++23', 'name' : 'C++ 23', 'selected' : False, 'label' : False, 'single' : True , 'ccopt' : True, 'cppStd' : True }, 144 | {'desc': 'cpp2c' , 'flag' : '-std=c++2c', 'name' : 'C++ 2c', 'selected' : False, 'label' : False, 'single' : True , 'ccopt' : True, 'cppStd' : True }, 145 | {'desc': 'Alternative Styles' , 'flag' : '', 'name' : 'Alternative Styles', 'selected' : False, 'label' : True, 'single' : False , 'ccopt' : False, 'cppStd' : False }, 146 | {'desc': 'alt-syntax-for' , 'flag' : '-alt-syntax-for', 'name' : 'for-loops as while-loops', 'selected' : False, 'label' : False, 'single' : False , 'ccopt' : False, 'cppStd' : False }, 147 | {'desc': 'alt-syntax-subscription' , 'flag' : '-alt-syntax-subscription', 'name' : 'array subscription', 'selected' : False, 'label' : False, 'single' : False , 'ccopt' : False, 'cppStd' : False }, 148 | {'desc': 'More Transformations' , 'flag' : '', 'name' : 'More Transformations', 'selected' : False, 'label' : True, 'single' : False , 'ccopt' : False, 'cppStd' : False }, 149 | {'desc': 'all-implicit-casts' , 'flag' : '-show-all-implicit-casts', 'name' : 'Show all implicit casts', 'selected' : False, 'label' : False, 'single' : False , 'ccopt' : False, 'cppStd' : False }, 150 | {'desc': 'show-all-callexpr-template-parameters' , 'flag' : '-show-all-callexpr-template-parameters', 'name' : 'Show all template parameters of a CallExpr', 'selected' : False, 'label' : False, 'single' : False , 'ccopt' : False, 'cppStd' : False }, 151 | {'desc': 'use-libcpp' , 'flag' : '-use-libc++', 'name' : 'Use libc++', 'selected' : False, 'label' : False, 'single' : False , 'ccopt' : False, 'cppStd' : False }, 152 | {'desc': 'edu-show-initlist' , 'flag' : '-edu-show-initlist', 'name' : 'Transform std::initializer_list', 'selected' : False, 'label' : False, 'single' : False , 'ccopt' : False, 'cppStd' : False }, 153 | {'desc': 'edu-show-noexcept' , 'flag' : '-edu-show-noexcept', 'name' : 'Show noexcept internals', 'selected' : False, 'label' : False, 'single' : False , 'ccopt' : False, 'cppStd' : False }, 154 | {'desc': 'edu-show-padding' , 'flag' : '-edu-show-padding', 'name' : 'Show padding information', 'selected' : False, 'label' : False, 'single' : False , 'ccopt' : False, 'cppStd' : False }, 155 | {'desc': 'edu-show-coroutines' , 'flag' : '-edu-show-coroutine-transformation', 'name' : 'Show coroutine transformation', 'selected' : False, 'label' : False, 'single' : False , 'ccopt' : False, 'cppStd' : False }, 156 | {'desc': 'edu-show-cfront' , 'flag' : '-edu-show-cfront', 'name' : 'Show C++ to C transformation', 'selected' : False, 'label' : False, 'single' : False , 'ccopt' : False, 'cppStd' : False }, 157 | {'desc': 'edu-show-lifetime' , 'flag' : '-edu-show-lifetime', 'name' : 'Show object lifetime', 'selected' : False, 'label' : False, 'single' : False , 'ccopt' : False, 'cppStd' : False }, 158 | ] 159 | 160 | return opts 161 | #------------------------------------------------------------------------------ 162 | 163 | def getInsightsSelections(selected): 164 | stdSelections = getSupportedOptions() 165 | bHaveCppStd = False 166 | 167 | for opt in selected: 168 | for e in stdSelections: 169 | if opt == e['desc']: 170 | e['selected'] = True 171 | 172 | if True == e['cppStd']: 173 | bHaveCppStd = True 174 | 175 | # check that at least one C++ standard is selected, if not insert the default. 176 | if not bHaveCppStd: 177 | for e in stdSelections: 178 | if e['desc'] == getDefaultStandard(): 179 | e['selected'] = True 180 | 181 | return stdSelections 182 | #------------------------------------------------------------------------------ 183 | 184 | def render(insightsOptions, code, twcard=False, desc=''): 185 | stdout = '' 186 | stderr = '' 187 | 188 | return buildResponse(code, stdout, stderr, insightsOptions, 200, twcard, desc) 189 | #------------------------------------------------------------------------------ 190 | 191 | def getValidInsightsOptions(options): 192 | validOpts = getSupportedOptions() 193 | opts = [] 194 | 195 | for opt in options: 196 | for e in validOpts: 197 | if e['desc'] == opt: 198 | opts.append(e) 199 | 200 | return opts 201 | #------------------------------------------------------------------------------ 202 | 203 | @app.route("/api/v1/transform", methods=['POST']) 204 | def api(): 205 | content = request.json 206 | code = content['code'] 207 | options = getValidInsightsOptions(content['insightsOptions']) 208 | 209 | insightsOptions = [opt['flag'] for opt in options if not opt['ccopt']] 210 | cppStd = [opt['flag'] for opt in options if opt['ccopt']] 211 | 212 | if 0 == len(cppStd): 213 | cppStd = [ getValidInsightsOptions([getDefaultStandard()])[0]['flag'] ] 214 | 215 | stdout, stderr, returncode = runDocker(code, insightsOptions, cppStd[0]) 216 | 217 | if (None == stderr) or ('' == stderr): 218 | stderr = 'Insights exited with result code: %d' %(returncode) 219 | 220 | if returncode: 221 | stdout = 'Compilation failed!' 222 | 223 | resp = {} 224 | resp['returncode'] = returncode 225 | resp['stdout'] = stdout 226 | resp['stderr'] = stderr 227 | 228 | 229 | return jsonify(resp) 230 | #------------------------------------------------------------------------------ 231 | 232 | @app.route("/api/v1/getshortlink", methods=['POST']) 233 | def getShortLink(): 234 | content = request.json 235 | code = content['code'] 236 | desc = content['desc'] 237 | rev = content['rev'] 238 | cppStd = content['std'] 239 | options = '|'.join(content['options']) 240 | toolId = 1 # reserved in case we will have a windows container later. 241 | 242 | code = decodeCode(code) 243 | desc = decodeCode(desc) 244 | 245 | # check for missing code 246 | if None == code: 247 | resp = {} 248 | resp['returncode'] = 2 249 | resp['shortlink'] = 'No source' 250 | 251 | return jsonify(resp) 252 | 253 | # limit content length 254 | elif len(code) > 1000000: # 1 MB 255 | resp = {} 256 | resp['returncode'] = 1 257 | resp['shortlink'] = 'Source too long' 258 | 259 | return jsonify(resp) 260 | 261 | conn = sqlite3.connect(getDbName()) 262 | 263 | c = conn.cursor() 264 | 265 | # Create the structure if it does not exist 266 | c.execute('''CREATE TABLE IF NOT EXISTS shortened (id INTEGER primary key autoincrement, toolid INTEGER, code TEXT NOT NULL, desc TEXT NOT NULL, rev text NOT NULL, cppStd TEXT NOT NULL, options TEXT NOT NULL, short TEXT NOT NULL, create_at TIMESTAMP)''') 267 | 268 | # Check if there is already an entry for this combination 269 | cur = c.execute('SELECT short FROM shortened WHERE code=? AND rev=? AND cppStd=? AND options=?', (code, rev, cppStd, options,)) 270 | 271 | rv = cur.fetchone() 272 | 273 | # Pack all together for the unique hash 274 | full = code + rev + cppStd + options 275 | hash_object = hashlib.sha1(full.encode('utf-8')) 276 | hex_dig = hash_object.hexdigest() 277 | surl = hex_dig[ : 8] 278 | retVal = 0 279 | 280 | if None == rv: 281 | now = datetime.now() 282 | c.execute('INSERT INTO shortened (toolid, code, desc, rev, cppStd, options, short, create_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)', (toolId, code, desc, rev, cppStd, options, surl, now,)) 283 | else: 284 | retVal = 1 285 | surl = rv[0] 286 | 287 | 288 | # Commit and close 289 | conn.commit() 290 | conn.close() 291 | 292 | resp = {} 293 | resp['returncode'] = retVal 294 | resp['shortlink'] = '/s/%s' %(surl) 295 | 296 | return jsonify(resp) 297 | #------------------------------------------------------------------------------ 298 | 299 | def getVersionInfo(): 300 | stdout, stderr, returncode = runDocker('', None, None, True) 301 | 302 | if (None == stderr) or ('' == stderr): 303 | stderr = 'Insights exited with result code: %d' %(returncode) 304 | 305 | if returncode: 306 | stdout = 'Compilation failed!' 307 | 308 | resp = {} 309 | resp['returncode'] = returncode 310 | resp['stdout'] = stdout 311 | resp['stderr'] = stderr 312 | 313 | dockerImage = 'cppinsights-container' 314 | if app.config['USE_DOCKER']: 315 | p = subprocess.Popen(['docker', 'images', '--filter=reference=%s' %(dockerImage), '--format', '{{.ID}} {{.CreatedAt}}'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) 316 | 317 | stdout, stderr = p.communicate(timeout=20) 318 | stdout = stdout.decode('utf-8') 319 | returncode = p.returncode 320 | else: 321 | stdout = 'Docker not used' 322 | 323 | resp['stdout'] += '\nDocker image "%s" info: ' %(dockerImage) + stdout + '\n' 324 | 325 | return resp 326 | #------------------------------------------------------------------------------ 327 | 328 | @app.route("/api/v1/version", methods=['GET']) 329 | def apiversion(): 330 | resp = getVersionInfo() 331 | 332 | return jsonify(resp) 333 | #------------------------------------------------------------------------------ 334 | 335 | @app.route("/version", methods=['GET']) 336 | def version(): 337 | resp = getVersionInfo() 338 | 339 | version = resp['stdout'] 340 | version = version.replace('\n', '
') 341 | 342 | response = make_response(render_template('version.html', **locals())) 343 | 344 | return response 345 | #------------------------------------------------------------------------------ 346 | 347 | def getDbName(): 348 | return 'urls.db' 349 | #------------------------------------------------------------------------------ 350 | 351 | def decodeCode(code): 352 | try: 353 | # keep this in mind if, we get a incorrect length base64 string 354 | #code = code + '=' * (-len(code) % 4) 355 | # XXX: somehow we get a corrupt base64 string 356 | code = code.replace(' ', '+') 357 | 358 | # base 64 decode 359 | return base64.b64decode(code).decode('utf-8') 360 | 361 | except: 362 | print(repr(code)) 363 | 364 | return None 365 | #------------------------------------------------------------------------------ 366 | 367 | @app.route("/", methods=['GET']) 368 | def index(): 369 | code = '' 370 | 371 | return render([getDefaultStandard()], code) 372 | #------------------------------------------------------------------------------ 373 | 374 | def proccessLink(code, desc, cppStd, insightsOptions, rev): 375 | if not rev or '1.0' != rev: 376 | return error_handler(404, 'The revision of the link is invalid.') 377 | 378 | return render(insightsOptions, code, twcard=True, desc=desc) 379 | #------------------------------------------------------------------------------ 380 | 381 | @app.route("/lnk", methods=['GET', 'POST']) 382 | def lnk(): 383 | code = request.args.get('code', '') 384 | rev = request.args.get('rev', '') 385 | cppStd = request.args.get('std', None) 386 | 387 | rawInsightsOptions = request.args.get('insightsOptions', '') 388 | insightsOptions = rawInsightsOptions.split(',') 389 | 390 | # If this is an old link it has 'std' set. In this case use it. 391 | if None != cppStd: 392 | insightsOptions.append(cppStd) 393 | 394 | if code: 395 | code = decodeCode(code) 396 | 397 | if None == code: 398 | code = '' 399 | 400 | return proccessLink(code, '', cppStd, insightsOptions, rev) 401 | #------------------------------------------------------------------------------ 402 | 403 | @app.route("/s/") 404 | def slnk(link): 405 | conn = sqlite3.connect(getDbName()) 406 | c = conn.cursor() 407 | 408 | cur = c.execute('SELECT code, desc, rev, cppStd, options FROM shortened WHERE short = ?', (link,)) 409 | 410 | rv = cur.fetchone() 411 | 412 | conn.close() 413 | 414 | if None == rv: 415 | return error_handler(404, 'There is no such link.') 416 | 417 | code = rv[0] 418 | desc = rv[1] 419 | rev = rv[2] 420 | cppStd = rv[3] 421 | rawInsightsOptions = rv[4] 422 | insightsOptions = rawInsightsOptions.split('|') 423 | 424 | return proccessLink(code, desc, cppStd, insightsOptions, rev) 425 | #------------------------------------------------------------------------------ 426 | 427 | 428 | @app.errorhandler(404) 429 | def page_not_found(e): 430 | # print(request.path) 431 | # return redirect("/", code=302) 432 | return render_template('404.html'), 404 433 | #------------------------------------------------------------------------------ 434 | 435 | @app.errorhandler(413) 436 | def request_to_large(e): 437 | return error_handler(413, '') 438 | #------------------------------------------------------------------------------ 439 | 440 | @app.errorhandler(Exception) 441 | def other_errors(e): 442 | code = request.form.get('code', '') 443 | ecode = 500 444 | 445 | print(e) 446 | 447 | if isinstance(e, HTTPException): 448 | ecode = e.code 449 | 450 | return error_handler(ecode, code) 451 | #------------------------------------------------------------------------------ 452 | 453 | def getApp(): 454 | return app 455 | #------------------------------------------------------------------------------ 456 | 457 | 458 | if __name__ == "__main__": 459 | app.run(host='0.0.0.0') 460 | #------------------------------------------------------------------------------ 461 | 462 | -------------------------------------------------------------------------------- /app/static/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andreasfertig/cppinsights-web/13b1b4886e5037313c9a8957332aead088c27795/app/static/16.png -------------------------------------------------------------------------------- /app/static/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andreasfertig/cppinsights-web/13b1b4886e5037313c9a8957332aead088c27795/app/static/32.png -------------------------------------------------------------------------------- /app/static/about.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | C++ Insights 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 52 | 53 | 54 |

About

55 | 56 |

C++ Insights is a clang-based tool which 57 | does a source to source transformation. Its goal is to make things visible, which normally and intentionally 58 | happen behind the scenes. It's about the magic the compiler does for us to make things work. Or looking through 59 | the classes of a compiler.

60 | 61 |

Some time ago, I started looking into some new things we got with C++11, C++14, C++17 and C++20. Amazing 62 | things like 63 | lambdas, range-based for-loops, and structured bindings. I put it together in a talk. You can find the slides 65 | and a video online.

66 | 67 |

However, all that research and some of my training and teaching got me to start thinking about how it would be 68 | if we 69 | could see 70 | with the eyes of the compiler. Sure, there is an AST-dump, at least for clang. With tools like Compiler Explorer, we can see what code the compiler generates from a C++ 72 | source snippet. However, what we see is assembler. Neither the AST nor the Compiler 73 | Explorer output is in the language I write code, and therefore I'm most familiar with. Plus, when 74 | teaching 75 | students C++ showing an AST and explaining that it is all there was not quite satisfying for me.

76 | 77 |

I started to write a clang-based tool able to transform 78 | a 79 | range-based for-loop into the compiler-internal version. Then, I did the same for structured bindings and 80 | lambdas. In the end, I ended up doing a lot more as initially planned. It shows where operators are 81 | invoked, 82 | places in which the compiler does some casting. C++ Insights is able to deduce the type behind 83 | auto or decltype. The goal is to produce compilable code. However, this is not 84 | possible 85 | in all places.

86 | 87 |

Still, there is work to do.

88 | 89 |

I do not claim to get all the things right. This is just the initial version of C++ Insights I 90 | consider good enough to hand 91 | it to the public. Also, keep in mind that it is solely based on clang and my understanding of C++ and the AST.

92 | 93 |

You can see, for example, the transformation of a lambda, 94 | range-based 95 | for-loop, or auto. 96 | Of course, you can transform any other C++ snippet.

97 | 98 |

For those who like videos, there is a C++ Insights series on Youtube. If you have topics you like me to speak 100 | about, please reach out to me.

101 | 102 |

The version information of the C++ Insights version running on this website can be found here: https://cppinsights.io/version.

103 |

You can find the source of the web front-end 104 | and 105 | the tool C++ Insights on Github.

106 |

If you like to support the project, consider submitting 107 | a patch. Another alternative is to become a Patreon 108 | supporter.

109 |

Contact: andy at cppinsights.io

110 | 111 | 115 |
116 | 117 |
118 | 119 | 120 | 121 | 122 | -------------------------------------------------------------------------------- /app/static/cm/Readme.md: -------------------------------------------------------------------------------- 1 | This is a third-party component and not covered by the license of the C++ Insights project. 2 | -------------------------------------------------------------------------------- /app/static/cm/active-line.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: http://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | var WRAP_CLASS = "CodeMirror-activeline"; 14 | var BACK_CLASS = "CodeMirror-activeline-background"; 15 | var GUTT_CLASS = "CodeMirror-activeline-gutter"; 16 | 17 | CodeMirror.defineOption("styleActiveLine", false, function(cm, val, old) { 18 | var prev = old == CodeMirror.Init ? false : old; 19 | if (val == prev) return 20 | if (prev) { 21 | cm.off("beforeSelectionChange", selectionChange); 22 | clearActiveLines(cm); 23 | delete cm.state.activeLines; 24 | } 25 | if (val) { 26 | cm.state.activeLines = []; 27 | updateActiveLines(cm, cm.listSelections()); 28 | cm.on("beforeSelectionChange", selectionChange); 29 | } 30 | }); 31 | 32 | function clearActiveLines(cm) { 33 | for (var i = 0; i < cm.state.activeLines.length; i++) { 34 | cm.removeLineClass(cm.state.activeLines[i], "wrap", WRAP_CLASS); 35 | cm.removeLineClass(cm.state.activeLines[i], "background", BACK_CLASS); 36 | cm.removeLineClass(cm.state.activeLines[i], "gutter", GUTT_CLASS); 37 | } 38 | } 39 | 40 | function sameArray(a, b) { 41 | if (a.length != b.length) return false; 42 | for (var i = 0; i < a.length; i++) 43 | if (a[i] != b[i]) return false; 44 | return true; 45 | } 46 | 47 | function updateActiveLines(cm, ranges) { 48 | var active = []; 49 | for (var i = 0; i < ranges.length; i++) { 50 | var range = ranges[i]; 51 | var option = cm.getOption("styleActiveLine"); 52 | if (typeof option == "object" && option.nonEmpty ? range.anchor.line != range.head.line : !range.empty()) 53 | continue 54 | var line = cm.getLineHandleVisualStart(range.head.line); 55 | if (active[active.length - 1] != line) active.push(line); 56 | } 57 | if (sameArray(cm.state.activeLines, active)) return; 58 | cm.operation(function() { 59 | clearActiveLines(cm); 60 | for (var i = 0; i < active.length; i++) { 61 | cm.addLineClass(active[i], "wrap", WRAP_CLASS); 62 | cm.addLineClass(active[i], "background", BACK_CLASS); 63 | cm.addLineClass(active[i], "gutter", GUTT_CLASS); 64 | } 65 | cm.state.activeLines = active; 66 | }); 67 | } 68 | 69 | function selectionChange(cm, sel) { 70 | updateActiveLines(cm, sel.ranges); 71 | } 72 | }); 73 | -------------------------------------------------------------------------------- /app/static/cm/codemirror.css: -------------------------------------------------------------------------------- 1 | /* BASICS */ 2 | 3 | .CodeMirror { 4 | /* Set height, width, borders, and global font properties here */ 5 | font-family: monospace; 6 | height: 300px; 7 | color: black; 8 | direction: ltr; 9 | } 10 | 11 | /* PADDING */ 12 | 13 | .CodeMirror-lines { 14 | padding: 4px 0; /* Vertical padding around content */ 15 | } 16 | .CodeMirror pre { 17 | padding: 0 4px; /* Horizontal padding of content */ 18 | } 19 | 20 | .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 21 | background-color: white; /* The little square between H and V scrollbars */ 22 | } 23 | 24 | /* GUTTER */ 25 | 26 | .CodeMirror-gutters { 27 | border-right: 1px solid #ddd; 28 | background-color: #f7f7f7; 29 | white-space: nowrap; 30 | } 31 | .CodeMirror-linenumbers {} 32 | .CodeMirror-linenumber { 33 | padding: 0 3px 0 5px; 34 | min-width: 20px; 35 | text-align: right; 36 | color: #999; 37 | white-space: nowrap; 38 | } 39 | 40 | .CodeMirror-guttermarker { color: black; } 41 | .CodeMirror-guttermarker-subtle { color: #999; } 42 | 43 | /* CURSOR */ 44 | 45 | .CodeMirror-cursor { 46 | border-left: 1px solid black; 47 | border-right: none; 48 | width: 0; 49 | } 50 | /* Shown when moving in bi-directional text */ 51 | .CodeMirror div.CodeMirror-secondarycursor { 52 | border-left: 1px solid silver; 53 | } 54 | .cm-fat-cursor .CodeMirror-cursor { 55 | width: auto; 56 | border: 0 !important; 57 | background: #7e7; 58 | } 59 | .cm-fat-cursor div.CodeMirror-cursors { 60 | z-index: 1; 61 | } 62 | .cm-fat-cursor-mark { 63 | background-color: rgba(20, 255, 20, 0.5); 64 | -webkit-animation: blink 1.06s steps(1) infinite; 65 | -moz-animation: blink 1.06s steps(1) infinite; 66 | animation: blink 1.06s steps(1) infinite; 67 | } 68 | .cm-animate-fat-cursor { 69 | width: auto; 70 | border: 0; 71 | -webkit-animation: blink 1.06s steps(1) infinite; 72 | -moz-animation: blink 1.06s steps(1) infinite; 73 | animation: blink 1.06s steps(1) infinite; 74 | background-color: #7e7; 75 | } 76 | @-moz-keyframes blink { 77 | 0% {} 78 | 50% { background-color: transparent; } 79 | 100% {} 80 | } 81 | @-webkit-keyframes blink { 82 | 0% {} 83 | 50% { background-color: transparent; } 84 | 100% {} 85 | } 86 | @keyframes blink { 87 | 0% {} 88 | 50% { background-color: transparent; } 89 | 100% {} 90 | } 91 | 92 | /* Can style cursor different in overwrite (non-insert) mode */ 93 | .CodeMirror-overwrite .CodeMirror-cursor {} 94 | 95 | .cm-tab { display: inline-block; text-decoration: inherit; } 96 | 97 | .CodeMirror-rulers { 98 | position: absolute; 99 | left: 0; right: 0; top: -50px; bottom: -20px; 100 | overflow: hidden; 101 | } 102 | .CodeMirror-ruler { 103 | border-left: 1px solid #ccc; 104 | top: 0; bottom: 0; 105 | position: absolute; 106 | } 107 | 108 | /* DEFAULT THEME */ 109 | 110 | .cm-s-default .cm-header {color: blue;} 111 | .cm-s-default .cm-quote {color: #090;} 112 | .cm-negative {color: #d44;} 113 | .cm-positive {color: #292;} 114 | .cm-header, .cm-strong {font-weight: bold;} 115 | .cm-em {font-style: italic;} 116 | .cm-link {text-decoration: underline;} 117 | .cm-strikethrough {text-decoration: line-through;} 118 | 119 | .cm-s-default .cm-keyword {color: #708;} 120 | .cm-s-default .cm-atom {color: #219;} 121 | .cm-s-default .cm-number {color: #164;} 122 | .cm-s-default .cm-def {color: #00f;} 123 | .cm-s-default .cm-variable, 124 | .cm-s-default .cm-punctuation, 125 | .cm-s-default .cm-property, 126 | .cm-s-default .cm-operator {} 127 | .cm-s-default .cm-variable-2 {color: #05a;} 128 | .cm-s-default .cm-variable-3, .cm-s-default .cm-type {color: #085;} 129 | .cm-s-default .cm-comment {color: #a50;} 130 | .cm-s-default .cm-string {color: #a11;} 131 | .cm-s-default .cm-string-2 {color: #f50;} 132 | .cm-s-default .cm-meta {color: #555;} 133 | .cm-s-default .cm-qualifier {color: #555;} 134 | .cm-s-default .cm-builtin {color: #30a;} 135 | .cm-s-default .cm-bracket {color: #997;} 136 | .cm-s-default .cm-tag {color: #170;} 137 | .cm-s-default .cm-attribute {color: #00c;} 138 | .cm-s-default .cm-hr {color: #999;} 139 | .cm-s-default .cm-link {color: #00c;} 140 | 141 | .cm-s-default .cm-error {color: #f00;} 142 | .cm-invalidchar {color: #f00;} 143 | 144 | .CodeMirror-composing { border-bottom: 2px solid; } 145 | 146 | /* Default styles for common addons */ 147 | 148 | div.CodeMirror span.CodeMirror-matchingbracket {color: #0b0;} 149 | div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;} 150 | .CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); } 151 | .CodeMirror-activeline-background {background: #e8f2ff;} 152 | 153 | /* STOP */ 154 | 155 | /* The rest of this file contains styles related to the mechanics of 156 | the editor. You probably shouldn't touch them. */ 157 | 158 | .CodeMirror { 159 | position: relative; 160 | overflow: hidden; 161 | background: white; 162 | } 163 | 164 | .CodeMirror-scroll { 165 | overflow: scroll !important; /* Things will break if this is overridden */ 166 | /* 30px is the magic margin used to hide the element's real scrollbars */ 167 | /* See overflow: hidden in .CodeMirror */ 168 | margin-bottom: -30px; margin-right: -30px; 169 | padding-bottom: 30px; 170 | height: 100%; 171 | outline: none; /* Prevent dragging from highlighting the element */ 172 | position: relative; 173 | } 174 | .CodeMirror-sizer { 175 | position: relative; 176 | border-right: 30px solid transparent; 177 | } 178 | 179 | /* The fake, visible scrollbars. Used to force redraw during scrolling 180 | before actual scrolling happens, thus preventing shaking and 181 | flickering artifacts. */ 182 | .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 183 | position: absolute; 184 | z-index: 6; 185 | display: none; 186 | } 187 | .CodeMirror-vscrollbar { 188 | right: 0; top: 0; 189 | overflow-x: hidden; 190 | overflow-y: scroll; 191 | } 192 | .CodeMirror-hscrollbar { 193 | bottom: 0; left: 0; 194 | overflow-y: hidden; 195 | overflow-x: scroll; 196 | } 197 | .CodeMirror-scrollbar-filler { 198 | right: 0; bottom: 0; 199 | } 200 | .CodeMirror-gutter-filler { 201 | left: 0; bottom: 0; 202 | } 203 | 204 | .CodeMirror-gutters { 205 | position: absolute; left: 0; top: 0; 206 | min-height: 100%; 207 | z-index: 3; 208 | } 209 | .CodeMirror-gutter { 210 | white-space: normal; 211 | height: 100%; 212 | display: inline-block; 213 | vertical-align: top; 214 | margin-bottom: -30px; 215 | } 216 | .CodeMirror-gutter-wrapper { 217 | position: absolute; 218 | z-index: 4; 219 | background: none !important; 220 | border: none !important; 221 | } 222 | .CodeMirror-gutter-background { 223 | position: absolute; 224 | top: 0; bottom: 0; 225 | z-index: 4; 226 | } 227 | .CodeMirror-gutter-elt { 228 | position: absolute; 229 | cursor: default; 230 | z-index: 4; 231 | } 232 | .CodeMirror-gutter-wrapper ::selection { background-color: transparent } 233 | .CodeMirror-gutter-wrapper ::-moz-selection { background-color: transparent } 234 | 235 | .CodeMirror-lines { 236 | cursor: text; 237 | min-height: 1px; /* prevents collapsing before first draw */ 238 | } 239 | .CodeMirror pre { 240 | /* Reset some styles that the rest of the page might have set */ 241 | -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; 242 | border-width: 0; 243 | background: transparent; 244 | font-family: inherit; 245 | font-size: inherit; 246 | margin: 0; 247 | white-space: pre; 248 | word-wrap: normal; 249 | line-height: inherit; 250 | color: inherit; 251 | z-index: 2; 252 | position: relative; 253 | overflow: visible; 254 | -webkit-tap-highlight-color: transparent; 255 | -webkit-font-variant-ligatures: contextual; 256 | font-variant-ligatures: contextual; 257 | } 258 | .CodeMirror-wrap pre { 259 | word-wrap: break-word; 260 | white-space: pre-wrap; 261 | word-break: normal; 262 | } 263 | 264 | .CodeMirror-linebackground { 265 | position: absolute; 266 | left: 0; right: 0; top: 0; bottom: 0; 267 | z-index: 0; 268 | } 269 | 270 | .CodeMirror-linewidget { 271 | position: relative; 272 | z-index: 2; 273 | padding: 0.1px; /* Force widget margins to stay inside of the container */ 274 | } 275 | 276 | .CodeMirror-widget {} 277 | 278 | .CodeMirror-rtl pre { direction: rtl; } 279 | 280 | .CodeMirror-code { 281 | outline: none; 282 | } 283 | 284 | /* Force content-box sizing for the elements where we expect it */ 285 | .CodeMirror-scroll, 286 | .CodeMirror-sizer, 287 | .CodeMirror-gutter, 288 | .CodeMirror-gutters, 289 | .CodeMirror-linenumber { 290 | -moz-box-sizing: content-box; 291 | box-sizing: content-box; 292 | } 293 | 294 | .CodeMirror-measure { 295 | position: absolute; 296 | width: 100%; 297 | height: 0; 298 | overflow: hidden; 299 | visibility: hidden; 300 | } 301 | 302 | .CodeMirror-cursor { 303 | position: absolute; 304 | pointer-events: none; 305 | } 306 | .CodeMirror-measure pre { position: static; } 307 | 308 | div.CodeMirror-cursors { 309 | visibility: hidden; 310 | position: relative; 311 | z-index: 3; 312 | } 313 | div.CodeMirror-dragcursors { 314 | visibility: visible; 315 | } 316 | 317 | .CodeMirror-focused div.CodeMirror-cursors { 318 | visibility: visible; 319 | } 320 | 321 | .CodeMirror-selected { background: #d9d9d9; } 322 | .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; } 323 | .CodeMirror-crosshair { cursor: crosshair; } 324 | 325 | .cm-searching { 326 | background-color: #ffa; 327 | background-color: rgba(255, 255, 0, .4); 328 | } 329 | 330 | /* Used to force a border model for a node */ 331 | .cm-force-border { padding-right: .1px; } 332 | 333 | @media print { 334 | /* Hide the cursor when printing */ 335 | .CodeMirror div.CodeMirror-cursors { 336 | visibility: hidden; 337 | } 338 | } 339 | 340 | /* See issue #2901 */ 341 | .cm-tab-wrap-hack:after { content: ''; } 342 | 343 | /* Help users use markselection to safely style text background */ 344 | span.CodeMirror-selectedtext { background: none; } 345 | -------------------------------------------------------------------------------- /app/static/cm/matchbrackets.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: http://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | var ie_lt8 = /MSIE \d/.test(navigator.userAgent) && 13 | (document.documentMode == null || document.documentMode < 8); 14 | 15 | var Pos = CodeMirror.Pos; 16 | 17 | var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"}; 18 | 19 | function findMatchingBracket(cm, where, config) { 20 | var line = cm.getLineHandle(where.line), pos = where.ch - 1; 21 | var afterCursor = config && config.afterCursor 22 | if (afterCursor == null) 23 | afterCursor = /(^| )cm-fat-cursor($| )/.test(cm.getWrapperElement().className) 24 | 25 | // A cursor is defined as between two characters, but in in vim command mode 26 | // (i.e. not insert mode), the cursor is visually represented as a 27 | // highlighted box on top of the 2nd character. Otherwise, we allow matches 28 | // from before or after the cursor. 29 | var match = (!afterCursor && pos >= 0 && matching[line.text.charAt(pos)]) || 30 | matching[line.text.charAt(++pos)]; 31 | if (!match) return null; 32 | var dir = match.charAt(1) == ">" ? 1 : -1; 33 | if (config && config.strict && (dir > 0) != (pos == where.ch)) return null; 34 | var style = cm.getTokenTypeAt(Pos(where.line, pos + 1)); 35 | 36 | var found = scanForBracket(cm, Pos(where.line, pos + (dir > 0 ? 1 : 0)), dir, style || null, config); 37 | if (found == null) return null; 38 | return {from: Pos(where.line, pos), to: found && found.pos, 39 | match: found && found.ch == match.charAt(0), forward: dir > 0}; 40 | } 41 | 42 | // bracketRegex is used to specify which type of bracket to scan 43 | // should be a regexp, e.g. /[[\]]/ 44 | // 45 | // Note: If "where" is on an open bracket, then this bracket is ignored. 46 | // 47 | // Returns false when no bracket was found, null when it reached 48 | // maxScanLines and gave up 49 | function scanForBracket(cm, where, dir, style, config) { 50 | var maxScanLen = (config && config.maxScanLineLength) || 10000; 51 | var maxScanLines = (config && config.maxScanLines) || 1000; 52 | 53 | var stack = []; 54 | var re = config && config.bracketRegex ? config.bracketRegex : /[(){}[\]]/; 55 | var lineEnd = dir > 0 ? Math.min(where.line + maxScanLines, cm.lastLine() + 1) 56 | : Math.max(cm.firstLine() - 1, where.line - maxScanLines); 57 | for (var lineNo = where.line; lineNo != lineEnd; lineNo += dir) { 58 | var line = cm.getLine(lineNo); 59 | if (!line) continue; 60 | var pos = dir > 0 ? 0 : line.length - 1, end = dir > 0 ? line.length : -1; 61 | if (line.length > maxScanLen) continue; 62 | if (lineNo == where.line) pos = where.ch - (dir < 0 ? 1 : 0); 63 | for (; pos != end; pos += dir) { 64 | var ch = line.charAt(pos); 65 | if (re.test(ch) && (style === undefined || cm.getTokenTypeAt(Pos(lineNo, pos + 1)) == style)) { 66 | var match = matching[ch]; 67 | if ((match.charAt(1) == ">") == (dir > 0)) stack.push(ch); 68 | else if (!stack.length) return {pos: Pos(lineNo, pos), ch: ch}; 69 | else stack.pop(); 70 | } 71 | } 72 | } 73 | return lineNo - dir == (dir > 0 ? cm.lastLine() : cm.firstLine()) ? false : null; 74 | } 75 | 76 | function matchBrackets(cm, autoclear, config) { 77 | // Disable brace matching in long lines, since it'll cause hugely slow updates 78 | var maxHighlightLen = cm.state.matchBrackets.maxHighlightLineLength || 1000; 79 | var marks = [], ranges = cm.listSelections(); 80 | for (var i = 0; i < ranges.length; i++) { 81 | var match = ranges[i].empty() && findMatchingBracket(cm, ranges[i].head, config); 82 | if (match && cm.getLine(match.from.line).length <= maxHighlightLen) { 83 | var style = match.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket"; 84 | marks.push(cm.markText(match.from, Pos(match.from.line, match.from.ch + 1), {className: style})); 85 | if (match.to && cm.getLine(match.to.line).length <= maxHighlightLen) 86 | marks.push(cm.markText(match.to, Pos(match.to.line, match.to.ch + 1), {className: style})); 87 | } 88 | } 89 | 90 | if (marks.length) { 91 | // Kludge to work around the IE bug from issue #1193, where text 92 | // input stops going to the textare whever this fires. 93 | if (ie_lt8 && cm.state.focused) cm.focus(); 94 | 95 | var clear = function() { 96 | cm.operation(function() { 97 | for (var i = 0; i < marks.length; i++) marks[i].clear(); 98 | }); 99 | }; 100 | if (autoclear) setTimeout(clear, 800); 101 | else return clear; 102 | } 103 | } 104 | 105 | function doMatchBrackets(cm) { 106 | cm.operation(function() { 107 | if (cm.state.matchBrackets.currentlyHighlighted) { 108 | cm.state.matchBrackets.currentlyHighlighted(); 109 | cm.state.matchBrackets.currentlyHighlighted = null; 110 | } 111 | cm.state.matchBrackets.currentlyHighlighted = matchBrackets(cm, false, cm.state.matchBrackets); 112 | }); 113 | } 114 | 115 | CodeMirror.defineOption("matchBrackets", false, function(cm, val, old) { 116 | if (old && old != CodeMirror.Init) { 117 | cm.off("cursorActivity", doMatchBrackets); 118 | if (cm.state.matchBrackets && cm.state.matchBrackets.currentlyHighlighted) { 119 | cm.state.matchBrackets.currentlyHighlighted(); 120 | cm.state.matchBrackets.currentlyHighlighted = null; 121 | } 122 | } 123 | if (val) { 124 | cm.state.matchBrackets = typeof val == "object" ? val : {}; 125 | cm.on("cursorActivity", doMatchBrackets); 126 | } 127 | }); 128 | 129 | CodeMirror.defineExtension("matchBrackets", function() {matchBrackets(this, true);}); 130 | CodeMirror.defineExtension("findMatchingBracket", function(pos, config, oldConfig){ 131 | // Backwards-compatibility kludge 132 | if (oldConfig || typeof config == "boolean") { 133 | if (!oldConfig) { 134 | config = config ? {strict: true} : null 135 | } else { 136 | oldConfig.strict = config 137 | config = oldConfig 138 | } 139 | } 140 | return findMatchingBracket(this, pos, config) 141 | }); 142 | CodeMirror.defineExtension("scanForBracket", function(pos, dir, style, config){ 143 | return scanForBracket(this, pos, dir, style, config); 144 | }); 145 | }); 146 | -------------------------------------------------------------------------------- /app/static/cm/show-hint.css: -------------------------------------------------------------------------------- 1 | .CodeMirror-hints { 2 | position: absolute; 3 | z-index: 10; 4 | overflow: hidden; 5 | list-style: none; 6 | 7 | margin: 0; 8 | padding: 2px; 9 | 10 | -webkit-box-shadow: 2px 3px 5px rgba(0,0,0,.2); 11 | -moz-box-shadow: 2px 3px 5px rgba(0,0,0,.2); 12 | box-shadow: 2px 3px 5px rgba(0,0,0,.2); 13 | border-radius: 3px; 14 | border: 1px solid silver; 15 | 16 | background: white; 17 | font-size: 90%; 18 | font-family: monospace; 19 | 20 | max-height: 20em; 21 | overflow-y: auto; 22 | } 23 | 24 | .CodeMirror-hint { 25 | margin: 0; 26 | padding: 0 4px; 27 | border-radius: 2px; 28 | white-space: pre; 29 | color: black; 30 | cursor: pointer; 31 | } 32 | 33 | li.CodeMirror-hint-active { 34 | background: #08f; 35 | color: white; 36 | } 37 | -------------------------------------------------------------------------------- /app/static/cm/show-hint.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: http://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | 14 | var HINT_ELEMENT_CLASS = "CodeMirror-hint"; 15 | var ACTIVE_HINT_ELEMENT_CLASS = "CodeMirror-hint-active"; 16 | 17 | // This is the old interface, kept around for now to stay 18 | // backwards-compatible. 19 | CodeMirror.showHint = function(cm, getHints, options) { 20 | if (!getHints) return cm.showHint(options); 21 | if (options && options.async) getHints.async = true; 22 | var newOpts = {hint: getHints}; 23 | if (options) for (var prop in options) newOpts[prop] = options[prop]; 24 | return cm.showHint(newOpts); 25 | }; 26 | 27 | CodeMirror.defineExtension("showHint", function(options) { 28 | options = parseOptions(this, this.getCursor("start"), options); 29 | var selections = this.listSelections() 30 | if (selections.length > 1) return; 31 | // By default, don't allow completion when something is selected. 32 | // A hint function can have a `supportsSelection` property to 33 | // indicate that it can handle selections. 34 | if (this.somethingSelected()) { 35 | if (!options.hint.supportsSelection) return; 36 | // Don't try with cross-line selections 37 | for (var i = 0; i < selections.length; i++) 38 | if (selections[i].head.line != selections[i].anchor.line) return; 39 | } 40 | 41 | if (this.state.completionActive) this.state.completionActive.close(); 42 | var completion = this.state.completionActive = new Completion(this, options); 43 | if (!completion.options.hint) return; 44 | 45 | CodeMirror.signal(this, "startCompletion", this); 46 | completion.update(true); 47 | }); 48 | 49 | function Completion(cm, options) { 50 | this.cm = cm; 51 | this.options = options; 52 | this.widget = null; 53 | this.debounce = 0; 54 | this.tick = 0; 55 | this.startPos = this.cm.getCursor("start"); 56 | this.startLen = this.cm.getLine(this.startPos.line).length - this.cm.getSelection().length; 57 | 58 | var self = this; 59 | cm.on("cursorActivity", this.activityFunc = function() { self.cursorActivity(); }); 60 | } 61 | 62 | var requestAnimationFrame = window.requestAnimationFrame || function(fn) { 63 | return setTimeout(fn, 1000/60); 64 | }; 65 | var cancelAnimationFrame = window.cancelAnimationFrame || clearTimeout; 66 | 67 | Completion.prototype = { 68 | close: function() { 69 | if (!this.active()) return; 70 | this.cm.state.completionActive = null; 71 | this.tick = null; 72 | this.cm.off("cursorActivity", this.activityFunc); 73 | 74 | if (this.widget && this.data) CodeMirror.signal(this.data, "close"); 75 | if (this.widget) this.widget.close(); 76 | CodeMirror.signal(this.cm, "endCompletion", this.cm); 77 | }, 78 | 79 | active: function() { 80 | return this.cm.state.completionActive == this; 81 | }, 82 | 83 | pick: function(data, i) { 84 | var completion = data.list[i]; 85 | if (completion.hint) completion.hint(this.cm, data, completion); 86 | else this.cm.replaceRange(getText(completion), completion.from || data.from, 87 | completion.to || data.to, "complete"); 88 | CodeMirror.signal(data, "pick", completion); 89 | this.close(); 90 | }, 91 | 92 | cursorActivity: function() { 93 | if (this.debounce) { 94 | cancelAnimationFrame(this.debounce); 95 | this.debounce = 0; 96 | } 97 | 98 | var pos = this.cm.getCursor(), line = this.cm.getLine(pos.line); 99 | if (pos.line != this.startPos.line || line.length - pos.ch != this.startLen - this.startPos.ch || 100 | pos.ch < this.startPos.ch || this.cm.somethingSelected() || 101 | (pos.ch && this.options.closeCharacters.test(line.charAt(pos.ch - 1)))) { 102 | this.close(); 103 | } else { 104 | var self = this; 105 | this.debounce = requestAnimationFrame(function() {self.update();}); 106 | if (this.widget) this.widget.disable(); 107 | } 108 | }, 109 | 110 | update: function(first) { 111 | if (this.tick == null) return 112 | var self = this, myTick = ++this.tick 113 | fetchHints(this.options.hint, this.cm, this.options, function(data) { 114 | if (self.tick == myTick) self.finishUpdate(data, first) 115 | }) 116 | }, 117 | 118 | finishUpdate: function(data, first) { 119 | if (this.data) CodeMirror.signal(this.data, "update"); 120 | 121 | var picked = (this.widget && this.widget.picked) || (first && this.options.completeSingle); 122 | if (this.widget) this.widget.close(); 123 | 124 | this.data = data; 125 | 126 | if (data && data.list.length) { 127 | if (picked && data.list.length == 1) { 128 | this.pick(data, 0); 129 | } else { 130 | this.widget = new Widget(this, data); 131 | CodeMirror.signal(data, "shown"); 132 | } 133 | } 134 | } 135 | }; 136 | 137 | function parseOptions(cm, pos, options) { 138 | var editor = cm.options.hintOptions; 139 | var out = {}; 140 | for (var prop in defaultOptions) out[prop] = defaultOptions[prop]; 141 | if (editor) for (var prop in editor) 142 | if (editor[prop] !== undefined) out[prop] = editor[prop]; 143 | if (options) for (var prop in options) 144 | if (options[prop] !== undefined) out[prop] = options[prop]; 145 | if (out.hint.resolve) out.hint = out.hint.resolve(cm, pos) 146 | return out; 147 | } 148 | 149 | function getText(completion) { 150 | if (typeof completion == "string") return completion; 151 | else return completion.text; 152 | } 153 | 154 | function buildKeyMap(completion, handle) { 155 | var baseMap = { 156 | Up: function() {handle.moveFocus(-1);}, 157 | Down: function() {handle.moveFocus(1);}, 158 | PageUp: function() {handle.moveFocus(-handle.menuSize() + 1, true);}, 159 | PageDown: function() {handle.moveFocus(handle.menuSize() - 1, true);}, 160 | Home: function() {handle.setFocus(0);}, 161 | End: function() {handle.setFocus(handle.length - 1);}, 162 | Enter: handle.pick, 163 | Tab: handle.pick, 164 | Esc: handle.close 165 | }; 166 | var custom = completion.options.customKeys; 167 | var ourMap = custom ? {} : baseMap; 168 | function addBinding(key, val) { 169 | var bound; 170 | if (typeof val != "string") 171 | bound = function(cm) { return val(cm, handle); }; 172 | // This mechanism is deprecated 173 | else if (baseMap.hasOwnProperty(val)) 174 | bound = baseMap[val]; 175 | else 176 | bound = val; 177 | ourMap[key] = bound; 178 | } 179 | if (custom) 180 | for (var key in custom) if (custom.hasOwnProperty(key)) 181 | addBinding(key, custom[key]); 182 | var extra = completion.options.extraKeys; 183 | if (extra) 184 | for (var key in extra) if (extra.hasOwnProperty(key)) 185 | addBinding(key, extra[key]); 186 | return ourMap; 187 | } 188 | 189 | function getHintElement(hintsElement, el) { 190 | while (el && el != hintsElement) { 191 | if (el.nodeName.toUpperCase() === "LI" && el.parentNode == hintsElement) return el; 192 | el = el.parentNode; 193 | } 194 | } 195 | 196 | function Widget(completion, data) { 197 | this.completion = completion; 198 | this.data = data; 199 | this.picked = false; 200 | var widget = this, cm = completion.cm; 201 | 202 | var hints = this.hints = document.createElement("ul"); 203 | hints.className = "CodeMirror-hints"; 204 | this.selectedHint = data.selectedHint || 0; 205 | 206 | var completions = data.list; 207 | for (var i = 0; i < completions.length; ++i) { 208 | var elt = hints.appendChild(document.createElement("li")), cur = completions[i]; 209 | var className = HINT_ELEMENT_CLASS + (i != this.selectedHint ? "" : " " + ACTIVE_HINT_ELEMENT_CLASS); 210 | if (cur.className != null) className = cur.className + " " + className; 211 | elt.className = className; 212 | if (cur.render) cur.render(elt, data, cur); 213 | else elt.appendChild(document.createTextNode(cur.displayText || getText(cur))); 214 | elt.hintId = i; 215 | } 216 | 217 | var pos = cm.cursorCoords(completion.options.alignWithWord ? data.from : null); 218 | var left = pos.left, top = pos.bottom, below = true; 219 | hints.style.left = left + "px"; 220 | hints.style.top = top + "px"; 221 | // If we're at the edge of the screen, then we want the menu to appear on the left of the cursor. 222 | var winW = window.innerWidth || Math.max(document.body.offsetWidth, document.documentElement.offsetWidth); 223 | var winH = window.innerHeight || Math.max(document.body.offsetHeight, document.documentElement.offsetHeight); 224 | (completion.options.container || document.body).appendChild(hints); 225 | var box = hints.getBoundingClientRect(), overlapY = box.bottom - winH; 226 | var scrolls = hints.scrollHeight > hints.clientHeight + 1 227 | var startScroll = cm.getScrollInfo(); 228 | 229 | if (overlapY > 0) { 230 | var height = box.bottom - box.top, curTop = pos.top - (pos.bottom - box.top); 231 | if (curTop - height > 0) { // Fits above cursor 232 | hints.style.top = (top = pos.top - height) + "px"; 233 | below = false; 234 | } else if (height > winH) { 235 | hints.style.height = (winH - 5) + "px"; 236 | hints.style.top = (top = pos.bottom - box.top) + "px"; 237 | var cursor = cm.getCursor(); 238 | if (data.from.ch != cursor.ch) { 239 | pos = cm.cursorCoords(cursor); 240 | hints.style.left = (left = pos.left) + "px"; 241 | box = hints.getBoundingClientRect(); 242 | } 243 | } 244 | } 245 | var overlapX = box.right - winW; 246 | if (overlapX > 0) { 247 | if (box.right - box.left > winW) { 248 | hints.style.width = (winW - 5) + "px"; 249 | overlapX -= (box.right - box.left) - winW; 250 | } 251 | hints.style.left = (left = pos.left - overlapX) + "px"; 252 | } 253 | if (scrolls) for (var node = hints.firstChild; node; node = node.nextSibling) 254 | node.style.paddingRight = cm.display.nativeBarWidth + "px" 255 | 256 | cm.addKeyMap(this.keyMap = buildKeyMap(completion, { 257 | moveFocus: function(n, avoidWrap) { widget.changeActive(widget.selectedHint + n, avoidWrap); }, 258 | setFocus: function(n) { widget.changeActive(n); }, 259 | menuSize: function() { return widget.screenAmount(); }, 260 | length: completions.length, 261 | close: function() { completion.close(); }, 262 | pick: function() { widget.pick(); }, 263 | data: data 264 | })); 265 | 266 | if (completion.options.closeOnUnfocus) { 267 | var closingOnBlur; 268 | cm.on("blur", this.onBlur = function() { closingOnBlur = setTimeout(function() { completion.close(); }, 100); }); 269 | cm.on("focus", this.onFocus = function() { clearTimeout(closingOnBlur); }); 270 | } 271 | 272 | cm.on("scroll", this.onScroll = function() { 273 | var curScroll = cm.getScrollInfo(), editor = cm.getWrapperElement().getBoundingClientRect(); 274 | var newTop = top + startScroll.top - curScroll.top; 275 | var point = newTop - (window.pageYOffset || (document.documentElement || document.body).scrollTop); 276 | if (!below) point += hints.offsetHeight; 277 | if (point <= editor.top || point >= editor.bottom) return completion.close(); 278 | hints.style.top = newTop + "px"; 279 | hints.style.left = (left + startScroll.left - curScroll.left) + "px"; 280 | }); 281 | 282 | CodeMirror.on(hints, "dblclick", function(e) { 283 | var t = getHintElement(hints, e.target || e.srcElement); 284 | if (t && t.hintId != null) {widget.changeActive(t.hintId); widget.pick();} 285 | }); 286 | 287 | CodeMirror.on(hints, "click", function(e) { 288 | var t = getHintElement(hints, e.target || e.srcElement); 289 | if (t && t.hintId != null) { 290 | widget.changeActive(t.hintId); 291 | if (completion.options.completeOnSingleClick) widget.pick(); 292 | } 293 | }); 294 | 295 | CodeMirror.on(hints, "mousedown", function() { 296 | setTimeout(function(){cm.focus();}, 20); 297 | }); 298 | 299 | CodeMirror.signal(data, "select", completions[this.selectedHint], hints.childNodes[this.selectedHint]); 300 | return true; 301 | } 302 | 303 | Widget.prototype = { 304 | close: function() { 305 | if (this.completion.widget != this) return; 306 | this.completion.widget = null; 307 | this.hints.parentNode.removeChild(this.hints); 308 | this.completion.cm.removeKeyMap(this.keyMap); 309 | 310 | var cm = this.completion.cm; 311 | if (this.completion.options.closeOnUnfocus) { 312 | cm.off("blur", this.onBlur); 313 | cm.off("focus", this.onFocus); 314 | } 315 | cm.off("scroll", this.onScroll); 316 | }, 317 | 318 | disable: function() { 319 | this.completion.cm.removeKeyMap(this.keyMap); 320 | var widget = this; 321 | this.keyMap = {Enter: function() { widget.picked = true; }}; 322 | this.completion.cm.addKeyMap(this.keyMap); 323 | }, 324 | 325 | pick: function() { 326 | this.completion.pick(this.data, this.selectedHint); 327 | }, 328 | 329 | changeActive: function(i, avoidWrap) { 330 | if (i >= this.data.list.length) 331 | i = avoidWrap ? this.data.list.length - 1 : 0; 332 | else if (i < 0) 333 | i = avoidWrap ? 0 : this.data.list.length - 1; 334 | if (this.selectedHint == i) return; 335 | var node = this.hints.childNodes[this.selectedHint]; 336 | node.className = node.className.replace(" " + ACTIVE_HINT_ELEMENT_CLASS, ""); 337 | node = this.hints.childNodes[this.selectedHint = i]; 338 | node.className += " " + ACTIVE_HINT_ELEMENT_CLASS; 339 | if (node.offsetTop < this.hints.scrollTop) 340 | this.hints.scrollTop = node.offsetTop - 3; 341 | else if (node.offsetTop + node.offsetHeight > this.hints.scrollTop + this.hints.clientHeight) 342 | this.hints.scrollTop = node.offsetTop + node.offsetHeight - this.hints.clientHeight + 3; 343 | CodeMirror.signal(this.data, "select", this.data.list[this.selectedHint], node); 344 | }, 345 | 346 | screenAmount: function() { 347 | return Math.floor(this.hints.clientHeight / this.hints.firstChild.offsetHeight) || 1; 348 | } 349 | }; 350 | 351 | function applicableHelpers(cm, helpers) { 352 | if (!cm.somethingSelected()) return helpers 353 | var result = [] 354 | for (var i = 0; i < helpers.length; i++) 355 | if (helpers[i].supportsSelection) result.push(helpers[i]) 356 | return result 357 | } 358 | 359 | function fetchHints(hint, cm, options, callback) { 360 | if (hint.async) { 361 | hint(cm, callback, options) 362 | } else { 363 | var result = hint(cm, options) 364 | if (result && result.then) result.then(callback) 365 | else callback(result) 366 | } 367 | } 368 | 369 | function resolveAutoHints(cm, pos) { 370 | var helpers = cm.getHelpers(pos, "hint"), words 371 | if (helpers.length) { 372 | var resolved = function(cm, callback, options) { 373 | var app = applicableHelpers(cm, helpers); 374 | function run(i) { 375 | if (i == app.length) return callback(null) 376 | fetchHints(app[i], cm, options, function(result) { 377 | if (result && result.list.length > 0) callback(result) 378 | else run(i + 1) 379 | }) 380 | } 381 | run(0) 382 | } 383 | resolved.async = true 384 | resolved.supportsSelection = true 385 | return resolved 386 | } else if (words = cm.getHelper(cm.getCursor(), "hintWords")) { 387 | return function(cm) { return CodeMirror.hint.fromList(cm, {words: words}) } 388 | } else if (CodeMirror.hint.anyword) { 389 | return function(cm, options) { return CodeMirror.hint.anyword(cm, options) } 390 | } else { 391 | return function() {} 392 | } 393 | } 394 | 395 | CodeMirror.registerHelper("hint", "auto", { 396 | resolve: resolveAutoHints 397 | }); 398 | 399 | CodeMirror.registerHelper("hint", "fromList", function(cm, options) { 400 | var cur = cm.getCursor(), token = cm.getTokenAt(cur); 401 | var to = CodeMirror.Pos(cur.line, token.end); 402 | if (token.string && /\w/.test(token.string[token.string.length - 1])) { 403 | var term = token.string, from = CodeMirror.Pos(cur.line, token.start); 404 | } else { 405 | var term = "", from = to; 406 | } 407 | var found = []; 408 | for (var i = 0; i < options.words.length; i++) { 409 | var word = options.words[i]; 410 | if (word.slice(0, term.length) == term) 411 | found.push(word); 412 | } 413 | 414 | if (found.length) return {list: found, from: from, to: to}; 415 | }); 416 | 417 | CodeMirror.commands.autocomplete = CodeMirror.showHint; 418 | 419 | var defaultOptions = { 420 | hint: CodeMirror.hint.auto, 421 | completeSingle: true, 422 | alignWithWord: true, 423 | closeCharacters: /[\s()\[\]{};:>,]/, 424 | closeOnUnfocus: true, 425 | completeOnSingleClick: true, 426 | container: null, 427 | customKeys: null, 428 | extraKeys: null 429 | }; 430 | 431 | CodeMirror.defineOption("hintOptions", null); 432 | }); 433 | -------------------------------------------------------------------------------- /app/static/cookie-policy.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | C++ Insights - Cookie Policy 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 52 | 53 | 54 |

C++ Insights Cookie Policy

55 | 56 |

Browsers support the storing and sending back of small text files called "Cookies". These cookie files can be 57 | used (amongst other things) to help to track the usage of a website. See the Mozilla description of cookies 59 | for more details.

60 | 61 |

C++ Insights uses a cookie to store current state, such as: 62 |

65 |

66 | 67 |

C++ Insights uses a Local Storage to store current state, such as: 68 |

72 |

73 | 74 |

This information is used to provide a better user experience, it is used at no point to track any user 75 | identifying information. For general privacy information see Privacy Policy

76 | 77 |

Note that the consent status cookie is set even if you decline cookies. Otherwise, the website would ask you 78 | all 79 | the time whether to accept cookies. In case, you do not click decline or accept no cookies at all are 80 | stored.

81 | 82 |
83 | 84 | 88 | 89 |
90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /app/static/css/s.css: -------------------------------------------------------------------------------- 1 | /* C++ Insights Web, copyright (c) by Andreas Fertig 2 | Distributed under an MIT license. See /LICENSE */ 3 | 4 | :root { 5 | --cl-main-bg: #fff; 6 | --cl-text: #000; 7 | --cl-text-gray: #333; 8 | --cl-text-light-gray: gray; 9 | --cl-black: #000; 10 | --cl-button: var(--cl-main-bg); 11 | --cl-button-hover: #e6ebf1; 12 | --cl-button-border: rgba(27, 31, 35, 0.2); 13 | --cl-button-border-hover: rgba(27, 31, 35, 0.35); 14 | --cl-button-text: #24292e; 15 | 16 | --cl-link: #0275d8; 17 | --cl-link-hover: #014c8c; 18 | 19 | --cl-button-header: #545353; 20 | 21 | --cl-in-readonly: var(--cl-text-gray); 22 | --cl-in-readonly-selected: var(--cl-in-readonly); 23 | --cl-in-readonly-bg: #f2f2f2; 24 | 25 | --cl-code-bg: var(--cl-main-bg); 26 | --cl-code-selected-line: #e6e6e6; 27 | --cl-code-selection: #a3d0ff; 28 | --cl-code-border: #c5c5c5; 29 | --cl-console-bg: #f4f4f4; 30 | --cl-cursor: var(--cl-text); 31 | 32 | --cl-cm-string: #032f62; 33 | --cl-cm-keyword: #d73a49; 34 | --cl-cm-definition: #6f42c1; 35 | --cl-cm-number: #005cc5; 36 | 37 | --filter-icon-invert: 0%; 38 | --filter-icon-grayscale: 0%; 39 | --filter-logo-brightness: 100%; 40 | } 41 | 42 | @media (prefers-color-scheme: dark) { 43 | /* CSS Code if Dark Mode is active. Keep in sync with "[data-theme="dark"]" */ 44 | 45 | :root { 46 | --cl-main-bg: #353839; 47 | --cl-black: #cdcdce; 48 | --cl-text: var(--cl-black); 49 | --cl-text-gray: var(--cl-black); 50 | --cl-text-light-gray: var(--cl-button-text); 51 | 52 | --cl-button: #515354; 53 | --cl-button-hover: var(--cl-main-bg); 54 | --cl-button-border: var(--cl-button); 55 | --cl-button-border-hover: var(--cl-button); 56 | --cl-button-text: #cdcdce; 57 | 58 | --cl-link: #64b2f5; 59 | --cl-link-hover: #87b1d5; 60 | 61 | --cl-button-header: inherit; 62 | 63 | --cl-in-readonly: #5a5c62; 64 | --cl-in-readonly-selected: white; 65 | --cl-in-readonly-bg: #313131; 66 | 67 | --cl-code-bg: #1f1f23; 68 | --cl-code-selected-line: #24252c; 69 | --cl-code-selection: #515b70; 70 | --cl-code-border: #000; 71 | --cl-console-bg: var(--cl-code-bg); 72 | --cl-cursor: var(--cl-text); 73 | 74 | --cl-cm-string: #569cd6; 75 | --cl-cm-keyword: #fb60a2; 76 | --cl-cm-definition: #9874d9; 77 | --cl-cm-number: #b5cea8; 78 | 79 | --filter-icon-invert: 60%; 80 | --filter-icon-grayscale: 100%; 81 | --filter-logo-brightness: 130%; 82 | } 83 | } 84 | 85 | [data-theme="dark"] { 86 | /* copy & paste from above (@media (prefers-color-scheme: dark)) */ 87 | --cl-main-bg: #353839; 88 | --cl-black: #cdcdce; 89 | --cl-text: var(--cl-black); 90 | --cl-text-gray: var(--cl-black); 91 | --cl-text-light-gray: var(--cl-button-text); 92 | 93 | --cl-button: #515354; 94 | --cl-button-hover: var(--cl-main-bg); 95 | --cl-button-border: var(--cl-button); 96 | --cl-button-border-hover: var(--cl-button); 97 | --cl-button-text: #cdcdce; 98 | 99 | --cl-link: #64b2f5; 100 | --cl-link-hover: #87b1d5; 101 | 102 | --cl-button-header: inherit; 103 | 104 | --cl-in-readonly: #5a5c62; 105 | --cl-in-readonly-selected: white; 106 | --cl-in-readonly-bg: #313131; 107 | 108 | --cl-code-bg: #1f1f23; 109 | --cl-code-selected-line: #24252c; 110 | --cl-code-selection: #515b70; 111 | --cl-code-border: #000; 112 | --cl-console-bg: var(--cl-code-bg); 113 | --cl-cursor: var(--cl-text); 114 | 115 | --cl-cm-string: #569cd6; 116 | --cl-cm-keyword: #fb60a2; 117 | --cl-cm-definition: #9874d9; 118 | --cl-cm-number: #b5cea8; 119 | 120 | --filter-icon-invert: 60%; 121 | --filter-icon-grayscale: 100%; 122 | --filter-logo-brightness: 130%; 123 | 124 | } 125 | 126 | html { 127 | height: 100%; 128 | } 129 | 130 | body, 131 | html { 132 | margin: 0; 133 | padding: 0; 134 | color: var(--cl-text-gray); 135 | background: var(--cl-main-bg); 136 | font-family: Verdana, Helvetica, Arial, sans-serif; 137 | } 138 | 139 | body { 140 | min-height: 100%; 141 | font-size: initial; 142 | font-weight: 400; 143 | line-height: 1.5; 144 | } 145 | 146 | h1 { 147 | font-size: larger; 148 | } 149 | 150 | input {} 151 | 152 | label { 153 | margin: 0; 154 | } 155 | 156 | button:focus { 157 | outline: none; 158 | } 159 | 160 | main { 161 | display: grid; 162 | margin-left: 10px; 163 | margin-right: 10px; 164 | 165 | grid-template: 166 | "nav nav nav" 41px "srcin vdragbar insights" minmax(60vh, auto) "hdragbar hdragbar hdragbar" 10px "console console console" minmax(0, 150px) / calc(50% - 5px) 10px calc(50% - 5px); 167 | height: 100vh; 168 | } 169 | 170 | #single { 171 | grid-template: 172 | "nav nav" 41px "content content " auto / 100%; 173 | } 174 | 175 | nav { 176 | grid-area: nav; 177 | display: grid; 178 | grid-template: 179 | "controls news about" 41px / max-content minmax(100px, auto) max-content; 180 | align-items: center; 181 | } 182 | 183 | srcin { 184 | grid-area: srcin; 185 | } 186 | 187 | vdragbar { 188 | grid-area: vdragbar; 189 | cursor: col-resize; 190 | } 191 | 192 | hdragbar { 193 | grid-area: hdragbar; 194 | cursor: row-resize; 195 | } 196 | 197 | vdragbar:hover, 198 | hdragbar:hover { 199 | background: var(--cl-console-bg); 200 | } 201 | 202 | insights { 203 | grid-area: insights; 204 | } 205 | 206 | console { 207 | grid-area: console; 208 | } 209 | 210 | info { 211 | grid-area: info; 212 | } 213 | 214 | controls { 215 | grid-area: controls; 216 | } 217 | 218 | news { 219 | grid-area: news; 220 | margin-left: auto; 221 | margin-right: auto; 222 | } 223 | 224 | about { 225 | grid-area: about; 226 | align-self: baseline; 227 | } 228 | 229 | .icon-run, 230 | .icon-save, 231 | .icon-load, 232 | .icon-link, 233 | .icon-twitter { 234 | speak: none; 235 | font-style: normal; 236 | font-weight: normal; 237 | font-variant: normal; 238 | text-transform: none; 239 | line-height: 1; 240 | -webkit-font-smoothing: antialiased; 241 | } 242 | 243 | .icon-run:before, 244 | .icon-run:hover:before, 245 | #active .icon-run:before, 246 | .icon-save:before, 247 | .icon-save:hover:before, 248 | #active .icon-save:before, 249 | .icon-load:before, 250 | .icon-load:hover:before, 251 | #active .icon-load:before, 252 | .icon-link:before, 253 | .icon-link:hover:before, 254 | #active .icon-link:before, 255 | .icon-twitter:before, 256 | .icon-twitter:hover:before, 257 | #active .icon-twitter:before { 258 | content: ""; 259 | background-image: url("../img/nav.svg"); 260 | width: 16px; 261 | height: 16px; 262 | display: block; 263 | filter: invert(var(--filter-icon-invert)); 264 | } 265 | 266 | .btn-group-lg>.btn, 267 | .btn-lg { 268 | font-size: .8em; 269 | } 270 | 271 | .btn-primary:hover, 272 | .ms-choice:hover { 273 | color: var(--cl-text); 274 | background-color: var(--cl-button-hover); 275 | border-color: var(--cl-button-border-hover); 276 | } 277 | 278 | .btn-primary { 279 | color: var(--cl-button-text); 280 | background-color: var(--cl-button); 281 | border: 1px solid var(--cl-button-border); 282 | } 283 | 284 | .btn-group-lg>.btn, 285 | .btn-lg { 286 | height: 32px; 287 | line-height: 32px; 288 | padding-left: 1rem; 289 | padding-right: 1rem; 290 | } 291 | 292 | #cppStd { 293 | padding-left: 0.5rem; 294 | } 295 | 296 | .btn { 297 | transition: initial; 298 | margin-top: 5px; 299 | cursor: pointer; 300 | } 301 | 302 | .btn:focus, 303 | .btn:hover { 304 | text-decoration: none; 305 | outline: none; 306 | } 307 | 308 | .btn-group-lg>.btn, 309 | .btn-lg { 310 | border-radius: .3rem; 311 | } 312 | 313 | .btn { 314 | display: inline-block; 315 | text-align: center; 316 | white-space: nowrap; 317 | vertical-align: middle; 318 | -webkit-user-select: none; 319 | -moz-user-select: none; 320 | -ms-user-select: none; 321 | user-select: none; 322 | -webkit-transition: all .2s ease-in-out; 323 | -o-transition: all .2s ease-in-out; 324 | transition: all .2s ease-in-out; 325 | } 326 | 327 | /* remove standard-styles */ 328 | select { 329 | -webkit-appearance: none; 330 | -moz-appearance: none; 331 | appearance: none; 332 | border: none; 333 | border-radius: 0; 334 | font-size: 1em; 335 | /*width: 100%*/ 336 | } 337 | 338 | /* 339 | @-moz-document url-prefix() { 340 | select, select:-moz-focusring, select::-moz-focus-inner { 341 | color: transparent !important; 342 | text-shadow: 0 0 0 #000 !important; 343 | background-image: none !important; 344 | border:0; 345 | } 346 | } 347 | */ 348 | 349 | /* remove standard-styles */ 350 | /* 351 | select { 352 | -webkit-appearance: none; 353 | -moz-appearance: none; 354 | appearance: none; 355 | border:none; 356 | border-radius: 0; 357 | font-size: 1em; 358 | width: 100%; 359 | display: inline-block; 360 | overflow: hidden; 361 | } 362 | */ 363 | 364 | [role="button"], 365 | a, 366 | area, 367 | button, 368 | input, 369 | label, 370 | select, 371 | summary, 372 | textarea { 373 | -ms-touch-action: manipulation; 374 | touch-action: manipulation; 375 | } 376 | 377 | *, 378 | ::after, 379 | ::before { 380 | -webkit-box-sizing: inherit; 381 | box-sizing: inherit; 382 | } 383 | 384 | img { 385 | vertical-align: middle; 386 | border-style: none; 387 | } 388 | 389 | #cookie-law { 390 | background-color: lightcyan; 391 | height: 60px; 392 | vertical-align: middle; 393 | margin: 0; 394 | font-size: 0.9em; 395 | line-height: 60px; 396 | padding-left: 10px; 397 | } 398 | 399 | .accept-decline { 400 | width: 20%; 401 | display: inline-block; 402 | float: right; 403 | } 404 | 405 | .btn-decline, 406 | .btn-accept { 407 | width: 50%; 408 | display: block; 409 | height: 60px; 410 | text-align: center; 411 | } 412 | 413 | .btn-accept { 414 | float: right; 415 | background-color: darkturquoise; 416 | } 417 | 418 | .btn-decline { 419 | float: left; 420 | } 421 | 422 | a:focus, 423 | a:hover { 424 | color: var(--cl-link-hover); 425 | text-decoration: underline; 426 | } 427 | 428 | a:active, 429 | a:hover { 430 | outline-width: 0; 431 | } 432 | 433 | a { 434 | background-color: transparent; 435 | color: var(--cl-link); 436 | text-decoration: none; 437 | } 438 | 439 | a.close-cookie-banner, 440 | a.decline-cookie-banner { 441 | display: block; 442 | background-color: turquoise; 443 | height: 60px; 444 | text-align: center; 445 | color: white; 446 | } 447 | 448 | a.decline-cookie-banner { 449 | background-color: lightcyan; 450 | color: black; 451 | } 452 | 453 | a.decline-cookie-banner:focus, 454 | a.decline-cookie-banner:hover, 455 | a.close-cookie-banner:focus, 456 | a.close-cookie-banner:hover { 457 | background-color: darkturquoise; 458 | color: white; 459 | text-decoration: none; 460 | } 461 | 462 | #container { 463 | width: 100%; 464 | margin-left: auto; 465 | margin-right: auto; 466 | } 467 | 468 | #container>div { 469 | margin: auto 1em; 470 | } 471 | 472 | #stdin, 473 | #stdout, 474 | #stderr { 475 | height: 100%; 476 | display: flex; 477 | flex-flow: column; 478 | } 479 | 480 | #stderr-div { 481 | display: flex; 482 | flex-flow: column; 483 | flex-grow: 1; 484 | } 485 | 486 | #stderr button, 487 | #banner button { 488 | border: none; 489 | background: inherit; 490 | margin: 0; 491 | padding: 0; 492 | font-size: small; 493 | color: inherit; 494 | cursor: pointer; 495 | vertical-align: super; 496 | } 497 | 498 | #stderr button { 499 | background-position: center center; 500 | background-repeat: no-repeat; 501 | background-size: 100%; 502 | width: 10px; 503 | height: 10px; 504 | opacity: .5; 505 | } 506 | 507 | #stderr button:hover { 508 | opacity: 1; 509 | } 510 | 511 | #stderr button { 512 | background-image: url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='UTF-8' standalone='no'%3F%3E%3Csvg xmlns='http://www.w3.org/2000/svg' version='1.1' width='10' height='10' viewBox='0 0 10 10'%3E%3Cg stroke='%23808080' fill='none'%3E%3Cpath d='m0,0 10,0 0,10 -10,0 0,-10'/%3E%3Cpath d='m0,1 10,0' stroke-width='2'/%3E%3C/g%3E%3C/svg%3E%0A"); 513 | } 514 | 515 | #stderr .minimized { 516 | background-image: url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='UTF-8' standalone='no'%3F%3E%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 10 10'%3E%3Cg stroke='%23808080' fill='none'%3E%3Cpath d='m0,10 10,0' stroke-width='3'/%3E%3C/g%3E%3C/svg%3E "); 517 | } 518 | 519 | #lnkdesc { 520 | width: 100%; 521 | height: 30px; 522 | background-color: var(--cl-main-bg); 523 | color: var(--cl-text); 524 | border: 1px solid #ccc; 525 | border-bottom: none; 526 | border-top: none; 527 | } 528 | 529 | #header { 530 | text-align: left; 531 | font-size: 70%; 532 | border-left: 1px dotted var(--cl-black); 533 | padding-left: 10px; 534 | color: var(--cl-text-light-gray); 535 | padding-top: 5px; 536 | } 537 | 538 | #footer { 539 | display: none; 540 | } 541 | 542 | .kt, 543 | .k { 544 | color: #226BAA; 545 | font-weight: bold; 546 | } 547 | 548 | .c1, 549 | .cm { 550 | color: #6666FF; 551 | } 552 | 553 | .s, 554 | .se { 555 | color: #B3793D; 556 | } 557 | 558 | .linenos { 559 | color: #7F7F7F; 560 | } 561 | 562 | .icon-run:before, 563 | .icon-run:hover:before { 564 | background-position: 0px 0; 565 | } 566 | 567 | .icon-save:before, 568 | .icon-save:hover:before { 569 | background-position: 64px 0; 570 | } 571 | 572 | .icon-load:before, 573 | .icon-load:hover:before { 574 | background-position: 48px 0; 575 | } 576 | 577 | .icon-link:before, 578 | .icon-link:hover:before { 579 | background-position: 32px 0; 580 | } 581 | 582 | .icon-twitter:before, 583 | .icon-twitter:hover:before { 584 | background-position: 16px 0; 585 | } 586 | 587 | .CodeMirror { 588 | overflow: hidden; 589 | width: 100%; 590 | border: solid var(--cl-code-border); 591 | border-width: 1px; 592 | flex-grow: 1; 593 | background: var(--cl-code-bg); 594 | } 595 | 596 | .CodeMirror-gutters { 597 | border-right: initial; 598 | background-color: initial; 599 | white-space: nowrap; 600 | } 601 | 602 | .CodeMirror-selected { 603 | background-color: var(--cl-code-selection) !important; 604 | } 605 | 606 | .CodeMirror-focused div.CodeMirror-selected { 607 | background: rgba(255, 255, 255, 0.10); 608 | } 609 | 610 | .CodeMirror-activeline-background { 611 | background: #e8e8e8; 612 | } 613 | 614 | .CodeMirror-line::selection, 615 | .CodeMirror-line>span::selection, 616 | .CodeMirror-line>span>span::selection, 617 | .CodeMirror-activeline-background { 618 | background: var(--cl-code-selected-line); 619 | } 620 | 621 | .CodeMirror-line::-moz-selection, 622 | .CodeMirror-line>span::-moz-selection, 623 | .CodeMirror-line>span>span::-moz-selection { 624 | background: var(--cl-code-selected-line); 625 | } 626 | 627 | .CodeMirror, 628 | .cm-s-default .cm-variable, 629 | .cm-s-default .cm-punctuation, 630 | .cm-s-default .cm-property, 631 | .cm-s-default .cm-operator { 632 | color: var(--cl-text); 633 | } 634 | 635 | .CodeMirror div.CodeMirror-cursor { 636 | border-left: 1px solid var(--cl-cursor); 637 | } 638 | 639 | /*.CodeMirror-selectedtext { 640 | color: white; 641 | } 642 | */ 643 | 644 | div.CodeMirror span.CodeMirror-matchingbracket { 645 | color: inherit; 646 | border: var(--cl-text-light-gray); 647 | border-width: thin; 648 | border-style: solid; 649 | } 650 | 651 | .CodeMirror-linenumber { 652 | background-color: var(--cl-code-bg); 653 | } 654 | 655 | .CodeMirror-activeline-gutter .CodeMirror-linenumber { 656 | background: var(--cl-code-selected-line); 657 | } 658 | 659 | /** 660 | * GitHub Gist Theme from https://github.com/LouisBarranqueiro 661 | */ 662 | 663 | .cm-s-default .cm-comment, 664 | .c1, 665 | .cm { 666 | color: #6a737d; 667 | } 668 | 669 | .cm-s-default .cm-keyword, 670 | .cm-s-default .cm-meta, 671 | .cm-s-default .cm-type, 672 | .k, 673 | .kt, 674 | .cp, 675 | .cpf { 676 | color: var(--cl-cm-keyword); 677 | } 678 | 679 | .cm-s-default .cm-def, 680 | .nf { 681 | color: var(--cl-cm-definition); 682 | } 683 | 684 | .cm-s-default .cm-number { 685 | color: var(--cl-cm-number); 686 | } 687 | 688 | .cm-s-default .cm-string, 689 | .cm-s-default .cm-atom, 690 | .s, 691 | .se { 692 | color: var(--cl-cm-string); 693 | } 694 | 695 | .k, 696 | .kt { 697 | font-weight: normal; 698 | } 699 | 700 | .shortlink-entry { 701 | border: 1px solid #ddd; 702 | border-radius: 3px; 703 | margin-bottom: 16px; 704 | margin-top: 16px; 705 | margin-bottom: 1.5em; 706 | } 707 | 708 | .shortlink-entry-header { 709 | font-size: 12px; 710 | line-height: 32px; 711 | background-color: #fafbfc; 712 | border-bottom: 1px solid #e1e4e8; 713 | border-top-left-radius: 3px; 714 | border-top-right-radius: 3px; 715 | padding: 5px 10px; 716 | } 717 | 718 | .content { 719 | width: 90%; 720 | margin: auto; 721 | margin-left: auto !important; 722 | margin-right: auto !important; 723 | } 724 | 725 | .stderr .CodeMirror { 726 | height: 100%; 727 | background: var(--cl-console-bg); 728 | color: var(--cl-text); 729 | } 730 | 731 | .logo { 732 | height: 32px; 733 | width: auto; 734 | margin-right: 2em; 735 | filter: brightness(var(--filter-logo-brightness)); 736 | } 737 | 738 | .h2, 739 | h2 { 740 | font-size: 0.8rem; 741 | padding: 0; 742 | margin: 0; 743 | font-weight: 600; 744 | color: var(--cl-text-light-gray); 745 | } 746 | 747 | #file-input { 748 | display: none; 749 | } 750 | 751 | .copy-group { 752 | position: relative; 753 | } 754 | 755 | .input-group { 756 | display: table; 757 | } 758 | 759 | .input-group input:first-child, 760 | .input-group-button:first-child .cpybtn { 761 | border-top-right-radius: 0; 762 | border-bottom-right-radius: 0; 763 | } 764 | 765 | .input-group input, 766 | .input-group-button { 767 | display: table-cell; 768 | } 769 | 770 | .input-group input[type="text"] { 771 | min-height: 30px; 772 | padding: 3px 8px; 773 | color: var(--cl-in-readonly); 774 | vertical-align: middle; 775 | background-color: inherit; 776 | border: 1px solid #ccc; 777 | border-right: none; 778 | border-radius: 3px 0 0 0; 779 | -webkit-border-top-right-radius: 0; 780 | -moz-border-top-right-radius: 0; 781 | -webkit-border-bottom-right-radius: 0; 782 | -moz-border-bottom-right-radius: 0; 783 | outline: 0; 784 | box-shadow: inset 0 1px 2px rgba(0, 0, 0, .075); 785 | width: 100%; 786 | } 787 | 788 | .input-group-button:last-child .cpybtn { 789 | margin-left: -1px; 790 | } 791 | 792 | .input-group input:last-child, 793 | .input-group-button:last-child .cpybtn {} 794 | 795 | .input-group-row { 796 | display: table-row; 797 | border-collapse: collapse; 798 | } 799 | 800 | .input-group-url-row { 801 | width: 100%; 802 | border-collapse: collapse; 803 | } 804 | 805 | .cell-left { 806 | float: left; 807 | width: 80%; 808 | } 809 | 810 | .cell-right { 811 | float: right; 812 | width: 20%; 813 | } 814 | 815 | .cpybtn, 816 | .reqslbtn { 817 | padding: 0 8px; 818 | min-height: 30px; 819 | font-weight: 700; 820 | line-height: 20px; 821 | color: #333; 822 | white-space: nowrap; 823 | vertical-align: middle; 824 | cursor: pointer; 825 | background-color: inherit; 826 | /*#eee;*/ 827 | /* background-color: var(--cl-button); 828 | */ 829 | border: 1px solid #d5d5d5; 830 | border-radius: 0; 831 | border-top-right-radius: 3px; 832 | -webkit-user-select: none; 833 | -moz-user-select: none; 834 | -ms-user-select: none; 835 | user-select: none; 836 | -webkit-appearance: none; 837 | width: 100%; 838 | outline: none; 839 | } 840 | 841 | .reqslbtn { 842 | background-color: #0366d6; 843 | color: white; 844 | width: 100%; 845 | min-height: 42px; 846 | border-radius: 0 0 3px 3px; 847 | } 848 | 849 | .note-text { 850 | font-size: 0.6em; 851 | padding: 10px; 852 | background-color: lavender; 853 | border-bottom-left-radius: 3px; 854 | border-bottom-right-radius: 3px; 855 | } 856 | 857 | .pagination { 858 | font-size: 0.8em; 859 | margin-bottom: 10px; 860 | } 861 | 862 | .dropdown-table { 863 | padding: 5px; 864 | } 865 | 866 | .morebtn { 867 | margin: 0; 868 | padding: 0; 869 | font-size: inherit; 870 | border: none; 871 | margin-top: .8em; 872 | } 873 | 874 | .clip { 875 | margin-top: -3px; 876 | position: relative; 877 | top: 3px; 878 | filter: invert(var(--filter-icon-invert)); 879 | } 880 | 881 | .cebutton, 882 | .qbbutton { 883 | position: relative; 884 | top: -2px; 885 | height: 20px; 886 | filter: grayscale(var(--filter-icon-grayscale)) invert(var(--filter-icon-invert)); 887 | } 888 | 889 | .cebutton { 890 | height: 23px; 891 | } 892 | 893 | .copyDownDownContent, 894 | .moreDownDownContent { 895 | display: none; 896 | position: absolute; 897 | left: 0px; 898 | background-color: var(--cl-main-bg); 899 | min-width: 230px; 900 | box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2); 901 | z-index: 10; 902 | box-sizing: border-box; 903 | border-radius: 3px; 904 | border: 1px solid #aaa; 905 | } 906 | 907 | .moreDownDownContent { 908 | min-width: 0; 909 | background-color: var(--cl-main-bg); 910 | -webkit-box-shadow: 0 4px 5px rgba(0, 0, 0, .15); 911 | -moz-box-shadow: 0 4px 5px rgba(0, 0, 0, .15); 912 | box-shadow: 0 4px 5px rgba(0, 0, 0, .15); 913 | padding: 10px; 914 | color: var(--cl-black); 915 | border: 1px solid #aaa; 916 | -webkit-border-radius: 4px; 917 | -moz-border-radius: 4px; 918 | border-radius: 4px; 919 | font-size: .8em; 920 | } 921 | 922 | .btn-parent { 923 | display: inline-block; 924 | } 925 | 926 | .morebtn a { 927 | color: var(--cl-text); 928 | background-color: var(--cl-main-bg); 929 | } 930 | 931 | .morebtn a:focus, 932 | .morebtn a:hover { 933 | text-decoration: none; 934 | } 935 | 936 | .reqslbtn:hover { 937 | background-color: #0059bd; 938 | } 939 | 940 | .cpybtn:hover { 941 | border-color: var(--cl-button-border-hover); 942 | } 943 | 944 | .news { 945 | position: relative; 946 | display: inline-block; 947 | vertical-align: middle; 948 | margin-top: 5px; 949 | } 950 | 951 | #banner { 952 | border: 1px dotted #0275d8; 953 | border-radius: 2px; 954 | padding: 5px 10px 5px 10px; 955 | font-size: small; 956 | } 957 | 958 | .nocommunityevent { 959 | display: none !important; 960 | } 961 | 962 | .button-more>div { 963 | position: absolute; 964 | top: 0; 965 | right: 0; 966 | width: 20px; 967 | 968 | } 969 | 970 | /* 971 | .button-more>div::after { 972 | content: '▼'; 973 | color: var(--cl-text-light-gray); 974 | } 975 | */ 976 | 977 | #shrtlnkurl { 978 | color: var(--cl-text-light-gray); 979 | } 980 | 981 | #lnkurl::selection { 982 | background: var(--cl-code-selection); 983 | color: var(--cl-in-readonly-selected); 984 | } 985 | 986 | .show { 987 | display: block; 988 | } 989 | 990 | .ms-parent { 991 | display: inline-block; 992 | position: relative; 993 | vertical-align: middle; 994 | margin-top: 5px; 995 | } 996 | 997 | .ms-choice { 998 | display: block; 999 | width: 100%; 1000 | height: 32px; 1001 | line-height: 32px; 1002 | padding: 0; 1003 | overflow: hidden; 1004 | cursor: pointer; 1005 | text-align: left; 1006 | white-space: nowrap; 1007 | text-decoration: none; 1008 | -webkit-border-radius: 4px; 1009 | -moz-border-radius: 4px; 1010 | border-radius: 4px; 1011 | font-size: 0.8em; 1012 | color: var(--cl-button-text); 1013 | background-color: var(--cl-button); 1014 | border: 1px solid var(--cl-button-border); 1015 | } 1016 | 1017 | .ms-choice>span { 1018 | position: absolute; 1019 | top: 0; 1020 | left: 0; 1021 | right: 20px; 1022 | white-space: nowrap; 1023 | overflow: hidden; 1024 | text-overflow: ellipsis; 1025 | display: block; 1026 | padding-left: 8px; 1027 | } 1028 | 1029 | .ms-choice>span.placeholder {} 1030 | 1031 | .ms-choice>div { 1032 | position: absolute; 1033 | top: 0; 1034 | right: 0; 1035 | width: 20px; 1036 | } 1037 | 1038 | .ms-choice>div:after { 1039 | content: '▼'; 1040 | color: var(--cl-text-light-gray); 1041 | } 1042 | 1043 | .ms-choice>div.open:after { 1044 | content: '▲'; 1045 | color: var(--cl-text-light-gray); 1046 | } 1047 | 1048 | .ms-choice>div.open {} 1049 | 1050 | .ms-drop { 1051 | width: 100%; 1052 | overflow: hidden; 1053 | display: none; 1054 | margin-top: -1px; 1055 | padding: 0; 1056 | position: absolute; 1057 | z-index: 1000; 1058 | background: var(--cl-main-bg); 1059 | color: var(--cl-text); 1060 | border: 1px solid #aaa; 1061 | -webkit-border-radius: 4px; 1062 | -moz-border-radius: 4px; 1063 | border-radius: 4px; 1064 | font-size: .8em; 1065 | } 1066 | 1067 | .ms-drop.bottom { 1068 | top: 100%; 1069 | -webkit-box-shadow: 0 4px 5px rgba(0, 0, 0, .15); 1070 | -moz-box-shadow: 0 4px 5px rgba(0, 0, 0, .15); 1071 | box-shadow: 0 4px 5px rgba(0, 0, 0, .15); 1072 | } 1073 | 1074 | .ms-drop.top { 1075 | bottom: 100%; 1076 | -webkit-box-shadow: 0 -4px 5px rgba(0, 0, 0, .15); 1077 | -moz-box-shadow: 0 -4px 5px rgba(0, 0, 0, .15); 1078 | box-shadow: 0 -4px 5px rgba(0, 0, 0, .15); 1079 | } 1080 | 1081 | .ms-drop ul { 1082 | overflow: auto; 1083 | margin: 0; 1084 | padding: 5px 8px; 1085 | } 1086 | 1087 | .ms-drop ul>li { 1088 | list-style: none; 1089 | display: list-item; 1090 | background-image: none; 1091 | position: static; 1092 | } 1093 | 1094 | .ms-drop ul>li.multiple { 1095 | display: block; 1096 | float: left; 1097 | } 1098 | 1099 | .ms-drop ul>li.group { 1100 | clear: both; 1101 | } 1102 | 1103 | .ms-drop ul>li.multiple label { 1104 | width: 100%; 1105 | display: block; 1106 | white-space: nowrap; 1107 | overflow: hidden; 1108 | text-overflow: ellipsis; 1109 | } 1110 | 1111 | .ms-drop ul>li label { 1112 | font-weight: normal; 1113 | display: block; 1114 | white-space: nowrap; 1115 | } 1116 | 1117 | .ms-drop ul>li label.optgroup { 1118 | font-weight: bold; 1119 | color: var(--cl-button-header); 1120 | } 1121 | 1122 | .ms-drop input[type="checkbox"] { 1123 | vertical-align: middle; 1124 | margin: 5px; 1125 | } 1126 | 1127 | .optgroup input[type="checkbox"] { 1128 | display: none; 1129 | } 1130 | 1131 | .single input[type="checkbox"] { 1132 | -webkit-appearance: radio; 1133 | /* Chrome, Safari, Opera */ 1134 | -moz-appearance: radio; 1135 | /* Firefox */ 1136 | -ms-appearance: radio; 1137 | /* not currently supported */ 1138 | } 1139 | 1140 | .fonts input[type="radio"] { 1141 | display: none; 1142 | } 1143 | 1144 | .fonts input[type="radio"]:checked+span { 1145 | font-weight: bold; 1146 | } 1147 | 1148 | /* settings */ 1149 | #settings { 1150 | list-style: none; 1151 | padding-left: 0; 1152 | 1153 | } 1154 | 1155 | #settings label { 1156 | margin-left: .8em; 1157 | } 1158 | 1159 | /* settings */ 1160 | 1161 | @media screen and (max-width: 750px) { 1162 | main { 1163 | grid-template: 1164 | "nav nav" max-content "srcin srcin" 100% "vdragbar vdragbar" 0 "insights insights" 100% "hdragbar hdragbar" 0 "console console" 150px / 100%; 1165 | 1166 | } 1167 | 1168 | nav { 1169 | grid-template: 1170 | "controls controls" 1171 | "news news" 1172 | "about about" 100% / 100%; 1173 | } 1174 | 1175 | .logo { 1176 | margin-right: 0; 1177 | } 1178 | 1179 | #header { 1180 | display: none; 1181 | } 1182 | 1183 | #footer { 1184 | clear: both; 1185 | padding-top: 10px; 1186 | text-align: center; 1187 | font-size: 70%; 1188 | display: initial; 1189 | width: 100%; 1190 | color: var(--cl-text-light-gray); 1191 | } 1192 | 1193 | .btn-group-lg>.btn, 1194 | .btn-lg { 1195 | height: 24px; 1196 | line-height: 24px; 1197 | padding-left: 0.5rem; 1198 | padding-right: 0.5rem; 1199 | } 1200 | 1201 | #cppStd { 1202 | padding-left: 0; 1203 | } 1204 | 1205 | .ms-choice { 1206 | height: 24px; 1207 | line-height: 24px; 1208 | padding-left: 0.5rem; 1209 | padding-right: 0.5rem; 1210 | } 1211 | 1212 | .ms-choice>div { 1213 | width: 10px; 1214 | } 1215 | 1216 | .content { 1217 | width: 100%; 1218 | } 1219 | } 1220 | -------------------------------------------------------------------------------- /app/static/examples.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | C++ Insights 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 52 | 53 | 54 |

C++ Insights Examples

55 | 56 |

Here are some examples to give you an impression what C++ Insights can show you:

57 | 58 |

C++11:

59 | 95 | 96 |

C++14:

97 | 108 | 109 |

C++17:

110 | 116 | 117 |

In case, you're missing an important example, feel free to open a pull 118 | request with a modified 119 | examples.html. 120 | 121 |

125 |
126 | 127 |
128 | 129 | 130 | 131 | 132 | -------------------------------------------------------------------------------- /app/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andreasfertig/cppinsights-web/13b1b4886e5037313c9a8957332aead088c27795/app/static/favicon.ico -------------------------------------------------------------------------------- /app/static/img/ce.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 9 | 10 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /app/static/img/clb.svg: -------------------------------------------------------------------------------- 1 | 2 | 17 | 19 | 20 | 22 | image/svg+xml 23 | 25 | 26 | 27 | 28 | 30 | 51 | 54 | 59 | 63 | 68 | 71 | 76 | 81 | 82 | -------------------------------------------------------------------------------- /app/static/img/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /app/static/img/nav-dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 44 | 46 | 47 | 49 | image/svg+xml 50 | 52 | 53 | 54 | 55 | 56 | 61 | 66 | 70 | 75 | 76 | 80 | 85 | 86 | 89 | 94 | 95 | 98 | 102 | 103 | 107 | 112 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /app/static/img/nav.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 44 | 46 | 47 | 49 | image/svg+xml 50 | 52 | 53 | 54 | 55 | 56 | 61 | 66 | 69 | 73 | 74 | 77 | 81 | 82 | 85 | 89 | 90 | 93 | 97 | 98 | 101 | 105 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /app/static/img/qb.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 9 | Fichier 1 10 | 11 | 12 | 23 | 24 | 38 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /app/static/img/twcard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andreasfertig/cppinsights-web/13b1b4886e5037313c9a8957332aead088c27795/app/static/img/twcard.png -------------------------------------------------------------------------------- /app/static/js/cookie.js: -------------------------------------------------------------------------------- 1 | /* C++ Insights Web, copyright (c) by Andreas Fertig 2 | Distributed under an MIT license. See /LICENSE */ 3 | 4 | var cookieName = 'complianceCookie'; 5 | 6 | function createBanner() { 7 | var bodytag = document.getElementsByTagName('body')[0]; 8 | var div = document.createElement('div'); 9 | div.setAttribute('id', 'cookie-law'); 10 | div.innerHTML = 11 | 'This website uses cookies. See cookie policy for more information.
Decline
Accept
'; 12 | 13 | bodytag.insertBefore(div, bodytag.firstChild); 14 | } 15 | 16 | function createCookie(name, value) { 17 | var days = 365; 18 | var date = new Date(); 19 | date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); 20 | var expires = '; expires=' + date.toGMTString(); 21 | 22 | document.cookie = name + '=' + value + expires + '; path=/'; 23 | } 24 | 25 | function checkCookie(name) { 26 | var nameEQ = name + '='; 27 | var ca = document.cookie.split(';'); 28 | 29 | for (var i = 0; i < ca.length; i++) { 30 | var c = ca[i]; 31 | while (c.charAt(0) == ' ') { 32 | c = c.substring(1, c.length); 33 | } 34 | 35 | if (c.indexOf(nameEQ) == 0) { 36 | return c.substring(nameEQ.length, c.length); 37 | } 38 | } 39 | 40 | return null; 41 | } 42 | 43 | function createComplianceCookie(b) { 44 | createCookie(cookieName, b); 45 | } 46 | 47 | function cookieAccept(b) { // eslint-disable-line no-unused-vars 48 | createComplianceCookie(b); 49 | 50 | var element = document.getElementById('cookie-law'); 51 | element.parentNode.removeChild(element); 52 | } 53 | 54 | function storageAllowed() { // eslint-disable-line no-unused-vars 55 | return ('true' == checkCookie(cookieName)); 56 | } 57 | 58 | function onLoad() { // eslint-disable-line no-unused-vars 59 | if (!checkCookie(cookieName)) { 60 | createBanner(); 61 | } 62 | } 63 | 64 | function canUseLocalStorage() { 65 | return window.localStorage && storageAllowed(); 66 | } 67 | 68 | function setLocalStorageItem(key, data) { // eslint-disable-line no-unused-vars 69 | if (canUseLocalStorage()) { 70 | window.localStorage.setItem(key, JSON.stringify(data)); 71 | } 72 | } 73 | 74 | function getLocalStorageItem(key, deflt) { // eslint-disable-line no-unused-vars 75 | if (canUseLocalStorage()) { 76 | var data = window.localStorage.getItem(key); 77 | if (data) { 78 | return JSON.parse(data); 79 | } 80 | } 81 | 82 | return deflt; 83 | } 84 | -------------------------------------------------------------------------------- /app/static/js/main.js: -------------------------------------------------------------------------------- 1 | /* C++ Insights Web, copyright (c) by Andreas Fertig 2 | Distributed under an MIT license. See /LICENSE */ 3 | 4 | /* global CodeMirror, onLoad, getLocalStorageItem, setLocalStorageItem, canUseLocalStorage */ 5 | 6 | var DEFAULT_CPP_STD = 'cpp17'; 7 | var DEFAULT_REV = '1.0'; 8 | 9 | // load cookies 10 | onLoad(); 11 | 12 | var cppEditor = CodeMirror.fromTextArea(document.getElementById('cpp-code'), { 13 | lineNumbers: true, 14 | matchBrackets: true, 15 | styleActiveLine: true, 16 | mode: 'text/x-c++src' 17 | }); 18 | 19 | cppEditor.focus(); 20 | var insightsOptions = [DEFAULT_CPP_STD]; 21 | var code = cppEditor.getValue(); 22 | 23 | function setStandard(std) { 24 | var selected = $('#insightsOptions').multipleSelect('getSelects', 'value'); 25 | 26 | var filtered = selected.filter(function(value, index, arr) { // eslint-disable-line no-unused-vars 27 | return !value.startsWith('cpp'); 28 | }); 29 | 30 | filtered.push(std); 31 | 32 | $('#insightsOptions').multipleSelect('setSelects', filtered); 33 | } 34 | 35 | $('#insightsOptions').multipleSelect({ 36 | placeholder: 'C++ Insights Options', 37 | selectAll: false, 38 | onClick: function(opt) { 39 | if (opt.value.startsWith('cpp')) { 40 | setStandard(opt.value); 41 | } 42 | }, 43 | onOptgroupClick: function(group) { 44 | if ('C++ Standard' == group.label) { 45 | setStandard(DEFAULT_CPP_STD); 46 | } 47 | }, 48 | }); 49 | 50 | function changeFontSize(value) { 51 | var elCode = document.getElementById('stdin'); 52 | elCode.style.fontSize = value; 53 | var elStdOut = document.getElementById('stdout'); 54 | elStdOut.style.fontSize = value; 55 | var elStdErr = document.getElementById('stderr'); 56 | elStdErr.style.fontSize = value; 57 | } 58 | 59 | $('#fontSizer').multipleSelect({ 60 | placeholder: 'A+', 61 | selectAll: false, 62 | single: true, 63 | onClick: function(opt) { 64 | 65 | changeFontSize(opt.value); 66 | 67 | $('#fontSizer').multipleSelect('setSelects', opt); 68 | 69 | setLocalStorageItem('fontSize', opt.value); 70 | }, 71 | }); 72 | // check if the current url contains '/lnk' which means that we opened a link. In that case do not load the values from 73 | // local storage. 74 | function isLink() { 75 | return (window.location.href.indexOf('/lnk') > -1) || (window.location.href.indexOf('/s') > -1); 76 | } 77 | 78 | // If this is a link add a keydown listener to the cppEditor and remove the link, if the code is changed. 79 | if (isLink()) { 80 | cppEditor.on('keydown', function(instance, event) { // eslint-disable-line no-unused-vars 81 | if (isLink()) { 82 | history.pushState(null, null, '/'); 83 | } 84 | 85 | }); 86 | } 87 | 88 | if (canUseLocalStorage() && !isLink()) { 89 | if (!cppEditor.getValue()) { 90 | insightsOptions = window.localStorage.getItem('insightsOptions'); 91 | 92 | if (insightsOptions) { 93 | insightsOptions = JSON.parse(insightsOptions); 94 | } 95 | 96 | code = window.localStorage.getItem('code'); 97 | if (code) { 98 | code = JSON.parse(code); 99 | } 100 | } 101 | } 102 | 103 | if (!code) { 104 | insightsOptions = [DEFAULT_CPP_STD]; 105 | code = 106 | '#include \n\nint main()\n{\n const char arr[10]{2,4,6,8};\n\n for(const char& c : arr)\n {\n printf("c=%c\\n", c);\n }\n}'; 107 | 108 | } 109 | 110 | //try { 111 | if (!isLink()) { 112 | $('#insightsOptions').multipleSelect('setSelects', insightsOptions); 113 | } 114 | 115 | var DEFAULT_FONT_SIZE = getLocalStorageItem('fontSize', 'initial'); 116 | 117 | $('#fontSizer').multipleSelect('setSelects', [DEFAULT_FONT_SIZE]); 118 | changeFontSize(DEFAULT_FONT_SIZE); 119 | 120 | displayContents(code); 121 | //} catch (e) { 122 | // hm 123 | //} 124 | 125 | var mac = CodeMirror.keyMap.default == CodeMirror.keyMap.macDefault; 126 | CodeMirror.keyMap.default[(mac ? 'Cmd' : 'Ctrl') + '-Space'] = 'autocomplete'; 127 | var cppOutEditor = CodeMirror.fromTextArea(document.getElementById( 128 | 'cpp-code-out'), { // eslint-disable-line no-unused-vars 129 | lineNumbers: true, 130 | matchBrackets: true, 131 | styleActiveLine: true, 132 | readOnly: true, 133 | mode: 'text/x-c++src' 134 | }); 135 | var stdErrEditor = CodeMirror.fromTextArea(document.getElementById( 136 | 'stderr-out'), { // eslint-disable-line no-unused-vars 137 | lineNumbers: false, 138 | readOnly: true, 139 | mode: 'shell' 140 | }); 141 | 142 | function readSingleFile(e) { 143 | var file = e.target.files[0]; 144 | if (!file) { 145 | return; 146 | } 147 | var reader = new FileReader(); 148 | reader.onload = function(e) { 149 | var contents = e.target.result; 150 | displayContents(contents); 151 | }; 152 | reader.readAsText(file); 153 | } 154 | 155 | function displayContents(contents) { 156 | cppEditor.setValue(contents); 157 | } 158 | 159 | document.querySelector('.button-upload') 160 | .addEventListener('click', function(event) { 161 | event.preventDefault(); 162 | document.getElementById('file-input').click(); 163 | }); 164 | 165 | document.getElementById('file-input') 166 | .addEventListener('change', readSingleFile); 167 | 168 | document.querySelector('.button-download').addEventListener('click', function(event) { 169 | event.preventDefault(); 170 | download('cppinsights.txt', cppEditor.getValue()); 171 | }); 172 | 173 | function download(filename, text) { 174 | var pom = document.createElement('a'); 175 | pom.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text)); 176 | pom.setAttribute('download', filename); 177 | if (document.createEvent) { 178 | var event = document.createEvent('MouseEvents'); 179 | event.initEvent('click', true, true); 180 | pom.dispatchEvent(event); 181 | } else { 182 | pom.click(); 183 | } 184 | } 185 | 186 | function toggleButton(divId) { // eslint-disable-line no-unused-vars 187 | var element = document.getElementById(divId).style; 188 | 189 | if ('none' == element.display) { 190 | element.display = 'initial'; 191 | 192 | } else { 193 | element.display = 'none'; 194 | 195 | } 196 | 197 | setLocalStorageItem(divId, element.display); 198 | } 199 | 200 | function setElementDisplay(divId, value) { 201 | var element = document.getElementById(divId).style; 202 | 203 | element.display = value; 204 | } 205 | 206 | setElementDisplay('banner', getLocalStorageItem('banner', 'initial')); 207 | showHideConsole(getLocalStorageItem('stderr-div', 'initial')); 208 | 209 | /* -- resizing columns and rows -- */ 210 | 211 | function changeGridConsoleRowHeight(newHeight) { 212 | var allm = document.getElementById('allmain'); 213 | var navHeight = document.getElementById('nav').clientHeight + 'px'; 214 | 215 | allm.style.gridTemplateRows = navHeight + ' ' + 'auto' + ' 10px ' + newHeight; 216 | } 217 | 218 | var dragging = false; 219 | var draggingMode = ''; 220 | var consoleClosed = false; 221 | 222 | function showHideConsole(show) { 223 | var elem = document.getElementById('stderr-div'); 224 | var err = document.getElementById('errstd'); 225 | var oldHeight = err.clientHeight; 226 | 227 | var allm = document.getElementById('allmain'); 228 | 229 | var element = elem.style; 230 | var minimized = document.getElementById('console-btn'); 231 | 232 | if (show) { 233 | element.display = 'initial'; 234 | 235 | allm.style.removeProperty('grid-template-rows'); 236 | 237 | minimized.classList.remove('minimized'); 238 | // refresh codemirror, otherwise the editor contains the old value 239 | stdErrEditor.refresh(); 240 | 241 | } else { 242 | var height = elem.clientHeight; 243 | 244 | element.display = 'none'; 245 | var errStyleHeight = 'calc(' + oldHeight + 'px - ' + height + 'px)'; 246 | changeGridConsoleRowHeight(errStyleHeight); 247 | 248 | minimized.classList.add('minimized'); 249 | } 250 | 251 | consoleClosed = (element.display == 'none'); 252 | } 253 | 254 | function toggleConsole() { // eslint-disable-line no-unused-vars 255 | var element = document.getElementById('stderr-div').style; 256 | 257 | showHideConsole('none' == element.display); 258 | 259 | setLocalStorageItem('stderr-div', !consoleClosed); 260 | } 261 | 262 | function dragstart(e, mode) { 263 | e.preventDefault(); 264 | dragging = true; 265 | draggingMode = mode; 266 | } 267 | 268 | function dragmove(e) { 269 | if (dragging) { 270 | var percentage = 0; 271 | var mainPercentage = 0; 272 | 273 | if ('v' == draggingMode) { 274 | percentage = (e.pageX / window.innerWidth) * 100; 275 | if (percentage > 5 && percentage < 98) { 276 | mainPercentage = 100 - percentage; 277 | var allm = document.getElementById('allmain'); 278 | 279 | allm.style.gridTemplateColumns = 'calc(' + percentage + '% - 5px) 10px calc(' + mainPercentage + '% - 5px)'; 280 | } 281 | } else if ('h' == draggingMode) { 282 | if (consoleClosed) { 283 | return; 284 | } 285 | 286 | percentage = (e.pageY / window.innerHeight) * 100; 287 | 288 | if (percentage > 5 && percentage < 94) { 289 | mainPercentage = 100 - percentage; 290 | changeGridConsoleRowHeight(mainPercentage + '%'); 291 | } 292 | } 293 | 294 | } 295 | } 296 | 297 | function dragend() { 298 | dragging = false; 299 | } 300 | 301 | if (window.addEventListener) { 302 | document.getElementById('vdragbar').addEventListener('mousedown', function(e) { 303 | dragstart(e, 'v'); 304 | }); 305 | document.getElementById('vdragbar').addEventListener('touchstart', function(e) { 306 | dragstart(e, 'v'); 307 | }); 308 | document.getElementById('hdragbar').addEventListener('mousedown', function(e) { 309 | dragstart(e, 'h'); 310 | }); 311 | document.getElementById('hdragbar').addEventListener('touchstart', function(e) { 312 | dragstart(e, 'h'); 313 | }); 314 | window.addEventListener('mousemove', function(e) { 315 | dragmove(e); 316 | }); 317 | window.addEventListener('touchmove', function(e) { 318 | dragmove(e); 319 | }); 320 | window.addEventListener('mouseup', dragend); 321 | window.addEventListener('touchend', dragend); 322 | } 323 | 324 | /* -- resizing columns and rows -- */ 325 | 326 | function getInsightsOptions() { 327 | return $('#insightsOptions').multipleSelect('getSelects', 'value'); 328 | } 329 | 330 | function getCppStd() { 331 | var filtered = getInsightsOptions().filter(function(value, index, arr) { // eslint-disable-line no-unused-vars 332 | return value.startsWith('cpp'); 333 | }); 334 | 335 | return filtered[0]; 336 | } 337 | 338 | function getUseLibStdCpp() { 339 | var filtered = getInsightsOptions().filter(function(value, index, arr) { // eslint-disable-line no-unused-vars 340 | return value.startsWith('use-libcpp'); 341 | }); 342 | 343 | if (0 == filtered.length) { 344 | return null; 345 | } 346 | 347 | return filtered[0]; 348 | } 349 | 350 | function OnRunKeyDown(e) { 351 | if (!((e.keyCode == 10 || e.keyCode == 13) && e.ctrlKey)) return; 352 | Transform(); 353 | } 354 | 355 | function OnRunClicked(e) { 356 | e.preventDefault(); 357 | Transform(); 358 | cppEditor.focus(); 359 | } 360 | 361 | function OnWaitForResultKeyDown(e) { 362 | if (!((e.keyCode == 10 || e.keyCode == 13) && e.ctrlKey)) return; 363 | stdErrEditor.setValue('A request is already in the air...'); 364 | } 365 | 366 | function OnWaitForResultRunClicked(e) { 367 | e.preventDefault(); 368 | stdErrEditor.setValue('A request is already in the air...'); 369 | } 370 | 371 | function RunListenersSetup(addKeyDown, removeRunBtn, addRunBtn) { 372 | window.onkeyup = addKeyDown; 373 | 374 | var runButton = document.querySelector('.button-run'); 375 | if (runButton) { 376 | runButton.title = 'Run C++ Insights (' + (mac ? 'Cmd-Return' : 'Ctrl-Enter') + ')'; 377 | runButton.removeEventListener('click', removeRunBtn); 378 | runButton.addEventListener('click', addRunBtn); 379 | } 380 | cppEditor.focus(); 381 | } 382 | 383 | function SetRunListeners() { 384 | RunListenersSetup(OnRunKeyDown, OnWaitForResultRunClicked, OnRunClicked); 385 | } 386 | 387 | // set them initially 388 | SetRunListeners(); 389 | 390 | function SetupRequestShortLinkListener() { 391 | var requestShortLinkButton = document.querySelector('#rqslbtn'); 392 | if (requestShortLinkButton) { 393 | requestShortLinkButton.title = 'Request short link'; 394 | requestShortLinkButton.addEventListener('click', RequestShortLink); 395 | } 396 | } 397 | 398 | SetupRequestShortLinkListener(); 399 | 400 | function RemoveRequestShortLinkListener() { 401 | var requestShortLinkButton = document.querySelector('#rqslbtn'); 402 | if (requestShortLinkButton) { 403 | requestShortLinkButton.removeEventListener('click', RequestShortLink); 404 | } 405 | } 406 | 407 | function SetWaitForResultListeners() { 408 | RunListenersSetup(OnWaitForResultKeyDown, OnRunClicked, OnWaitForResultRunClicked); 409 | } 410 | 411 | function CopyClick() { // eslint-disable-line no-unused-vars 412 | var textToCopy = document.getElementById('lnkurl'); 413 | 414 | textToCopy.select(); 415 | 416 | document.execCommand('copy'); 417 | } 418 | 419 | function RequestShortLinkClick() { // eslint-disable-line no-unused-vars 420 | RequestShortLink(); 421 | } 422 | 423 | // at least FireFox has a problem with just btoa with UTF-8 characters 424 | function b64UTFEncode(str) { 425 | return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function(match, v) { 426 | return String.fromCharCode(parseInt(v, 16)); 427 | })); 428 | } 429 | 430 | function updateLinkToCompilerExplorer() { 431 | var cppstdparam = '-std=' + getCppStd().replace('cpp', 'c++'); 432 | 433 | var libCpp = getUseLibStdCpp(); 434 | if (null != libCpp) { 435 | cppstdparam += ' -stdlib=libc++'; 436 | } 437 | 438 | var clientstate = { 439 | sessions: [{ 440 | id: 1, 441 | language: 'c++', 442 | source: cppEditor.getValue(), 443 | compilers: [{ 444 | id: 'clang_trunk', 445 | options: cppstdparam 446 | }] 447 | }] 448 | }; 449 | 450 | var link = location.protocol + '//compiler-explorer.com/clientstate/' + b64UTFEncode(JSON.stringify(clientstate)); 451 | var ceButton = document.getElementById('button-ce'); 452 | ceButton.href = link; 453 | } 454 | 455 | document.querySelector('#button-ce').addEventListener('mousedown', function() { 456 | updateLinkToCompilerExplorer(); 457 | }); 458 | 459 | // From: https://github.com/mattgodbolt/compiler-explorer/pull/1823/files 460 | function asciiEncodeJsonText(json) { 461 | return json.replace(/[\u007F-\uFFFF]/g, function(chr) { 462 | // json unicode escapes must always be 4 characters long, so pad with leading zeros 463 | return '\\u' + ('0000' + chr.charCodeAt(0).toString(16)).substr(-4); 464 | }); 465 | } 466 | 467 | function updateLinkToQuickBench() { 468 | var quickBenchState = { 469 | text: cppEditor.getValue() 470 | }; 471 | 472 | var link = 'http:' /*location.protocol*/ + '//quick-bench.com/#' + b64UTFEncode(asciiEncodeJsonText(JSON.stringify( 473 | quickBenchState))); 474 | var qbButton = document.getElementById('button-qb'); 475 | qbButton.href = link; 476 | } 477 | 478 | document.querySelector('#button-qb').addEventListener('mousedown', function() { 479 | updateLinkToQuickBench(); 480 | }); 481 | 482 | function getLongLinkBase() { 483 | var cppStd = getCppStd(); 484 | var insightsOptions = getInsightsOptions(); 485 | var text = 'lnk?code=' + b64UTFEncode(cppEditor.getValue()) + '&insightsOptions=' + 486 | insightsOptions + '&std=' + cppStd + '&rev=' + DEFAULT_REV; 487 | 488 | return text; 489 | } 490 | 491 | document.querySelector('.button-create-link').addEventListener('click', function(event) { 492 | event.preventDefault(); 493 | event.stopPropagation(); 494 | var text = buildURL('/' + getLongLinkBase()); 495 | 496 | var lnkElement = document.getElementById('lnkurl'); 497 | lnkElement.value = text; 498 | 499 | var lnkDescElement = document.getElementById('lnkdesc'); 500 | lnkDescElement.value = ''; 501 | 502 | var element = document.getElementById('copyDropdown'); 503 | element.classList.toggle('show'); 504 | }); 505 | 506 | document.querySelector('.button-more').addEventListener('click', function(event) { 507 | event.preventDefault(); 508 | event.stopPropagation(); 509 | 510 | var element = document.getElementById('moreDropdown'); 511 | element.classList.toggle('show'); 512 | }); 513 | 514 | window.onclick = function(event) { 515 | if (!event.target.matches('.dropbtn') && !event.target.matches('.cpybtn') && !event.target.matches('#lnkurl') && 516 | !event.target.matches('#lnkdesc') && !event.target.matches('#rqslbtn')) { 517 | 518 | var dropdowns = document.getElementsByClassName('copyDownDownContent'); 519 | var i; 520 | for (i = 0; i < dropdowns.length; i++) { 521 | var openDropdown = dropdowns[i]; 522 | if (openDropdown.classList.contains('show')) { 523 | openDropdown.classList.remove('show'); 524 | } 525 | } 526 | } 527 | }; 528 | 529 | // without trailing '/' 530 | function getURLBase() { 531 | return location.protocol + '//' + location.hostname + (location.port ? ':' + location.port : ''); 532 | } 533 | 534 | function buildURL(url) { 535 | return getURLBase() + url; 536 | } 537 | 538 | // Send a transformation request to the server 539 | function Transform() { // eslint-disable-line no-unused-vars 540 | 541 | var request = new XMLHttpRequest(); 542 | 543 | setLocalStorageItem('code', cppEditor.getValue()); 544 | setLocalStorageItem('insightsOptions', getInsightsOptions()); 545 | 546 | request.onreadystatechange = function() { 547 | if (this.readyState == 4 && this.status == 200) { 548 | var response = JSON.parse(this.responseText); 549 | cppOutEditor.setValue(response.stdout); 550 | stdErrEditor.setValue(response.stderr); 551 | SetRunListeners(); 552 | } else if (this.readyState == 4 && this.status != 200) { 553 | stdErrEditor.setValue('Sorry, your request failed'); 554 | SetRunListeners(); 555 | } 556 | }; 557 | 558 | stdErrEditor.setValue('Waiting for response...'); 559 | 560 | var url = buildURL('/api/v1/transform'); 561 | 562 | request.open('POST', url, true); 563 | request.setRequestHeader('Content-Type', 'application/json;charset=UTF-8'); 564 | 565 | var data = {}; 566 | 567 | data.insightsOptions = getInsightsOptions(); 568 | data.code = cppEditor.getValue(); 569 | 570 | SetWaitForResultListeners(); 571 | request.send(JSON.stringify(data)); 572 | } 573 | 574 | // Send a short link request to the server 575 | function RequestShortLink() { // eslint-disable-line no-unused-vars 576 | 577 | var request = new XMLHttpRequest(); 578 | 579 | request.onreadystatechange = function() { 580 | var linkField = document.getElementById('lnkurl'); 581 | 582 | if (this.readyState == 4 && this.status == 200) { 583 | var response = JSON.parse(this.responseText); 584 | 585 | linkField.value = buildURL(response.shortlink); 586 | } else if (this.readyState == 4 && this.status != 200) { 587 | linkField.value = 'Sorry, your request failed'; 588 | } 589 | 590 | SetupRequestShortLinkListener(); 591 | }; 592 | 593 | var linkField = document.getElementById('lnkurl'); 594 | var linkDescField = document.getElementById('lnkdesc'); 595 | var longLink = getLongLinkBase(); 596 | 597 | // trim spaces and empty newlines from begin and end? 598 | 599 | linkField.value = 'Please wait...'; 600 | 601 | var url = buildURL('/api/v1/getshortlink'); 602 | 603 | request.open('POST', url, true); 604 | request.setRequestHeader('Content-Type', 'application/json;charset=UTF-8'); 605 | 606 | var data = {}; 607 | 608 | data.longurl = longLink; 609 | data.code = b64UTFEncode(cppEditor.getValue()); 610 | data.desc = b64UTFEncode(linkDescField.value); 611 | data.rev = DEFAULT_REV; 612 | data.std = getCppStd(); 613 | data.options = getInsightsOptions(); 614 | 615 | RemoveRequestShortLinkListener(); 616 | 617 | request.send(JSON.stringify(data)); 618 | } 619 | -------------------------------------------------------------------------------- /app/static/js/settings.js: -------------------------------------------------------------------------------- 1 | /* C++ Insights Web, copyright (c) by Andreas Fertig 2 | Distributed under an MIT license. See /LICENSE */ 3 | 4 | /* global getLocalStorageItem, setLocalStorageItem, storageAllowed, createComplianceCookie */ 5 | 6 | function toggleButton(divId, show) { // eslint-disable-line no-unused-vars 7 | var value = 'initial'; 8 | 9 | if (!show) { 10 | value = 'none'; 11 | 12 | } 13 | 14 | setLocalStorageItem(divId, value); 15 | } 16 | 17 | function applyTheme(dark) { 18 | if (dark) { 19 | document.body.setAttribute('data-theme', 'dark'); 20 | } else { 21 | document.body.removeAttribute('data-theme'); 22 | } 23 | } 24 | 25 | function toggleTheme(dark) { 26 | applyTheme(dark); 27 | 28 | setLocalStorageItem('dark-theme', dark); 29 | } 30 | 31 | function adjustTheme() { 32 | applyTheme(getLocalStorageItem('dark-theme', false)); 33 | } 34 | 35 | var settingsConfiguration = [ 36 | // name, get-function, set-function, requires cookies 37 | ['Accept cookies', storageAllowed, createComplianceCookie, false], 38 | ['Show community events', function() { 39 | return 'initial' === getLocalStorageItem('banner', 'initial'); 40 | }, function(b) { 41 | toggleButton('banner', b); 42 | }, true], 43 | ['Minimize console', function() { 44 | return getLocalStorageItem('stderr-div', false); 45 | }, function(b) { 46 | setLocalStorageItem('stderr-div', !b); 47 | }, true], 48 | ['Use dark theme', function() { 49 | return getLocalStorageItem('dark-theme', false); 50 | }, toggleTheme, true], 51 | ]; 52 | 53 | // settings 54 | function SettingsHandler() { 55 | var settingsList = document.getElementById('settings'); 56 | if (null == settingsList) { 57 | // settings page not loaded 58 | return; 59 | } 60 | 61 | while (settingsList.firstChild) { 62 | settingsList.removeChild(settingsList.firstChild); 63 | } 64 | 65 | function createSetting(item, index) { 66 | var li = document.createElement('li'); 67 | 68 | var input = document.createElement('input'); 69 | input.setAttribute('type', 'checkbox'); 70 | var label = document.createElement('label'); 71 | var text = document.createTextNode(item[0]); 72 | label.appendChild(text); 73 | 74 | input.checked = item[1](); 75 | 76 | // disable cookie related items, if cookies are not allowed 77 | if ((item[3] == true) && !storageAllowed()) { 78 | input.disabled = true; 79 | } 80 | 81 | li.appendChild(input); 82 | li.appendChild(label); 83 | 84 | input.addEventListener('change', (event) => { 85 | item[2](event.target.checked); 86 | 87 | if (0 == index) { // assuming that cookies are at pos 0 88 | // reload the entire list to prevent changes to cookie related items 89 | SettingsHandler(); 90 | } 91 | }); 92 | 93 | settingsList.appendChild(li); 94 | } 95 | 96 | settingsConfiguration.forEach(createSetting); 97 | 98 | } 99 | 100 | SettingsHandler(); 101 | 102 | // set to dark if enabled 103 | adjustTheme(); 104 | -------------------------------------------------------------------------------- /app/static/ms/Readme.md: -------------------------------------------------------------------------------- 1 | This is a third-party component and not covered by the license of the C++ Insights project. 2 | -------------------------------------------------------------------------------- /app/static/privacy-policy.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | C++ Insights - Privacy Policy 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 | 53 | 54 | 55 |

C++ Insights Privacy Policy

56 | 57 | 58 |

Your Data

59 | 60 |

Your data always remains in your control.

61 | 62 |

The source code you typed in the editor window is send to the C++ Insights server. At the server your source 63 | code is temporarily stored just to compile it, after that the source code on the server is deleted and the 64 | result 65 | is sent back to your browser.

66 | 67 |

Web logs

68 | 69 |

C++ Insights keeps web logs to be able to track down issues with the website. These logs are kept for a short 70 | period, after which they are permanently deleted.

71 | 72 |

Cookies & Local Storage

73 | 74 |

C++ Insights uses small pieces of information stored on your computer: Cookies and Local Storage. These help 75 | to 76 | restore your source code the next time you visit C++ Insights. It also keeps the C++ Standard you have 77 | selected. 78 | This information is stored on your computer. Only during the translation it is temporarily transferred to the 79 | C++ 80 | Insights server.

81 |

They are only used with the user's permission. See the Cookie Policy for 82 | more 83 | information.

84 | 85 | 86 | 87 |

A user can decide to create a short link using the C++ Insights web-interface. The code as well as a time 88 | stamp 89 | and all compiler/insights options are stored in a database together with the short link id. You have no 90 | guarantees, that the links will be kept forever.

91 | 92 |

C++ Insights and the GDPR

93 | 94 |

Name and Address of the controller

95 |

The Controller for the purposes of the General Data Protection Regulation (GDPR), other data protection laws 96 | applicable in member states of the European Union and other provisions related to data protection is:

97 | 98 |

Andreas Fertig, andy at cppinsights.io

99 | 103 |
104 | 105 | 106 |
107 | 108 | 109 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /app/static/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /app/static/settings.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | C++ Insights - Settings 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 41 | 42 | 43 |

C++ Insights - Local Settings

44 | 45 |
    46 |
47 | 48 | 49 | 53 |
54 | 55 |
56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /app/static/sitemap.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | https://cppinsights.io 4 | 5 | 6 | https://cppinsights.io/about.html 7 | 8 | 9 | https://cppinsights.io/cookie-policy.html 10 | 11 | 12 | https://cppinsights.io/privacy-policy.html 13 | 14 | 15 | https://cppinsights.io/examples.html 16 | 17 | 18 | -------------------------------------------------------------------------------- /app/templates/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | C++ Insights - 404 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 42 | 43 | 44 |

404 - Page Not Found

45 | 46 | Sorry, the content your are looking for is not there. 47 | 48 | 52 |
53 | 54 |
55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /app/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | C++ Insights{{ desc }} 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 |
41 | 212 | 213 | 214 |
215 |

Source:

216 | 217 |
218 |
219 | 220 | 221 | 222 | 223 | 224 |
225 |

Insight:

226 | 227 |
228 |
229 | 230 | 231 | 232 | 233 | 234 | 235 |
236 |

Console:

237 |
238 |
239 |
240 | 241 | 246 | 247 |
248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | -------------------------------------------------------------------------------- /app/templates/version.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | C++ Insights - Version Information 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 42 | 43 | 44 |

C++ Insights Version Information

45 | 46 | {{ version|safe }} 47 | 48 | 49 |
CodeMirror:
50 | 51 | 55 |
56 | 57 |
58 | 59 | 60 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /deploy_rsa.enc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andreasfertig/cppinsights-web/13b1b4886e5037313c9a8957332aead088c27795/deploy_rsa.enc -------------------------------------------------------------------------------- /insights.wsgi: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import sys 3 | import logging 4 | logging.basicConfig(stream=sys.stderr) 5 | sys.path.insert(0,"/home/insights/public_html/insights") 6 | 7 | from insights import app as application 8 | -------------------------------------------------------------------------------- /local.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # C++ Insights Web, copyright (c) by Andreas Fertig 5 | # Distributed under an MIT license. See /LICENSE 6 | #------------------------------------------------------------------------------ 7 | 8 | import app 9 | #------------------------------------------------------------------------------ 10 | 11 | if __name__ == "__main__": 12 | a = app.getApp() 13 | # set the parameters for running it without sudo 14 | a.config['USE_DOCKER'] = True 15 | a.config['USE_SUDO'] = False 16 | 17 | # run the app 18 | a.run(host='0.0.0.0') 19 | #------------------------------------------------------------------------------ 20 | 21 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cppinsights-web", 3 | "version": "1.0.0", 4 | "description": "C++ Insights - Web Front-End", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "nyc mocha", 8 | "report-coverage": "nyc --reporter=lcov npm test" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/andreasfertig/cppinsights-web.git" 13 | }, 14 | "author": "Andreas Fertig", 15 | "license": "MIT", 16 | "bugs": { 17 | "url": "https://github.com/andreasfertig/cppinsights-web/issues" 18 | }, 19 | "homepage": "https://github.com/andreasfertig/cppinsights-web#readme", 20 | "dependencies": { 21 | "grunt": "1.6.1", 22 | "grunt-cli": "^1.4.3", 23 | "js-beautify": "^1.8.8", 24 | "uglify-es": "github:mishoo/UglifyJS2#harmony" 25 | }, 26 | "devDependencies": { 27 | "async": "^3.2.5", 28 | "eslint": "^8.56.0", 29 | "grunt-contrib-copy": "^1.0.0", 30 | "grunt-contrib-cssmin": "^5.0.0", 31 | "grunt-contrib-htmlmin": "^3.1.0", 32 | "grunt-contrib-imagemin": "^4.0.0", 33 | "grunt-contrib-uglify": "^5.0.1", 34 | "grunt-contrib-uglify-es": "^3.3.0", 35 | "grunt-eslint": "^24.3.0", 36 | "grunt-jsbeautifier": "^0.2.13", 37 | "grunt-shell": "^4.0.0", 38 | "grunt-string-replace": "^1.3.1", 39 | "grunt-svgmin": "^7.0.0", 40 | "jsdom": "24.0.0", 41 | "jsdom-global": "3.0.2", 42 | "mocha": "^10.3.0", 43 | "nyc": "^15.1.0" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /pics/window-maximize.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /pics/window-minimize.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | flask 2 | pysqlite3-binary 3 | -------------------------------------------------------------------------------- /requirements_gh.txt: -------------------------------------------------------------------------------- 1 | flask 2 | pysqlite3 3 | coverage 4 | testfixtures 5 | mock 6 | -------------------------------------------------------------------------------- /test/cookie.test.js: -------------------------------------------------------------------------------- 1 | require('jsdom-global')() 2 | 3 | var app = require('../app/static/js/cookie.js'); 4 | 5 | // insert an additional cookie at front to test parsing multiple cookies 6 | function createCookie(name, value) { 7 | var days = 365; 8 | var date = new Date(); 9 | date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); 10 | var expires = '; expires=' + date.toGMTString(); 11 | 12 | document.cookie = name + '=' + value + expires + '; path=/'; 13 | } 14 | 15 | var assert = require('assert'); 16 | describe('Cookie', function() { 17 | createCookie('dummy', 'nothing'); 18 | // banner gets inserted during window load 19 | app.onLoad(); 20 | 21 | it('innerHTML contains cookie-law node', function() { 22 | assert.notEqual(document.getElementById('cookie-law'), null); 23 | }); 24 | 25 | it('storageAllowed returns false at startup', function() { 26 | assert.equal(app.storageAllowed(), false); 27 | }); 28 | 29 | it('User declines cookie -> cookieAccept(false)', function() { 30 | // user clicks decline 31 | app.cookieAccept(false); 32 | 33 | assert.equal(app.storageAllowed(), false); 34 | assert.equal(document.getElementById('cookie-law'), null); 35 | }); 36 | 37 | it('User accepts cookie -> cookieAccept(true)', function() { 38 | // simulate reload 39 | this.jsdom = require('jsdom-global')() 40 | app.onLoad(); 41 | 42 | // user clicks accept 43 | app.cookieAccept(true); 44 | 45 | assert.equal(app.storageAllowed(), true); 46 | assert.equal(document.getElementById('cookie-law'), null); 47 | }); 48 | 49 | it('ensure no cookie banner is shown', function() { 50 | // simulate reload 51 | var cookie = document.cookie; // keep existing cookies 52 | this.jsdom = require('jsdom-global')() // reset the DOM 53 | document.cookie = cookie; // restore existing cookies 54 | app.onLoad(); // reload 55 | 56 | assert.equal(document.getElementById('cookie-law'), null); 57 | }); 58 | 59 | }); 60 | --------------------------------------------------------------------------------