├── .gitignore ├── Makefile ├── README.md ├── appveyor.yml ├── bin ├── ZXPSignCmd ├── ZXPSignCmd.exe └── about.md ├── config.js ├── gulp ├── paths.js └── tasks │ ├── build.js │ ├── clean.js │ ├── lint.js │ └── watch.js ├── gulpfile.js ├── img ├── collapsed_view.PNG └── protio_wide_view.PNG ├── jsconfig.json ├── package.json ├── src ├── .debug ├── CSXS │ └── manifest.xml ├── bin │ └── set_debug_mode.bat ├── html │ └── opentimelineio.html ├── img │ ├── python.png │ └── terminal.png ├── js │ ├── lib │ │ └── app.js │ └── opentimelineio.js ├── jsx │ ├── json2.js │ └── opentimeline.jsx ├── lib │ ├── CSInterface.js │ └── jquery-1.9.1.js ├── python │ └── premiere-opentimelineio.py └── styles │ ├── _variables.scss │ ├── opentimelineio.scss │ ├── page_overrides │ └── opentimelineio.scss │ ├── partials │ ├── _base.scss │ ├── _buttons.scss │ └── _icons.scss │ └── vendor │ ├── bootstrap.css │ ├── devicon.css │ ├── font-awesome.css │ └── wing.css └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Jetbrains Webstorm project 6 | *.idea 7 | 8 | # Sublime Project 9 | *.sublime-project 10 | *.sublime-workspace 11 | 12 | # Python 13 | *.pyc 14 | 15 | # Runtime data 16 | pids 17 | *.pid 18 | *.seed 19 | 20 | # Directory for instrumented libs generated by jscoverage/JSCover 21 | lib-cov 22 | 23 | # Coverage directory used by tools like istanbul 24 | coverage 25 | 26 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 27 | .grunt 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build 34 | 35 | # Build files 36 | dist/ 37 | pydist/ 38 | *.egg-info 39 | .eggs 40 | 41 | # Dependency directory 42 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 43 | node_modules 44 | jspm_packages 45 | 46 | 47 | # Other 48 | .DS_Store 49 | .premiere_refresh_trigger 50 | 51 | # Certificate files for Adobe 52 | *.p12 53 | # Output folder 54 | output/ -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | DIST_DIR := $(shell pwd)/dist 2 | 3 | help: 4 | @echo "build - Create built extension for the current platform" 5 | @echo "install - Install the built extension" 6 | @echo "dev - Install in dev location" 7 | @echo "docs - Generate documentation" 8 | @echo "debug - Set the reg/plist keys for debugging a CEP extension" 9 | 10 | uname_S := None 11 | ifeq ($(OS),Windows_NT) 12 | uname_S := Windows 13 | else: 14 | uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not') 15 | endif 16 | 17 | DIST_DIR := $(shell pwd)/dist 18 | 19 | clean: clean-build clean-gulp 20 | 21 | clean-build: 22 | rm -rf build/ 23 | rm -rf dist/ 24 | 25 | clean-gulp: 26 | gulp clean 2>/dev/null || true 27 | 28 | ifneq ($(uname_S), Windows) 29 | debug: 30 | defaults write com.adobe.CSXS.8 PlayerDebugMode 1 31 | endif 32 | 33 | _docs: 34 | gulp docs 35 | 36 | docs: _docs 37 | open docs/protio/index.html 38 | 39 | release-docs: _docs 40 | 41 | deps:~ 42 | yarn install 43 | jspm install 44 | 45 | build-dev: 46 | gulp watch 47 | 48 | 49 | build: 50 | ifneq ($(uname_S), Windows) 51 | gulp build-prod 52 | ./bin/ZXPSignCmd -sign dist output/protio.zxp MyCert.p12 abc123 53 | else 54 | gulp build-prod 55 | endif 56 | 57 | deploy: 58 | ifneq ($(uname_S), Windows) 59 | rm "/Library/Application Support/Adobe/CEP/extensions/protio" 2>/dev/null || true 60 | mkdir -p "/Library/Application Support/Adobe/CEP/extensions/protio" 2>/dev/null || true 61 | cp output/protio.zxp "/Library/Application Support/Adobe/CEP/extensions/protio/protio.zxp" 62 | else 63 | cmd /c rmdir /Q "%APPDATA%\\Adobe\\CEP\\extensions\\protio" 2>/dev/null || true 64 | xcopy /EHK output/protio.zxp "%APPDATA%\\Adobe\\CEP\\extensions\\protio\\protio.zxp" 65 | endif 66 | 67 | 68 | ifeq ($(uname_S), Windows) 69 | dev: clean 70 | cmd /c rmdir /Q "%APPDATA%\\Adobe\\CEP\\extensions\\protio" 2>/dev/null || true 71 | cmd /c mklink /J "%APPDATA%\\Adobe\\CEP\\extensions\\protio" "$(shell cygpath -w $(DIST_DIR))" 72 | gulp watch 73 | else 74 | dev: clean 75 | rm "/Library/Application Support/Adobe/CEP/extensions/protio" 2>/dev/null || true 76 | ln -s $(DIST_DIR) "/Library/Application Support/Adobe/CEP/extensions/protio" 77 | gulp watch 78 | endif 79 | 80 | uninstall: 81 | cmd /c rmdir /Q "%APPDATA%\\Adobe\\CEP\\extensions\\premiere-otio" 2>/dev/null || true 82 | 83 | ifneq ($(uname_S), Windows) 84 | create-cert: 85 | ./bin/ZXPSignCmd -selfSignedCert US OR MyCompanyName MyPersonName abc123 MyCert.p12 86 | endif 87 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Protio 2 | ======== 3 | Premiere OpenTimelineIO extension. Pronounced "Pro-T-Oh". 4 | 5 | Allows you to import and export OpenTimelineIO sequences, and launch the otioview application. 6 | 7 | ![Wide View](https://github.com/alexwidener/protio/raw/master/img/protio_wide_view.PNG) 8 | 9 | ![Collapsed View](https://github.com/alexwidener/protio/raw/master/img/collapsed_view.PNG) 10 | 11 | Contribution 12 | ============= 13 | [Michael Nowakowski](https://github.com/pantsworth) - Helped a lot on the UI design 14 | 15 | [Bruce Bullis](https://github.com/bbb999) - Insurmountable Premiere Wisdom 16 | 17 | Installation 18 | ============= 19 | ##### Building requirements: 20 | 21 | - npm or yarn (yarn is in the make file, change it to npm if you need to) 22 | - gulp 23 | - jspm 24 | - make 25 | - On Windows, cygwin. cmd is not supported. 26 | 27 | ##### Runtime requirements: 28 | 29 | - Python 2.7/3.6+ 30 | 31 | 32 | ## Building For Dev: 33 | 34 | If you're not familiar with make, you should [get familiar with it first](https://www.gnu.org/software/make/#content). 35 | 36 | To build a local dev copy in order to do work, you have to build a folder path first. That folder path is: 37 | 38 | ```markdown 39 | "%APPDATA%\\Adobe\\CEP\\extensions\\protio" 40 | ``` 41 | 42 | or 43 | 44 | ```markdown 45 | /Library/Application Support/Adobe/CEP/extensions/protio 46 | ``` 47 | 48 | 49 | ```markdown 50 | cd /c/dev/protio 51 | yarn global add gulp (This is only required the very first time) 52 | yarn global add jspm (This is only required the very first time) 53 | make deps 54 | make build-dev 55 | gulp watch (Doesn't seem to be picking up on Windows inside build-dev, need to look into this). 56 | ``` 57 | 58 | If you notice inside of the protio source folder, there is a .debug file. This file states what port the extension 59 | is debugged on, which is 6145. Go to `localhost:6145` in a Chrome browser and you can do debugging. 60 | 61 | However, you can't do that just yet if this is your first time developing an Adobe CEP extension. You can't even open the 62 | extension inside of Premiere yet, even though it shows up under Window > Extensions > Protio 63 | 64 | If you are in the midst of development, you can bypass the check for extension signature by editing the CSXS preference 65 | properties file, located at: 66 | 67 | ``` 68 | Win: regedit > 69 | HKEY_CURRENT_USER/Software/Adobe/CSXS.8 70 | Add a new entry PlayerDebugMode of type "string" with the value of "1". 71 | ``` 72 | 73 | ``` 74 | Mac: In the terminal, type: 75 | defaults write com.adobe.CSXS.8 PlayerDebugMode 1 76 | (The plist is also located at /Users/USERNAME/Library/Preferences/com.adobe.CSXS.8.plist) 77 | ``` 78 | 79 | This puts your Adobe apps into debug mode, and now you can open the application. 80 | 81 | While Gulp is running, you can update files and close and reopen the extension to test changes. At a studio that I used 82 | to work at, we had a Ctrl + R refresh working at some point, but an update eventually ruined what allowed that to work. 83 | It currently works intermittently and eventually stops working altogether. 84 | 85 | Additionally, you can now go to [localhost:6145](http://localhost:6145) and run the debugger. 86 | 87 | ## Building for Release: 88 | 89 | 90 | Usage 91 | ====== 92 | After installing, go to Window > Extensions > Protio 93 | 94 | Then, click either the `import otio as sequence` button or the `export sequence as otio` button. 95 | 96 | 97 | Deployment 98 | =========== 99 | If you work in a studio environment... have fun. Adobe deployment for their packages is atrocious 100 | and is not built for a scalable, distributed solution like one that is needed at visual effects studios. 101 | Ironic, right? They have this desire to want to put everything in their store, but stuff like that does not 102 | work when you're off network, or need to push out constant updates. 103 | 104 | At the studio that I used to work at, we had a deployment manager called Ansible (but you can do the same thing 105 | with a Docker container) that moved packages around on disk and we would put a variation of the development 106 | workflow on disk locally for the editors. 107 | 108 | To build everything out into a deployable package, run 109 | 110 | ```markdown 111 | make build 112 | ``` 113 | 114 | This will make a folder called dist/ and a folder called output, and you want to take the built extension from the output 115 | folder and distribute that. 116 | 117 | As a note, the Makefile contains default fake data in the build step for the ZXPSignCmd. Fill this with information for 118 | you or your organization. 119 | 120 | Finally, you can run the 121 | 122 | ```markdown 123 | make deploy 124 | ``` 125 | 126 | step which will place the built .zxp in the output/ folder in the correct location for Premiere to find it. 127 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | # Test against the latest version of this Node.js version 2 | environment: 3 | nodejs_version: "6" 4 | 5 | # Install scripts. (runs after repo cloning) 6 | install: 7 | # Get the latest stable version of Node.js or io.js 8 | - ps: Install-Product node $env:nodejs_version 9 | # install modules 10 | - npm install 11 | 12 | # Post-install test scripts. 13 | test_script: 14 | # Output useful info for debugging. 15 | - node --version 16 | - npm --version 17 | # run tests 18 | - npm test 19 | 20 | # Don't actually build. 21 | build: off 22 | -------------------------------------------------------------------------------- /bin/ZXPSignCmd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boredstiff/protio/37f50be177c9204fc0a2ac413e2745fa32cfcaf7/bin/ZXPSignCmd -------------------------------------------------------------------------------- /bin/ZXPSignCmd.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boredstiff/protio/37f50be177c9204fc0a2ac413e2745fa32cfcaf7/bin/ZXPSignCmd.exe -------------------------------------------------------------------------------- /bin/about.md: -------------------------------------------------------------------------------- 1 | As best as I know, this is version 4.0.7 of the ZXPSignCmd. Why wouldn't Adobe put something as simple as the 2 | version number into one of their tools? Your guess is as good as mine. 3 | -------------------------------------------------------------------------------- /config.js: -------------------------------------------------------------------------------- 1 | System.config({ 2 | defaultJSExtensions: true, 3 | transpiler: "babel", 4 | babelOptions: { 5 | "optional": [ 6 | "runtime", 7 | "optimisation.modules.system" 8 | ] 9 | }, 10 | paths: { 11 | "*": "src/js/*", 12 | "github:*": "jspm_packages/github/*", 13 | "npm:*": "jspm_packages/npm/*" 14 | }, 15 | 16 | map: { 17 | "babel": "npm:babel-core@5.8.38", 18 | "babel-runtime": "npm:babel-runtime@5.8.38", 19 | "bootstrap": "npm:bootstrap@4.0.0-alpha.6", 20 | "core-js": "npm:core-js@1.2.7", 21 | "devicons": "npm:devicons@1.8.0", 22 | "font-awesome": "npm:font-awesome@4.7.0", 23 | "loglevel": "github:pimterry/loglevel@1.4.1", 24 | "path": "github:jspm/nodelibs-path@0.2.3", 25 | "process": "github:jspm/nodelibs-process@0.1.2", 26 | "shell-quote": "npm:shell-quote@1.6.1", 27 | "github:jspm/nodelibs-assert@0.1.0": { 28 | "assert": "npm:assert@1.4.1" 29 | }, 30 | "github:jspm/nodelibs-buffer@0.1.1": { 31 | "buffer": "npm:buffer@5.0.7" 32 | }, 33 | "github:jspm/nodelibs-path@0.1.0": { 34 | "path-browserify": "npm:path-browserify@0.0.0" 35 | }, 36 | "github:jspm/nodelibs-process@0.1.2": { 37 | "process": "npm:process@0.11.10" 38 | }, 39 | "github:jspm/nodelibs-util@0.1.0": { 40 | "util": "npm:util@0.10.3" 41 | }, 42 | "github:jspm/nodelibs-vm@0.1.0": { 43 | "vm-browserify": "npm:vm-browserify@0.0.4" 44 | }, 45 | "npm:assert@1.4.1": { 46 | "assert": "github:jspm/nodelibs-assert@0.1.0", 47 | "buffer": "github:jspm/nodelibs-buffer@0.1.1", 48 | "process": "github:jspm/nodelibs-process@0.1.2", 49 | "util": "npm:util@0.10.3" 50 | }, 51 | "npm:babel-runtime@5.8.38": { 52 | "process": "github:jspm/nodelibs-process@0.1.2" 53 | }, 54 | "npm:bootstrap@4.0.0-alpha.6": { 55 | "jquery": "npm:jquery@3.2.1", 56 | "tether": "github:HubSpot/tether@1.4.0" 57 | }, 58 | "npm:buffer@5.0.7": { 59 | "base64-js": "npm:base64-js@1.2.1", 60 | "ieee754": "npm:ieee754@1.1.8" 61 | }, 62 | "npm:core-js@1.2.7": { 63 | "fs": "github:jspm/nodelibs-fs@0.1.2", 64 | "path": "github:jspm/nodelibs-path@0.1.0", 65 | "process": "github:jspm/nodelibs-process@0.1.2", 66 | "systemjs-json": "github:systemjs/plugin-json@0.1.2" 67 | }, 68 | "npm:font-awesome@4.7.0": { 69 | "css": "github:systemjs/plugin-css@0.1.35" 70 | }, 71 | "npm:inherits@2.0.1": { 72 | "util": "github:jspm/nodelibs-util@0.1.0" 73 | }, 74 | "npm:path-browserify@0.0.0": { 75 | "process": "github:jspm/nodelibs-process@0.1.2" 76 | }, 77 | "npm:process@0.11.10": { 78 | "assert": "github:jspm/nodelibs-assert@0.1.0", 79 | "fs": "github:jspm/nodelibs-fs@0.1.2", 80 | "vm": "github:jspm/nodelibs-vm@0.1.0" 81 | }, 82 | "npm:shell-quote@1.6.1": { 83 | "array-filter": "npm:array-filter@0.0.1", 84 | "array-map": "npm:array-map@0.0.0", 85 | "array-reduce": "npm:array-reduce@0.0.0", 86 | "jsonify": "npm:jsonify@0.0.0" 87 | }, 88 | "npm:util@0.10.3": { 89 | "inherits": "npm:inherits@2.0.1", 90 | "process": "github:jspm/nodelibs-process@0.1.2" 91 | }, 92 | "npm:vm-browserify@0.0.4": { 93 | "indexof": "npm:indexof@0.0.1" 94 | } 95 | } 96 | }); 97 | -------------------------------------------------------------------------------- /gulp/paths.js: -------------------------------------------------------------------------------- 1 | var appRoot = 'src/' 2 | var outputRoot = 'dist' 3 | var baseRoot = './' 4 | 5 | module.exports = { 6 | root: appRoot, 7 | baseRoot: baseRoot, 8 | js: appRoot + 'js/**/*.js', 9 | jsToBundle: appRoot + 'js/*.js', 10 | jsToOutput: outputRoot + '/js', 11 | jsx: baseRoot + 'jsx/**/*.jsx', 12 | html: baseRoot + appRoot + 'html/*.html', 13 | css: appRoot + '**/*.css', 14 | less: appRoot + '**/*.less', 15 | sass: appRoot + '**/*.scss', 16 | outputRoot: outputRoot, 17 | htmlOutput: outputRoot, 18 | configjs: "config.js", 19 | doc: './doc', 20 | premiereTrigger: './.premiere_refresh_trigger', 21 | premiereDebug: appRoot + './.debug', 22 | devSymlinks: [ 23 | appRoot + 'CSXS', 24 | appRoot + 'js', 25 | appRoot + 'jsx', 26 | appRoot + 'img', 27 | appRoot + 'lib', 28 | appRoot + 'python', 29 | 'jspm_packages' 30 | ], 31 | prodCopy: [ 32 | appRoot + "CSXS/**.xml", 33 | appRoot + "files/**/*.*", 34 | appRoot + "img/**/*.*", 35 | appRoot + "jsx/**.*", 36 | appRoot + "lib/**.*", 37 | appRoot + "python/**.py", 38 | ], 39 | devJSPMCopy: [ 40 | 'package.json', 41 | 'jsconfig.json' 42 | ] 43 | } 44 | -------------------------------------------------------------------------------- /gulp/tasks/build.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp') 2 | var git = require('gulp-git') 3 | var gitRev = require('git-rev') 4 | var gulpDocumentation = require('gulp-documentation') 5 | var runSequence = require('run-sequence') 6 | var changed = require('gulp-changed') 7 | var sass = require('gulp-sass') 8 | var notify = require('gulp-notify') 9 | var paths = require('../paths') 10 | var path = require('path') 11 | var systemJs = require('systemjs-builder') 12 | var replace = require('gulp-replace') 13 | var vinylFS = require('vinyl-fs') 14 | 15 | gulp.task('build-css', function() { 16 | return gulp.src(paths.css) 17 | .pipe(changed(paths.outputRoot, {extension: 'css'})) 18 | .pipe(gulp.dest(paths.outputRoot)) 19 | }) 20 | 21 | gulp.task('build-sass', function() { 22 | gulp.src(paths.sass) 23 | .pipe(sass().on('error', notify.onError('Error: <%= error.message %>'))) 24 | .pipe(gulp.dest(paths.outputRoot)) 25 | }) 26 | 27 | gulp.task('build-docs', function() { 28 | return gulp.src('./src/js/**/*.js') 29 | .pipe(gulpDocumentation('html')) 30 | .pipe(gulp.dest('dist/docs')) 31 | }) 32 | 33 | gulp.task('build-dev-copy-jspm-config', function() { 34 | return gulp.src(paths.devJSPMCopy) 35 | .pipe(changed(paths.outputRoot)) 36 | .pipe(gulp.dest(paths.outputRoot)) 37 | }) 38 | 39 | gulp.task('build-dev-html', function() { 40 | return gitRev.tag(function(tag) { 41 | return gitRev.short(function(hash) { 42 | return gulp.src(paths.html) 43 | .pipe(changed(paths.htmlOutput)) 44 | .pipe(replace(/ 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 26 | 27 |
28 | 31 | 34 | 37 |
38 |
39 | 40 |
41 |
42 | 43 |
44 |
45 |
46 | 47 |
/c/dev/cygwin
48 |
49 |
50 |
51 |
52 | 53 |
/c/dev/OpenTimelineIO/venv/bin/python36.exe
54 |
55 |
56 |
57 | 66 |
67 |
68 |
69 |
70 | 71 |
72 | 76 |
77 |
78 |
79 |
80 | 81 | -------------------------------------------------------------------------------- /src/img/python.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boredstiff/protio/37f50be177c9204fc0a2ac413e2745fa32cfcaf7/src/img/python.png -------------------------------------------------------------------------------- /src/img/terminal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boredstiff/protio/37f50be177c9204fc0a2ac413e2745fa32cfcaf7/src/img/terminal.png -------------------------------------------------------------------------------- /src/js/lib/app.js: -------------------------------------------------------------------------------- 1 | console.log('starting app imports') 2 | // const log = require('loglevel') 3 | import * as log from 'loglevel' 4 | import * as shellquote from 'shell-quote' 5 | 6 | import * as process from 'process' 7 | 8 | let fs = require('fs') 9 | let path = require('path') 10 | 11 | // Debugging 12 | log.setLevel(0) 13 | 14 | /** 15 | * The main application class. 16 | */ 17 | export class App { 18 | constructor() { 19 | // log.debug('Building application') 20 | try { 21 | log.info('loading') 22 | this.loadJSX() 23 | } catch (err) { 24 | log.error(err) 25 | } 26 | 27 | var cs = new CSInterface() 28 | this.extensionPath = cs.getSystemPath(SystemPath.EXTENSION) 29 | // This one should already be normalized. 30 | this.pythonScriptPath = [this.extensionPath, 'python', 'premiere-opentimelineio.py'].join('/').replace('\\', '/') 31 | this.localConfigurationPath = path.resolve(this.extensionPath, '.protio') 32 | this.localConfiguration = this.getConfiguration(this.localConfigurationPath) 33 | this.userConfigurationPath = path.resolve(this.getHomeDir(), '.protio') 34 | this.userConfiguration = this.getConfiguration(this.userConfigurationPath) 35 | this.configuration = this.validateConfiguration(this.setUpConfiguration()) 36 | 37 | this.terminal = this.configuration['terminal'] 38 | // May need to set these up 39 | this.terminalArgs = this.configuration['terminal_args'] 40 | this.pythonInterpreter = this.configuration['python_interpreter'] 41 | this.pythonArgs = this.configuration['python_args'] 42 | 43 | log.info('configuration: ', this.configuration) 44 | } 45 | 46 | /** 47 | * 48 | * @param configuration 49 | * @returns {*} 50 | */ 51 | validateConfiguration(configuration) { 52 | // Need to find a good schema validator, but all the ones on npm are shit. Just like everything on npm. 53 | let required_keys = ['terminal', 'terminal_args', 'python_interpreter', 'python_args'] 54 | for (let i = 0; i < required_keys.length; i++) { 55 | if (!(configuration.hasOwnProperty(required_keys[i]))) { 56 | log.error('Missing required key in configuration: ', required_keys[i]) 57 | throw new Error('Missing required key in configuration: ', required_keys[i]) 58 | } 59 | } 60 | return configuration 61 | } 62 | 63 | /** 64 | * Determine which configuration to use. If the configuration exists locally inside of the extension, then that 65 | * one should have information. If a configuration is inside of the extension, then this is an extension that 66 | * has shipped with a Python interpreter with it. In that case, we usually do not need to have a user configuration. 67 | * However, if the user configuration does exist, it should override the one stored locally inside of the extension. 68 | * @returns {*} 69 | */ 70 | setUpConfiguration() { 71 | let configuration = this.localConfiguration 72 | if (this.userConfiguration !== {}) 73 | configuration = this.userConfiguration 74 | log.info('setUpConfiguration: ', configuration) 75 | return configuration 76 | } 77 | 78 | /** 79 | * Load the configuration. A configuration is stored as a JSON dictionary in one of two locations, and both 80 | * locations will be given by the path variable. The two places that a configuration can be are 81 | * 1 ) Inside of the extension, which means that this is a version of the CEP extension that is shipping with 82 | * a Python interpreter. 83 | * 2 ) In the user home directory. 84 | * In both cases, the file will be named .protio. 85 | * If the file exists in location 1 and 2, location 2 will overwrite location 1. 86 | * If the file exists in location 1 and not location 2, it will use that one. 87 | * If the file exists in location 2 and not location 1, it will use that one. 88 | * If the file does not exist, well then... Make one in location 2. 89 | * @param path 90 | * @returns {{}} 91 | */ 92 | getConfiguration(path) { 93 | if (path === null) { 94 | throw new Error('Unable to continue loading the configuration without a path.') 95 | } 96 | let data = {} 97 | if (this.doesConfigExist(path)) { 98 | data = JSON.parse(fs.readFileSync(path, 'utf8')) 99 | } 100 | return data 101 | } 102 | 103 | /** 104 | * Perform a csInterface evalScript as a Promise. 105 | * @param {string} command - An Extendscript command to perform. 106 | */ 107 | evalScript(command) { 108 | log.debug('Evalscript command: ', command) 109 | return new Promise(function(resolve, reject) { 110 | var cs = new CSInterface() 111 | return cs.evalScript(command, resolve) 112 | }) 113 | } 114 | 115 | /** 116 | * Set paths up to use forward slashes, replacing any path join messed up results. 117 | * @param path 118 | * @returns {*} 119 | */ 120 | normalizePath(path) { 121 | log.debug('Inside normalizePath before running the replace: ', path) 122 | path = path.replace(/[\\/]+/g, '/'); 123 | log.debug('After running the replace: ', path) 124 | return path 125 | } 126 | 127 | /** 128 | * Extendscript takes paths with backward slashes, while the rest of the code needs to take paths with forward 129 | * slashes... way to go, Adobe. Another great innovation. 130 | * @param path 131 | * @returns {*} 132 | */ 133 | makeJSXPath(path) { 134 | log.debug('Inside makeJSXPath before running the replace: ', path) 135 | path = path.replace(/[\\/]+/g, '\\\\'); 136 | log.debug('After running the replace: ', path) 137 | return path 138 | } 139 | 140 | /** 141 | * Get the home directory for the current user (or the user name this is passed in) 142 | * @param username 143 | * @returns {*} 144 | */ 145 | getHomeDir(username) { 146 | let home = process.env[(process.platform == 'win32') ? 'USERPROFILE' : 'HOME'] 147 | return username ? path.resolve(path.dirname(home), username) : home 148 | } 149 | 150 | /** 151 | * Check whether to see if the configuration file exists. If anything happens, I don't give a shit, it just doesn't 152 | * exist, don't load it. 153 | * @param configPath 154 | * @returns {*} 155 | */ 156 | doesConfigExist(configPath) { 157 | try { 158 | return fs.existsSync(configPath) 159 | } catch (err) { return false } 160 | } 161 | 162 | loadJSX() { 163 | log.info('Loading JSX') 164 | let cs = new CSInterface() 165 | let extensionRoot = cs.getSystemPath(SystemPath.EXTENSION) + '/jsx/opentimeline.jsx' 166 | log.debug('extensionRoot: ', extensionRoot) 167 | cs.evalScript('$.OpenTimelineIOTools.evalFile("' + extensionRoot + '")') 168 | } 169 | 170 | openFileInBrowser(url) { 171 | log.debug('Opening file url in system browser: ', url) 172 | if (window.cep) { 173 | window.cep.process.createProcess('/usr/bin/open', url) 174 | } else { 175 | window.open(url, '_blank') 176 | } 177 | } 178 | 179 | runPython(args, stdout, stderr) { 180 | let pythonArgs = [ 181 | this.terminal, this.terminalArgs, this.pythonInterpreter, '-u', this.pythonScriptPath].concat(args) 182 | 183 | return this.stream(pythonArgs, stdout, stderr) 184 | } 185 | 186 | openURL(url) { 187 | log.debug("Opening url: ", url) 188 | var csInterface = new CSInterface(); 189 | csInterface.openURLInDefaultBrowser(url); 190 | } 191 | 192 | stream(args, stdout, stderr) { 193 | console.log('starting runPython') 194 | 195 | function read(procID, stream, _array, callback) { 196 | console.log('reading...') 197 | let watch = true 198 | let data_function = function (data) { 199 | console.log('data inside data_function: ', data) 200 | if (data != null) { 201 | let lines = data.split('\n').filter(function(n) { return n != ''}) 202 | console.log('pushing: ', lines) 203 | console.log('array: ', _array) 204 | _array.push.apply(_array, lines) 205 | if (callback) { callback(lines) } 206 | } 207 | if (!watch) { return } 208 | window.cep.process[stream](procID, this.bind(this)) 209 | } 210 | 211 | data_function.bind(data_function)() 212 | return { stop: function() { watch = false }} 213 | } 214 | 215 | return new Promise(function(resolve, reject) { 216 | if (window.cep) { 217 | console.log('inside runPython promise') 218 | console.log('args: ', args) 219 | let proc = window.cep.process.createProcess.apply(this, args) 220 | let procID = proc.data 221 | console.log('procID: ', procID) 222 | let stdoutLines = [] 223 | let stderrLines = [] 224 | let stdoutWatch, stderrWatch 225 | 226 | if (stdout) { 227 | console.log('stdout: going to read: ', stdout) 228 | stdoutWatch = read(procID, 'stdout', stdoutLines, stdout) 229 | } else { 230 | console.log('stdout inside of else: ', stdout) 231 | window.cep.process.stdout(procID, function(line) { stdoutLines.push(line); console.log('stdOutLines: ', stdoutLines) }) 232 | } 233 | 234 | if (stderr) { 235 | console.log('stderr: going to read: ', stderr) 236 | stderrWatch = read(procID, 'stderr', stderrLines, stderr) 237 | } else { 238 | console.log('stderr inside of else: ', stderr) 239 | window.cep.process.stderr(procID, function(line) { stderrLines.push(line); console.log('stdErrLines: ', stderrLines) }) 240 | } 241 | 242 | function respond() { 243 | console.log('inside else of wait') 244 | let data = { 245 | proc: proc, 246 | stdout: stdoutLines.join('\n'), 247 | stderr: stderrLines.join('\n') 248 | } 249 | if (stdoutWatch) { console.log('stdoutWatchStop'); stdoutWatch.stop() } 250 | if (stderrWatch) { console.log('stderrWatchStop'); stderrWatch.stop() } 251 | console.log('proc.err ', proc.err) 252 | if (proc.err === 0) { 253 | resolve(data) 254 | } else { 255 | reject(data) 256 | } 257 | } 258 | 259 | function waitForStuff() { 260 | console.log('waiting') 261 | if (!proc.data) { 262 | setTimeout(waitForStuff, 1000) 263 | } else { 264 | respond() 265 | } 266 | } 267 | window.cep.process.onquit(procID, waitForStuff) 268 | } else { 269 | console.log('else at end of runPython') 270 | resolve('', '') 271 | } 272 | }.bind(this)) 273 | } 274 | 275 | } 276 | 277 | -------------------------------------------------------------------------------- /src/js/opentimelineio.js: -------------------------------------------------------------------------------- 1 | import * as log from 'loglevel' 2 | import path from 'path' 3 | import {App} from 'lib/app' 4 | 5 | import * as process from 'process' 6 | 7 | log.setLevel(0) 8 | 9 | /** 10 | * The main OpenTimelineIO class 11 | */ 12 | export class OpenTimelineIO { 13 | constructor(app) { 14 | log.info('starting to attach app') 15 | this.app = app 16 | log.info('Attached the app') 17 | } 18 | 19 | init() { 20 | log.info('init run') 21 | $('#export-btn').click(function() { 22 | this.exportOpenTimelineIO() 23 | }.bind(this)) 24 | 25 | $('#import-btn').click(function() { 26 | this.importOpenTimelineIO() 27 | }.bind(this)) 28 | 29 | this.status_field = $('#status-field') 30 | // For whatever reason, a textarea has a tab of empty space on initialization. 31 | // Stupid web shit. 32 | this.status_field.text('') 33 | 34 | 35 | let acc = document.getElementById("dev-info-panel") 36 | acc.addEventListener("click", function() { 37 | /* Toggle between adding and removing the "active" class, 38 | to highlight the button that controls the panel */ 39 | this.classList.toggle("active") 40 | 41 | /* Toggle between hiding and showing the active panel */ 42 | let panel = this.nextElementSibling 43 | if (panel.style.display === "block") { 44 | panel.style.display = "none" 45 | this.textContent = "" 46 | this.innerHTML = "▶ Dev Info" 47 | } else { 48 | panel.style.display = "block" 49 | this.textContent = "" 50 | this.innerHTML = "▼ Dev Info" 51 | } 52 | 53 | }); 54 | } 55 | 56 | exportActiveSequenceAsFCP7XML(path) { 57 | return this.app.evalScript('$.OpenTimelineIOTools.exportActiveSequenceAsFCP7XML("' + path + '")') 58 | } 59 | 60 | chooseExportLocation() { 61 | return this.app.evalScript('$.OpenTimelineIOTools.chooseOTIOExportLocation()') 62 | } 63 | 64 | /** 65 | * 66 | * @returns {Promise} 67 | */ 68 | selectOpenTimelineFile() { 69 | return this.app.evalScript('$.OpenTimelineIOTools.selectOTIOFileToImport()') 70 | } 71 | 72 | importSequence(path) { 73 | console.log('fucking path: ', path) 74 | return this.app.evalScript('$.OpenTimelineIOTools.importSequence()') 75 | } 76 | 77 | getTempFolder() { 78 | let folderPath 79 | let isWindows = process.platform === 'win32' 80 | let trailingSlashRegex = isWindows ? /[^:]\\$/ : /.\/$/ 81 | if (isWindows) { 82 | folderPath = process.env.TEMP || process.env.TMP || (process.env.SystemRoot || process.env.windir ) + '\\temp' 83 | } else { 84 | folderPath = process.env.TMPDIR || process.env.TMP || process.env.TEMP || '/tmp' 85 | } 86 | if (trailingSlashRegex.test(folderPath)) { 87 | folderPath = folderPath.slice(0, -1) 88 | } 89 | return folderPath 90 | } 91 | 92 | generateTempPath() { 93 | let now = new Date() 94 | let folderPath = this.getTempFolder() 95 | let fileName = [now.getYear(), now.getMonth(), now.getDate(), '-', process.pid, '-', (Math.random() * 0x100000000 + 1).toString(36), '.xml'].join('') 96 | return path.join(folderPath, fileName) 97 | } 98 | 99 | /** 100 | * Export a Premiere Pro Sequence as an OpenTimelineIO file. 101 | * 102 | * This is done by exporting a FCP7XML file to a temporary location, then loading that file into OpenTimelineIO, 103 | * running the adapter on it, and writing that out to disk. 104 | * @returns {Promise|Promise.} 105 | */ 106 | exportOpenTimelineIO() { 107 | // Get the export location 108 | return this.chooseExportLocation() 109 | .then(function(data) { 110 | // 'data' coming in should be a full path selected by the user. 111 | // TODO: Some validation on this path in here. 112 | // Is it possible for the UI to hand back a folder path? 113 | // Need to also populate with '.otio' if it doesn't exist. 114 | let temp_path = this.generateTempPath() 115 | let jsx_temp_path = this.app.makeJSXPath(temp_path) 116 | // Then export final cut pro xml with the temp path 117 | return this.exportActiveSequenceAsFCP7XML(jsx_temp_path) 118 | .then(function() { 119 | console.log('Sequence should be exported, calling python') 120 | // Then run conversion on the temp path file to the user-selected 121 | // output path 122 | 123 | let python_args = [ 124 | 'export-file', 125 | '--input', 126 | this.app.normalizePath(temp_path), 127 | '--output', 128 | this.app.normalizePath(data) 129 | ] 130 | 131 | log.debug('Export Python arguments before calling: ', python_args) 132 | return this.app.runPython(python_args) 133 | .then(function(python_output) { 134 | if (python_output.stderr) { 135 | this.status_field.text(python_output.stderr) 136 | } 137 | console.log('Python output: ', python_output) 138 | }) 139 | }.bind(this)) 140 | }.bind(this)) 141 | } 142 | 143 | /** 144 | * Import an OpenTimelineIO file into Premiere. 145 | * 146 | * This is handled by using the adapters provided by OpenTimelineIO, namely for the FCP7XML Adaoter. I take the 147 | * given OTIO file, create an OTIO object, write it to FCP7XML in a temporary location, and import that into PPro. 148 | * @returns {Promise|Promise.} 149 | */ 150 | importOpenTimelineIO() { 151 | return this.selectOpenTimelineFile() 152 | .then(function(path) { 153 | // Clear out the status field. 154 | this.status_field.text('') 155 | console.log('About to convert ', path, ' to FCP7XML') 156 | let temp_path = this.generateTempPath() 157 | temp_path = this.app.normalizePath(temp_path) 158 | 159 | let python_args = [ 160 | 'convert-file', 161 | '--format', 162 | 'fcp_xml', 163 | '--input', 164 | this.app.normalizePath(path), 165 | '--temp-file', 166 | this.app.normalizePath(temp_path) 167 | ] 168 | 169 | log.debug('Convert Python arguments before calling: ', python_args) 170 | 171 | return this.app.runPython(python_args) 172 | .then(function(data) { 173 | if (data.stderr) { 174 | msg = data.stderr 175 | alert(msg) 176 | this.status_field.text(data.stderr) 177 | throw new Error(msg) 178 | } 179 | let temp_import_path = data.stdout 180 | temp_import_path = this.app.makeJSXPath(temp_import_path) 181 | log.debug('Temp Import Path: ', temp_import_path) 182 | return this.importSequence(temp_import_path) 183 | // Then take THAT output and import FinalCutProXML 184 | }.bind(this)) 185 | }.bind(this)) 186 | } 187 | } 188 | 189 | $(document).ready(function() { 190 | console.log('document ready') 191 | let application = new App() 192 | window.view = new OpenTimelineIO(application) 193 | window.view.init() 194 | }) -------------------------------------------------------------------------------- /src/jsx/json2.js: -------------------------------------------------------------------------------- 1 | // json2.js 2 | // 2016-10-28 3 | // Public Domain. 4 | // NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. 5 | // See http://www.JSON.org/js.html 6 | // This code should be minified before deployment. 7 | // See http://javascript.crockford.com/jsmin.html 8 | 9 | // USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO 10 | // NOT CONTROL. 11 | 12 | // This file creates a global JSON object containing two methods: stringify 13 | // and parse. This file provides the ES5 JSON capability to ES3 systems. 14 | // If a project might run on IE8 or earlier, then this file should be included. 15 | // This file does nothing on ES5 systems. 16 | 17 | // JSON.stringify(value, replacer, space) 18 | // value any JavaScript value, usually an object or array. 19 | // replacer an optional parameter that determines how object 20 | // values are stringified for objects. It can be a 21 | // function or an array of strings. 22 | // space an optional parameter that specifies the indentation 23 | // of nested structures. If it is omitted, the text will 24 | // be packed without extra whitespace. If it is a number, 25 | // it will specify the number of spaces to indent at each 26 | // level. If it is a string (such as "\t" or " "), 27 | // it contains the characters used to indent at each level. 28 | // This method produces a JSON text from a JavaScript value. 29 | // When an object value is found, if the object contains a toJSON 30 | // method, its toJSON method will be called and the result will be 31 | // stringified. A toJSON method does not serialize: it returns the 32 | // value represented by the name/value pair that should be serialized, 33 | // or undefined if nothing should be serialized. The toJSON method 34 | // will be passed the key associated with the value, and this will be 35 | // bound to the value. 36 | 37 | // For example, this would serialize Dates as ISO strings. 38 | 39 | // Date.prototype.toJSON = function (key) { 40 | // function f(n) { 41 | // // Format integers to have at least two digits. 42 | // return (n < 10) 43 | // ? "0" + n 44 | // : n; 45 | // } 46 | // return this.getUTCFullYear() + "-" + 47 | // f(this.getUTCMonth() + 1) + "-" + 48 | // f(this.getUTCDate()) + "T" + 49 | // f(this.getUTCHours()) + ":" + 50 | // f(this.getUTCMinutes()) + ":" + 51 | // f(this.getUTCSeconds()) + "Z"; 52 | // }; 53 | 54 | // You can provide an optional replacer method. It will be passed the 55 | // key and value of each member, with this bound to the containing 56 | // object. The value that is returned from your method will be 57 | // serialized. If your method returns undefined, then the member will 58 | // be excluded from the serialization. 59 | 60 | // If the replacer parameter is an array of strings, then it will be 61 | // used to select the members to be serialized. It filters the results 62 | // such that only members with keys listed in the replacer array are 63 | // stringified. 64 | 65 | // Values that do not have JSON representations, such as undefined or 66 | // functions, will not be serialized. Such values in objects will be 67 | // dropped; in arrays they will be replaced with null. You can use 68 | // a replacer function to replace those with JSON values. 69 | 70 | // JSON.stringify(undefined) returns undefined. 71 | 72 | // The optional space parameter produces a stringification of the 73 | // value that is filled with line breaks and indentation to make it 74 | // easier to read. 75 | 76 | // If the space parameter is a non-empty string, then that string will 77 | // be used for indentation. If the space parameter is a number, then 78 | // the indentation will be that many spaces. 79 | 80 | // Example: 81 | 82 | // text = JSON.stringify(["e", {pluribus: "unum"}]); 83 | // // text is '["e",{"pluribus":"unum"}]' 84 | 85 | // text = JSON.stringify(["e", {pluribus: "unum"}], null, "\t"); 86 | // // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]' 87 | 88 | // text = JSON.stringify([new Date()], function (key, value) { 89 | // return this[key] instanceof Date 90 | // ? "Date(" + this[key] + ")" 91 | // : value; 92 | // }); 93 | // // text is '["Date(---current time---)"]' 94 | 95 | // JSON.parse(text, reviver) 96 | // This method parses a JSON text to produce an object or array. 97 | // It can throw a SyntaxError exception. 98 | 99 | // The optional reviver parameter is a function that can filter and 100 | // transform the results. It receives each of the keys and values, 101 | // and its return value is used instead of the original value. 102 | // If it returns what it received, then the structure is not modified. 103 | // If it returns undefined then the member is deleted. 104 | 105 | // Example: 106 | 107 | // // Parse the text. Values that look like ISO date strings will 108 | // // be converted to Date objects. 109 | 110 | // myData = JSON.parse(text, function (key, value) { 111 | // var a; 112 | // if (typeof value === "string") { 113 | // a = 114 | // /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); 115 | // if (a) { 116 | // return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], 117 | // +a[5], +a[6])); 118 | // } 119 | // } 120 | // return value; 121 | // }); 122 | 123 | // myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) { 124 | // var d; 125 | // if (typeof value === "string" && 126 | // value.slice(0, 5) === "Date(" && 127 | // value.slice(-1) === ")") { 128 | // d = new Date(value.slice(5, -1)); 129 | // if (d) { 130 | // return d; 131 | // } 132 | // } 133 | // return value; 134 | // }); 135 | 136 | // This is a reference implementation. You are free to copy, modify, or 137 | // redistribute. 138 | 139 | /*jslint 140 | eval, for, this 141 | */ 142 | 143 | /*property 144 | JSON, apply, call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, 145 | getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, 146 | lastIndex, length, parse, prototype, push, replace, slice, stringify, 147 | test, toJSON, toString, valueOf 148 | */ 149 | 150 | 151 | // Create a JSON object only if one does not already exist. We create the 152 | // methods in a closure to avoid creating global variables. 153 | 154 | if (typeof JSON !== "object") { 155 | JSON = {}; 156 | } 157 | 158 | (function () { 159 | "use strict"; 160 | 161 | var rx_one = /^[\],:{}\s]*$/; 162 | var rx_two = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g; 163 | var rx_three = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g; 164 | var rx_four = /(?:^|:|,)(?:\s*\[)+/g; 165 | var rx_escapable = /[\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; 166 | var rx_dangerous = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; 167 | 168 | function f(n) { 169 | // Format integers to have at least two digits. 170 | return n < 10 171 | ? "0" + n 172 | : n; 173 | } 174 | 175 | function this_value() { 176 | return this.valueOf(); 177 | } 178 | 179 | if (typeof Date.prototype.toJSON !== "function") { 180 | 181 | Date.prototype.toJSON = function () { 182 | 183 | return isFinite(this.valueOf()) 184 | ? this.getUTCFullYear() + "-" + 185 | f(this.getUTCMonth() + 1) + "-" + 186 | f(this.getUTCDate()) + "T" + 187 | f(this.getUTCHours()) + ":" + 188 | f(this.getUTCMinutes()) + ":" + 189 | f(this.getUTCSeconds()) + "Z" 190 | : null; 191 | }; 192 | 193 | Boolean.prototype.toJSON = this_value; 194 | Number.prototype.toJSON = this_value; 195 | String.prototype.toJSON = this_value; 196 | } 197 | 198 | var gap; 199 | var indent; 200 | var meta; 201 | var rep; 202 | 203 | 204 | function quote(string) { 205 | 206 | // If the string contains no control characters, no quote characters, and no 207 | // backslash characters, then we can safely slap some quotes around it. 208 | // Otherwise we must also replace the offending characters with safe escape 209 | // sequences. 210 | 211 | rx_escapable.lastIndex = 0; 212 | return rx_escapable.test(string) 213 | ? "\"" + string.replace(rx_escapable, function (a) { 214 | var c = meta[a]; 215 | return typeof c === "string" 216 | ? c 217 | : "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4); 218 | }) + "\"" 219 | : "\"" + string + "\""; 220 | } 221 | 222 | 223 | function str(key, holder) { 224 | 225 | // Produce a string from holder[key]. 226 | 227 | var i; // The loop counter. 228 | var k; // The member key. 229 | var v; // The member value. 230 | var length; 231 | var mind = gap; 232 | var partial; 233 | var value = holder[key]; 234 | 235 | // If the value has a toJSON method, call it to obtain a replacement value. 236 | 237 | if (value && typeof value === "object" && 238 | typeof value.toJSON === "function") { 239 | value = value.toJSON(key); 240 | } 241 | 242 | // If we were called with a replacer function, then call the replacer to 243 | // obtain a replacement value. 244 | 245 | if (typeof rep === "function") { 246 | value = rep.call(holder, key, value); 247 | } 248 | 249 | // What happens next depends on the value's type. 250 | 251 | switch (typeof value) { 252 | case "string": 253 | return quote(value); 254 | 255 | case "number": 256 | 257 | // JSON numbers must be finite. Encode non-finite numbers as null. 258 | 259 | return isFinite(value) 260 | ? String(value) 261 | : "null"; 262 | 263 | case "boolean": 264 | case "null": 265 | 266 | // If the value is a boolean or null, convert it to a string. Note: 267 | // typeof null does not produce "null". The case is included here in 268 | // the remote chance that this gets fixed someday. 269 | 270 | return String(value); 271 | 272 | // If the type is "object", we might be dealing with an object or an array or 273 | // null. 274 | 275 | case "object": 276 | 277 | // Due to a specification blunder in ECMAScript, typeof null is "object", 278 | // so watch out for that case. 279 | 280 | if (!value) { 281 | return "null"; 282 | } 283 | 284 | // Make an array to hold the partial results of stringifying this object value. 285 | 286 | gap += indent; 287 | partial = []; 288 | 289 | // Is the value an array? 290 | 291 | if (Object.prototype.toString.apply(value) === "[object Array]") { 292 | 293 | // The value is an array. Stringify every element. Use null as a placeholder 294 | // for non-JSON values. 295 | 296 | length = value.length; 297 | for (i = 0; i < length; i += 1) { 298 | partial[i] = str(i, value) || "null"; 299 | } 300 | 301 | // Join all of the elements together, separated with commas, and wrap them in 302 | // brackets. 303 | 304 | v = partial.length === 0 305 | ? "[]" 306 | : gap 307 | ? "[\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "]" 308 | : "[" + partial.join(",") + "]"; 309 | gap = mind; 310 | return v; 311 | } 312 | 313 | // If the replacer is an array, use it to select the members to be stringified. 314 | 315 | if (rep && typeof rep === "object") { 316 | length = rep.length; 317 | for (i = 0; i < length; i += 1) { 318 | if (typeof rep[i] === "string") { 319 | k = rep[i]; 320 | v = str(k, value); 321 | if (v) { 322 | partial.push(quote(k) + ( 323 | gap 324 | ? ": " 325 | : ":" 326 | ) + v); 327 | } 328 | } 329 | } 330 | } else { 331 | 332 | // Otherwise, iterate through all of the keys in the object. 333 | 334 | for (k in value) { 335 | if (Object.prototype.hasOwnProperty.call(value, k)) { 336 | v = str(k, value); 337 | if (v) { 338 | partial.push(quote(k) + ( 339 | gap 340 | ? ": " 341 | : ":" 342 | ) + v); 343 | } 344 | } 345 | } 346 | } 347 | 348 | // Join all of the member texts together, separated with commas, 349 | // and wrap them in braces. 350 | 351 | v = partial.length === 0 352 | ? "{}" 353 | : gap 354 | ? "{\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "}" 355 | : "{" + partial.join(",") + "}"; 356 | gap = mind; 357 | return v; 358 | } 359 | } 360 | 361 | // If the JSON object does not yet have a stringify method, give it one. 362 | 363 | if (typeof JSON.stringify !== "function") { 364 | meta = { // table of character substitutions 365 | "\b": "\\b", 366 | "\t": "\\t", 367 | "\n": "\\n", 368 | "\f": "\\f", 369 | "\r": "\\r", 370 | "\"": "\\\"", 371 | "\\": "\\\\" 372 | }; 373 | JSON.stringify = function (value, replacer, space) { 374 | 375 | // The stringify method takes a value and an optional replacer, and an optional 376 | // space parameter, and returns a JSON text. The replacer can be a function 377 | // that can replace values, or an array of strings that will select the keys. 378 | // A default replacer method can be provided. Use of the space parameter can 379 | // produce text that is more easily readable. 380 | 381 | var i; 382 | gap = ""; 383 | indent = ""; 384 | 385 | // If the space parameter is a number, make an indent string containing that 386 | // many spaces. 387 | 388 | if (typeof space === "number") { 389 | for (i = 0; i < space; i += 1) { 390 | indent += " "; 391 | } 392 | 393 | // If the space parameter is a string, it will be used as the indent string. 394 | 395 | } else if (typeof space === "string") { 396 | indent = space; 397 | } 398 | 399 | // If there is a replacer, it must be a function or an array. 400 | // Otherwise, throw an error. 401 | 402 | rep = replacer; 403 | if (replacer && typeof replacer !== "function" && 404 | (typeof replacer !== "object" || 405 | typeof replacer.length !== "number")) { 406 | throw new Error("JSON.stringify"); 407 | } 408 | 409 | // Make a fake root object containing our value under the key of "". 410 | // Return the result of stringifying the value. 411 | 412 | return str("", {"": value}); 413 | }; 414 | } 415 | 416 | 417 | // If the JSON object does not yet have a parse method, give it one. 418 | 419 | if (typeof JSON.parse !== "function") { 420 | JSON.parse = function (text, reviver) { 421 | 422 | // The parse method takes a text and an optional reviver function, and returns 423 | // a JavaScript value if the text is a valid JSON text. 424 | 425 | var j; 426 | 427 | function walk(holder, key) { 428 | 429 | // The walk method is used to recursively walk the resulting structure so 430 | // that modifications can be made. 431 | 432 | var k; 433 | var v; 434 | var value = holder[key]; 435 | if (value && typeof value === "object") { 436 | for (k in value) { 437 | if (Object.prototype.hasOwnProperty.call(value, k)) { 438 | v = walk(value, k); 439 | if (v !== undefined) { 440 | value[k] = v; 441 | } else { 442 | delete value[k]; 443 | } 444 | } 445 | } 446 | } 447 | return reviver.call(holder, key, value); 448 | } 449 | 450 | 451 | // Parsing happens in four stages. In the first stage, we replace certain 452 | // Unicode characters with escape sequences. JavaScript handles many characters 453 | // incorrectly, either silently deleting them, or treating them as line endings. 454 | 455 | text = String(text); 456 | rx_dangerous.lastIndex = 0; 457 | if (rx_dangerous.test(text)) { 458 | text = text.replace(rx_dangerous, function (a) { 459 | return "\\u" + 460 | ("0000" + a.charCodeAt(0).toString(16)).slice(-4); 461 | }); 462 | } 463 | 464 | // In the second stage, we run the text against regular expressions that look 465 | // for non-JSON patterns. We are especially concerned with "()" and "new" 466 | // because they can cause invocation, and "=" because it can cause mutation. 467 | // But just to be safe, we want to reject all unexpected forms. 468 | 469 | // We split the second stage into 4 regexp operations in order to work around 470 | // crippling inefficiencies in IE's and Safari's regexp engines. First we 471 | // replace the JSON backslash pairs with "@" (a non-JSON character). Second, we 472 | // replace all simple value tokens with "]" characters. Third, we delete all 473 | // open brackets that follow a colon or comma or that begin the text. Finally, 474 | // we look to see that the remaining characters are only whitespace or "]" or 475 | // "," or ":" or "{" or "}". If that is so, then the text is safe for eval. 476 | 477 | if ( 478 | rx_one.test( 479 | text 480 | .replace(rx_two, "@") 481 | .replace(rx_three, "]") 482 | .replace(rx_four, "") 483 | ) 484 | ) { 485 | 486 | // In the third stage we use the eval function to compile the text into a 487 | // JavaScript structure. The "{" operator is subject to a syntactic ambiguity 488 | // in JavaScript: it can begin a block or an object literal. We wrap the text 489 | // in parens to eliminate the ambiguity. 490 | 491 | j = eval("(" + text + ")"); 492 | 493 | // In the optional fourth stage, we recursively walk the new structure, passing 494 | // each name/value pair to a reviver function for possible transformation. 495 | 496 | return (typeof reviver === "function") 497 | ? walk({"": j}, "") 498 | : j; 499 | } 500 | 501 | // If the text is not JSON parseable, then a SyntaxError is thrown. 502 | 503 | throw new SyntaxError("JSON.parse"); 504 | }; 505 | } 506 | }()); -------------------------------------------------------------------------------- /src/jsx/opentimeline.jsx: -------------------------------------------------------------------------------- 1 | #include "json2.js"; 2 | 3 | if(typeof($) == 'undefined') 4 | $={}; 5 | 6 | $.OpenTimelineIOTools = { 7 | 8 | evalFile : function(path) { 9 | try { 10 | $.evalFile(path); 11 | } catch(err) { 12 | alert("Eval File Exception: " + err); 13 | } 14 | }, 15 | 16 | evalFiles: function(jsxFolderPath) { 17 | var folder = new Folder(jsxFolderPath); 18 | if (folder.exists) { 19 | var jsxFiles = folder.getFiles("*.jsx"); 20 | for (var i = 0; i < jsxFiles.length; i++) { 21 | var jsxFile = jsxFiles[i]; 22 | this.evalFile(jsxFile); 23 | } 24 | } 25 | }, 26 | 27 | exportActiveSequenceAsFCP7XML : function(path) { 28 | 29 | /** 30 | * Export the active sequence as a Final Cut Pro XML. 31 | * IMPORTANT: Has to be a local path, cannot be a server path. 32 | * You can move the file afterward, but Premiere will not export 33 | * this file to a server path. 34 | */ 35 | var seq = app.project.activeSequence; 36 | $.writeln('seq'); 37 | if (seq !== null) { 38 | // 1 says to suppress the UI 39 | seq.exportAsFinalCutProXML(path, 1); 40 | } else { 41 | alert('No active sequence'); 42 | } 43 | }, 44 | 45 | // importSequence : function(path) { 46 | // $.writeln('Sequence to import: ' + path); 47 | // if (path !== null) { 48 | // $.writeln('Here we go'); 49 | // try { 50 | // var result = app.project.importSequence(path); 51 | // $.writeln(result); 52 | // } catch(err) {alert('Error Importing: ' + err);} 53 | // } 54 | // }, 55 | 56 | importSequence : function(path) { 57 | // $.writeln('hello'); 58 | $.writeln('path'); 59 | }, 60 | 61 | chooseOTIOExportLocation : function() { 62 | var myFile = new File('~/Desktop').saveDlg('Export OpenTimelineIO file'); 63 | if (!myFile) { 64 | return; 65 | } else { 66 | return myFile.fsName; 67 | } 68 | }, 69 | 70 | selectOTIOFileToImport : function() { 71 | var myFile = new File('~/Desktop').openDlg('Select OpenTimelineIO file to import'); 72 | if (!myFile) { 73 | return; 74 | } else { 75 | return myFile.fsName; 76 | } 77 | }, 78 | }; 79 | -------------------------------------------------------------------------------- /src/lib/CSInterface.js: -------------------------------------------------------------------------------- 1 | /************************************************************************************************** 2 | * 3 | * ADOBE SYSTEMS INCORPORATED 4 | * Copyright 2013 Adobe Systems Incorporated 5 | * All Rights Reserved. 6 | * 7 | * NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the 8 | * terms of the Adobe license agreement accompanying it. If you have received this file from a 9 | * source other than Adobe, then your use, modification, or distribution of it requires the prior 10 | * written permission of Adobe. 11 | * 12 | **************************************************************************************************/ 13 | 14 | /** CSInterface - v6.1.0 */ 15 | 16 | /** 17 | * Stores constants for the window types supported by the CSXS infrastructure. 18 | */ 19 | function CSXSWindowType() 20 | { 21 | } 22 | 23 | /** Constant for the CSXS window type Panel. */ 24 | CSXSWindowType._PANEL = "Panel"; 25 | 26 | /** Constant for the CSXS window type Modeless. */ 27 | CSXSWindowType._MODELESS = "Modeless"; 28 | 29 | /** Constant for the CSXS window type ModalDialog. */ 30 | CSXSWindowType._MODAL_DIALOG = "ModalDialog"; 31 | 32 | /** EvalScript error message */ 33 | EvalScript_ErrMessage = "EvalScript error."; 34 | 35 | /** 36 | * @class Version 37 | * Defines a version number with major, minor, micro, and special 38 | * components. The major, minor and micro values are numeric; the special 39 | * value can be any string. 40 | * 41 | * @param major The major version component, a positive integer up to nine digits long. 42 | * @param minor The minor version component, a positive integer up to nine digits long. 43 | * @param micro The micro version component, a positive integer up to nine digits long. 44 | * @param special The special version component, an arbitrary string. 45 | * 46 | * @return A new \c Version object. 47 | */ 48 | function Version(major, minor, micro, special) 49 | { 50 | this.major = major; 51 | this.minor = minor; 52 | this.micro = micro; 53 | this.special = special; 54 | } 55 | 56 | /** 57 | * The maximum value allowed for a numeric version component. 58 | * This reflects the maximum value allowed in PlugPlug and the manifest schema. 59 | */ 60 | Version.MAX_NUM = 999999999; 61 | 62 | /** 63 | * @class VersionBound 64 | * Defines a boundary for a version range, which associates a \c Version object 65 | * with a flag for whether it is an inclusive or exclusive boundary. 66 | * 67 | * @param version The \c #Version object. 68 | * @param inclusive True if this boundary is inclusive, false if it is exclusive. 69 | * 70 | * @return A new \c VersionBound object. 71 | */ 72 | function VersionBound(version, inclusive) 73 | { 74 | this.version = version; 75 | this.inclusive = inclusive; 76 | } 77 | 78 | /** 79 | * @class VersionRange 80 | * Defines a range of versions using a lower boundary and optional upper boundary. 81 | * 82 | * @param lowerBound The \c #VersionBound object. 83 | * @param upperBound The \c #VersionBound object, or null for a range with no upper boundary. 84 | * 85 | * @return A new \c VersionRange object. 86 | */ 87 | function VersionRange(lowerBound, upperBound) 88 | { 89 | this.lowerBound = lowerBound; 90 | this.upperBound = upperBound; 91 | } 92 | 93 | /** 94 | * @class Runtime 95 | * Represents a runtime related to the CEP infrastructure. 96 | * Extensions can declare dependencies on particular 97 | * CEP runtime versions in the extension manifest. 98 | * 99 | * @param name The runtime name. 100 | * @param version A \c #VersionRange object that defines a range of valid versions. 101 | * 102 | * @return A new \c Runtime object. 103 | */ 104 | function Runtime(name, versionRange) 105 | { 106 | this.name = name; 107 | this.versionRange = versionRange; 108 | } 109 | 110 | /** 111 | * @class Extension 112 | * Encapsulates a CEP-based extension to an Adobe application. 113 | * 114 | * @param id The unique identifier of this extension. 115 | * @param name The localizable display name of this extension. 116 | * @param mainPath The path of the "index.html" file. 117 | * @param basePath The base path of this extension. 118 | * @param windowType The window type of the main window of this extension. 119 | Valid values are defined by \c #CSXSWindowType. 120 | * @param width The default width in pixels of the main window of this extension. 121 | * @param height The default height in pixels of the main window of this extension. 122 | * @param minWidth The minimum width in pixels of the main window of this extension. 123 | * @param minHeight The minimum height in pixels of the main window of this extension. 124 | * @param maxWidth The maximum width in pixels of the main window of this extension. 125 | * @param maxHeight The maximum height in pixels of the main window of this extension. 126 | * @param defaultExtensionDataXml The extension data contained in the default \c ExtensionDispatchInfo section of the extension manifest. 127 | * @param specialExtensionDataXml The extension data contained in the application-specific \c ExtensionDispatchInfo section of the extension manifest. 128 | * @param requiredRuntimeList An array of \c Runtime objects for runtimes required by this extension. 129 | * @param isAutoVisible True if this extension is visible on loading. 130 | * @param isPluginExtension True if this extension has been deployed in the Plugins folder of the host application. 131 | * 132 | * @return A new \c Extension object. 133 | */ 134 | function Extension(id, name, mainPath, basePath, windowType, width, height, minWidth, minHeight, maxWidth, maxHeight, 135 | defaultExtensionDataXml, specialExtensionDataXml, requiredRuntimeList, isAutoVisible, isPluginExtension) 136 | { 137 | this.id = id; 138 | this.name = name; 139 | this.mainPath = mainPath; 140 | this.basePath = basePath; 141 | this.windowType = windowType; 142 | this.width = width; 143 | this.height = height; 144 | this.minWidth = minWidth; 145 | this.minHeight = minHeight; 146 | this.maxWidth = maxWidth; 147 | this.maxHeight = maxHeight; 148 | this.defaultExtensionDataXml = defaultExtensionDataXml; 149 | this.specialExtensionDataXml = specialExtensionDataXml; 150 | this.requiredRuntimeList = requiredRuntimeList; 151 | this.isAutoVisible = isAutoVisible; 152 | this.isPluginExtension = isPluginExtension; 153 | } 154 | 155 | /** 156 | * @class CSEvent 157 | * A standard JavaScript event, the base class for CEP events. 158 | * 159 | * @param type The name of the event type. 160 | * @param scope The scope of event, can be "GLOBAL" or "APPLICATION". 161 | * @param appId The unique identifier of the application that generated the event. 162 | * @param extensionId The unique identifier of the extension that generated the event. 163 | * 164 | * @return A new \c CSEvent object 165 | */ 166 | function CSEvent(type, scope, appId, extensionId) 167 | { 168 | this.type = type; 169 | this.scope = scope; 170 | this.appId = appId; 171 | this.extensionId = extensionId; 172 | } 173 | 174 | /** Event-specific data. */ 175 | CSEvent.prototype.data = ""; 176 | 177 | /** 178 | * @class SystemPath 179 | * Stores operating-system-specific location constants for use in the 180 | * \c #CSInterface.getSystemPath() method. 181 | * @return A new \c SystemPath object. 182 | */ 183 | function SystemPath() 184 | { 185 | } 186 | 187 | /** The path to user data. */ 188 | SystemPath.USER_DATA = "userData"; 189 | 190 | /** The path to common files for Adobe applications. */ 191 | SystemPath.COMMON_FILES = "commonFiles"; 192 | 193 | /** The path to the user's default document folder. */ 194 | SystemPath.MY_DOCUMENTS = "myDocuments"; 195 | 196 | /** @deprecated. Use \c #SystemPath.Extension. */ 197 | SystemPath.APPLICATION = "application"; 198 | 199 | /** The path to current extension. */ 200 | SystemPath.EXTENSION = "extension"; 201 | 202 | /** The path to hosting application's executable. */ 203 | SystemPath.HOST_APPLICATION = "hostApplication"; 204 | 205 | /** 206 | * @class ColorType 207 | * Stores color-type constants. 208 | */ 209 | function ColorType() 210 | { 211 | } 212 | 213 | /** RGB color type. */ 214 | ColorType.RGB = "rgb"; 215 | 216 | /** Gradient color type. */ 217 | ColorType.GRADIENT = "gradient"; 218 | 219 | /** Null color type. */ 220 | ColorType.NONE = "none"; 221 | 222 | /** 223 | * @class RGBColor 224 | * Stores an RGB color with red, green, blue, and alpha values. 225 | * All values are in the range [0.0 to 255.0]. Invalid numeric values are 226 | * converted to numbers within this range. 227 | * 228 | * @param red The red value, in the range [0.0 to 255.0]. 229 | * @param green The green value, in the range [0.0 to 255.0]. 230 | * @param blue The blue value, in the range [0.0 to 255.0]. 231 | * @param alpha The alpha (transparency) value, in the range [0.0 to 255.0]. 232 | * The default, 255.0, means that the color is fully opaque. 233 | * 234 | * @return A new RGBColor object. 235 | */ 236 | function RGBColor(red, green, blue, alpha) 237 | { 238 | this.red = red; 239 | this.green = green; 240 | this.blue = blue; 241 | this.alpha = alpha; 242 | } 243 | 244 | /** 245 | * @class Direction 246 | * A point value in which the y component is 0 and the x component 247 | * is positive or negative for a right or left direction, 248 | * or the x component is 0 and the y component is positive or negative for 249 | * an up or down direction. 250 | * 251 | * @param x The horizontal component of the point. 252 | * @param y The vertical component of the point. 253 | * 254 | * @return A new \c Direction object. 255 | */ 256 | function Direction(x, y) 257 | { 258 | this.x = x; 259 | this.y = y; 260 | } 261 | 262 | /** 263 | * @class GradientStop 264 | * Stores gradient stop information. 265 | * 266 | * @param offset The offset of the gradient stop, in the range [0.0 to 1.0]. 267 | * @param rgbColor The color of the gradient at this point, an \c #RGBColor object. 268 | * 269 | * @return GradientStop object. 270 | */ 271 | function GradientStop(offset, rgbColor) 272 | { 273 | this.offset = offset; 274 | this.rgbColor = rgbColor; 275 | } 276 | 277 | /** 278 | * @class GradientColor 279 | * Stores gradient color information. 280 | * 281 | * @param type The gradient type, must be "linear". 282 | * @param direction A \c #Direction object for the direction of the gradient 283 | (up, down, right, or left). 284 | * @param numStops The number of stops in the gradient. 285 | * @param gradientStopList An array of \c #GradientStop objects. 286 | * 287 | * @return A new \c GradientColor object. 288 | */ 289 | function GradientColor(type, direction, numStops, arrGradientStop) 290 | { 291 | this.type = type; 292 | this.direction = direction; 293 | this.numStops = numStops; 294 | this.arrGradientStop = arrGradientStop; 295 | } 296 | 297 | /** 298 | * @class UIColor 299 | * Stores color information, including the type, anti-alias level, and specific color 300 | * values in a color object of an appropriate type. 301 | * 302 | * @param type The color type, 1 for "rgb" and 2 for "gradient". 303 | The supplied color object must correspond to this type. 304 | * @param antialiasLevel The anti-alias level constant. 305 | * @param color A \c #RGBColor or \c #GradientColor object containing specific color information. 306 | * 307 | * @return A new \c UIColor object. 308 | */ 309 | function UIColor(type, antialiasLevel, color) 310 | { 311 | this.type = type; 312 | this.antialiasLevel = antialiasLevel; 313 | this.color = color; 314 | } 315 | 316 | /** 317 | * @class AppSkinInfo 318 | * Stores window-skin properties, such as color and font. All color parameter values are \c #UIColor objects except that systemHighlightColor is \c #RGBColor object. 319 | * 320 | * @param baseFontFamily The base font family of the application. 321 | * @param baseFontSize The base font size of the application. 322 | * @param appBarBackgroundColor The application bar background color. 323 | * @param panelBackgroundColor The background color of the extension panel. 324 | * @param appBarBackgroundColorSRGB The application bar background color, as sRGB. 325 | * @param panelBackgroundColorSRGB The background color of the extension panel, as sRGB. 326 | * @param systemHighlightColor The operating-system highlight color, as sRGB. 327 | * 328 | * @return AppSkinInfo object. 329 | */ 330 | function AppSkinInfo(baseFontFamily, baseFontSize, appBarBackgroundColor, panelBackgroundColor, appBarBackgroundColorSRGB, panelBackgroundColorSRGB, systemHighlightColor) 331 | { 332 | this.baseFontFamily = baseFontFamily; 333 | this.baseFontSize = baseFontSize; 334 | this.appBarBackgroundColor = appBarBackgroundColor; 335 | this.panelBackgroundColor = panelBackgroundColor; 336 | this.appBarBackgroundColorSRGB = appBarBackgroundColorSRGB; 337 | this.panelBackgroundColorSRGB = panelBackgroundColorSRGB; 338 | this.systemHighlightColor = systemHighlightColor; 339 | } 340 | 341 | /** 342 | * @class HostEnvironment 343 | * Stores information about the environment in which the extension is loaded. 344 | * 345 | * @param appName The application's name. 346 | * @param appVersion The application's version. 347 | * @param appLocale The application's current license locale. 348 | * @param appUILocale The application's current UI locale. 349 | * @param appId The application's unique identifier. 350 | * @param isAppOnline True if the application is currently online. 351 | * @param appSkinInfo An \c #AppSkinInfo object containing the application's default color and font styles. 352 | * 353 | * @return A new \c HostEnvironment object. 354 | */ 355 | function HostEnvironment(appName, appVersion, appLocale, appUILocale, appId, isAppOnline, appSkinInfo) 356 | { 357 | this.appName = appName; 358 | this.appVersion = appVersion; 359 | this.appLocale = appLocale; 360 | this.appUILocale = appUILocale; 361 | this.appId = appId; 362 | this.isAppOnline = isAppOnline; 363 | this.appSkinInfo = appSkinInfo; 364 | } 365 | 366 | /** 367 | * @class HostCapabilities 368 | * Stores information about the host capabilities. 369 | * 370 | * @param EXTENDED_PANEL_MENU True if the application supports panel menu. 371 | * @param EXTENDED_PANEL_ICONS True if the application supports panel icon. 372 | * @param DELEGATE_APE_ENGINE True if the application supports delegated APE engine. 373 | * @param SUPPORT_HTML_EXTENSIONS True if the application supports HTML extensions. 374 | * @param DISABLE_FLASH_EXTENSIONS True if the application disables FLASH extensions. 375 | * 376 | * @return A new \c HostCapabilities object. 377 | */ 378 | function HostCapabilities(EXTENDED_PANEL_MENU, EXTENDED_PANEL_ICONS, DELEGATE_APE_ENGINE, SUPPORT_HTML_EXTENSIONS, DISABLE_FLASH_EXTENSIONS) 379 | { 380 | this.EXTENDED_PANEL_MENU = EXTENDED_PANEL_MENU; 381 | this.EXTENDED_PANEL_ICONS = EXTENDED_PANEL_ICONS; 382 | this.DELEGATE_APE_ENGINE = DELEGATE_APE_ENGINE; 383 | this.SUPPORT_HTML_EXTENSIONS = SUPPORT_HTML_EXTENSIONS; 384 | this.DISABLE_FLASH_EXTENSIONS = DISABLE_FLASH_EXTENSIONS; // Since 5.0.0 385 | } 386 | 387 | /** 388 | * @class ApiVersion 389 | * Stores current api version. 390 | * 391 | * Since 4.2.0 392 | * 393 | * @param major The major version 394 | * @param minor The minor version. 395 | * @param micro The micro version. 396 | * 397 | * @return ApiVersion object. 398 | */ 399 | function ApiVersion(major, minor, micro) 400 | { 401 | this.major = major; 402 | this.minor = minor; 403 | this.micro = micro; 404 | } 405 | 406 | /** 407 | * @class MenuItemStatus 408 | * Stores flyout menu item status 409 | * 410 | * Since 5.2.0 411 | * 412 | * @param menuItemLabel The menu item label. 413 | * @param enabled True if user wants to enable the menu item. 414 | * @param checked True if user wants to check the menu item. 415 | * 416 | * @return MenuItemStatus object. 417 | */ 418 | function MenuItemStatus(menuItemLabel, enabled, checked) 419 | { 420 | this.menuItemLabel = menuItemLabel; 421 | this.enabled = enabled; 422 | this.checked = checked; 423 | } 424 | 425 | /** 426 | * @class ContextMenuItemStatus 427 | * Stores the status of the context menu item. 428 | * 429 | * Since 5.2.0 430 | * 431 | * @param menuItemID The menu item id. 432 | * @param enabled True if user wants to enable the menu item. 433 | * @param checked True if user wants to check the menu item. 434 | * 435 | * @return MenuItemStatus object. 436 | */ 437 | function ContextMenuItemStatus(menuItemID, enabled, checked) 438 | { 439 | this.menuItemID = menuItemID; 440 | this.enabled = enabled; 441 | this.checked = checked; 442 | } 443 | //------------------------------ CSInterface ---------------------------------- 444 | 445 | /** 446 | * @class CSInterface 447 | * This is the entry point to the CEP extensibility infrastructure. 448 | * Instantiate this object and use it to: 449 | * 454 | * 455 | * @return A new \c CSInterface object 456 | */ 457 | function CSInterface() 458 | { 459 | } 460 | 461 | /** 462 | * User can add this event listener to handle native application theme color changes. 463 | * Callback function gives extensions ability to fine-tune their theme color after the 464 | * global theme color has been changed. 465 | * The callback function should be like below: 466 | * 467 | * @example 468 | * // event is a CSEvent object, but user can ignore it. 469 | * function OnAppThemeColorChanged(event) 470 | * { 471 | * // Should get a latest HostEnvironment object from application. 472 | * var skinInfo = JSON.parse(window.__adobe_cep__.getHostEnvironment()).appSkinInfo; 473 | * // Gets the style information such as color info from the skinInfo, 474 | * // and redraw all UI controls of your extension according to the style info. 475 | * } 476 | */ 477 | CSInterface.THEME_COLOR_CHANGED_EVENT = "com.adobe.csxs.events.ThemeColorChanged"; 478 | 479 | /** The host environment data object. */ 480 | CSInterface.prototype.hostEnvironment = JSON.parse(window.__adobe_cep__.getHostEnvironment()); 481 | 482 | /** Retrieves information about the host environment in which the 483 | * extension is currently running. 484 | * 485 | * @return A \c #HostEnvironment object. 486 | */ 487 | CSInterface.prototype.getHostEnvironment = function() 488 | { 489 | this.hostEnvironment = JSON.parse(window.__adobe_cep__.getHostEnvironment()); 490 | return this.hostEnvironment; 491 | }; 492 | 493 | /** Closes this extension. */ 494 | CSInterface.prototype.closeExtension = function() 495 | { 496 | window.__adobe_cep__.closeExtension(); 497 | }; 498 | 499 | /** 500 | * Retrieves a path for which a constant is defined in the system. 501 | * 502 | * @param pathType The path-type constant defined in \c #SystemPath , 503 | * 504 | * @return The platform-specific system path string. 505 | */ 506 | CSInterface.prototype.getSystemPath = function(pathType) 507 | { 508 | var path = decodeURI(window.__adobe_cep__.getSystemPath(pathType)); 509 | var OSVersion = this.getOSInformation(); 510 | if (OSVersion.indexOf("Windows") >= 0) 511 | { 512 | path = path.replace("file:///", ""); 513 | } 514 | else if (OSVersion.indexOf("Mac") >= 0) 515 | { 516 | path = path.replace("file://", ""); 517 | } 518 | return path; 519 | }; 520 | 521 | /** 522 | * Evaluates a JavaScript script, which can use the JavaScript DOM 523 | * of the host application. 524 | * 525 | * @param script The JavaScript script. 526 | * @param callback Optional. A callback function that receives the result of execution. 527 | * If execution fails, the callback function receives the error message \c EvalScript_ErrMessage. 528 | */ 529 | CSInterface.prototype.evalScript = function(script, callback) 530 | { 531 | if(callback === null || callback === undefined) 532 | { 533 | callback = function(result){}; 534 | } 535 | window.__adobe_cep__.evalScript(script, callback); 536 | }; 537 | 538 | /** 539 | * Retrieves the unique identifier of the application. 540 | * in which the extension is currently running. 541 | * 542 | * @return The unique ID string. 543 | */ 544 | CSInterface.prototype.getApplicationID = function() 545 | { 546 | var appId = this.hostEnvironment.appId; 547 | return appId; 548 | }; 549 | 550 | /** 551 | * Retrieves host capability information for the application 552 | * in which the extension is currently running. 553 | * 554 | * @return A \c #HostCapabilities object. 555 | */ 556 | CSInterface.prototype.getHostCapabilities = function() 557 | { 558 | var hostCapabilities = JSON.parse(window.__adobe_cep__.getHostCapabilities() ); 559 | return hostCapabilities; 560 | }; 561 | 562 | /** 563 | * Triggers a CEP event programmatically. Yoy can use it to dispatch 564 | * an event of a predefined type, or of a type you have defined. 565 | * 566 | * @param event A \c CSEvent object. 567 | */ 568 | CSInterface.prototype.dispatchEvent = function(event) 569 | { 570 | if (typeof event.data == "object") 571 | { 572 | event.data = JSON.stringify(event.data); 573 | } 574 | 575 | window.__adobe_cep__.dispatchEvent(event); 576 | }; 577 | 578 | /** 579 | * Registers an interest in a CEP event of a particular type, and 580 | * assigns an event handler. 581 | * The event infrastructure notifies your extension when events of this type occur, 582 | * passing the event object to the registered handler function. 583 | * 584 | * @param type The name of the event type of interest. 585 | * @param listener The JavaScript handler function or method. 586 | * @param obj Optional, the object containing the handler method, if any. 587 | * Default is null. 588 | */ 589 | CSInterface.prototype.addEventListener = function(type, listener, obj) 590 | { 591 | window.__adobe_cep__.addEventListener(type, listener, obj); 592 | }; 593 | 594 | /** 595 | * Removes a registered event listener. 596 | * 597 | * @param type The name of the event type of interest. 598 | * @param listener The JavaScript handler function or method that was registered. 599 | * @param obj Optional, the object containing the handler method, if any. 600 | * Default is null. 601 | */ 602 | CSInterface.prototype.removeEventListener = function(type, listener, obj) 603 | { 604 | window.__adobe_cep__.removeEventListener(type, listener, obj); 605 | }; 606 | 607 | /** 608 | * Loads and launches another extension, or activates the extension if it is already loaded. 609 | * 610 | * @param extensionId The extension's unique identifier. 611 | * @param startupParams Not currently used, pass "". 612 | * 613 | * @example 614 | * To launch the extension "help" with ID "HLP" from this extension, call: 615 | * requestOpenExtension("HLP", ""); 616 | * 617 | */ 618 | CSInterface.prototype.requestOpenExtension = function(extensionId, params) 619 | { 620 | window.__adobe_cep__.requestOpenExtension(extensionId, params); 621 | }; 622 | 623 | /** 624 | * Retrieves the list of extensions currently loaded in the current host application. 625 | * The extension list is initialized once, and remains the same during the lifetime 626 | * of the CEP session. 627 | * 628 | * @param extensionIds Optional, an array of unique identifiers for extensions of interest. 629 | * If omitted, retrieves data for all extensions. 630 | * 631 | * @return Zero or more \c #Extension objects. 632 | */ 633 | CSInterface.prototype.getExtensions = function(extensionIds) 634 | { 635 | var extensionIdsStr = JSON.stringify(extensionIds); 636 | var extensionsStr = window.__adobe_cep__.getExtensions(extensionIdsStr); 637 | 638 | var extensions = JSON.parse(extensionsStr); 639 | return extensions; 640 | }; 641 | 642 | /** 643 | * Retrieves network-related preferences. 644 | * 645 | * @return A JavaScript object containing network preferences. 646 | */ 647 | CSInterface.prototype.getNetworkPreferences = function() 648 | { 649 | var result = window.__adobe_cep__.getNetworkPreferences(); 650 | var networkPre = JSON.parse(result); 651 | 652 | return networkPre; 653 | }; 654 | 655 | /** 656 | * Initializes the resource bundle for this extension with property values 657 | * for the current application and locale. 658 | * To support multiple locales, you must define a property file for each locale, 659 | * containing keyed display-string values for that locale. 660 | * See localization documentation for Extension Builder and related products. 661 | * 662 | * Keys can be in the 663 | * form key.value="localized string", for use in HTML text elements. 664 | * For example, in this input element, the localized \c key.value string is displayed 665 | * instead of the empty \c value string: 666 | * 667 | * 668 | * 669 | * @return An object containing the resource bundle information. 670 | */ 671 | CSInterface.prototype.initResourceBundle = function() 672 | { 673 | var resourceBundle = JSON.parse(window.__adobe_cep__.initResourceBundle()); 674 | var resElms = document.querySelectorAll('[data-locale]'); 675 | for (var n = 0; n < resElms.length; n++) 676 | { 677 | var resEl = resElms[n]; 678 | // Get the resource key from the element. 679 | var resKey = resEl.getAttribute('data-locale'); 680 | if (resKey) 681 | { 682 | // Get all the resources that start with the key. 683 | for (var key in resourceBundle) 684 | { 685 | if (key.indexOf(resKey) === 0) 686 | { 687 | var resValue = resourceBundle[key]; 688 | if (key.length == resKey.length) 689 | { 690 | resEl.innerHTML = resValue; 691 | } 692 | else if ('.' == key.charAt(resKey.length)) 693 | { 694 | var attrKey = key.substring(resKey.length + 1); 695 | resEl[attrKey] = resValue; 696 | } 697 | } 698 | } 699 | } 700 | } 701 | return resourceBundle; 702 | }; 703 | 704 | /** 705 | * Writes installation information to a file. 706 | * 707 | * @return The file path. 708 | */ 709 | CSInterface.prototype.dumpInstallationInfo = function() 710 | { 711 | return window.__adobe_cep__.dumpInstallationInfo(); 712 | }; 713 | 714 | /** 715 | * Retrieves version information for the current Operating System, 716 | * See http://www.useragentstring.com/pages/Chrome/ for Chrome \c navigator.userAgent values. 717 | * 718 | * @return A string containing the OS version, or "unknown Operation System". 719 | * If user customizes the User Agent by setting CEF command parameter "--user-agent", only 720 | * "Mac OS X" or "Windows" will be returned. 721 | */ 722 | CSInterface.prototype.getOSInformation = function() 723 | { 724 | var userAgent = navigator.userAgent; 725 | 726 | if ((navigator.platform == "Win32") || (navigator.platform == "Windows")) 727 | { 728 | var winVersion = "Windows"; 729 | var winBit = ""; 730 | if (userAgent.indexOf("Windows") > -1) 731 | { 732 | if (userAgent.indexOf("Windows NT 5.0") > -1) 733 | { 734 | winVersion = "Windows 2000"; 735 | } 736 | else if (userAgent.indexOf("Windows NT 5.1") > -1) 737 | { 738 | winVersion = "Windows XP"; 739 | } 740 | else if (userAgent.indexOf("Windows NT 5.2") > -1) 741 | { 742 | winVersion = "Windows Server 2003"; 743 | } 744 | else if (userAgent.indexOf("Windows NT 6.0") > -1) 745 | { 746 | winVersion = "Windows Vista"; 747 | } 748 | else if (userAgent.indexOf("Windows NT 6.1") > -1) 749 | { 750 | winVersion = "Windows 7"; 751 | } 752 | else if (userAgent.indexOf("Windows NT 6.2") > -1) 753 | { 754 | winVersion = "Windows 8"; 755 | } 756 | else if (userAgent.indexOf("Windows NT 6.3") > -1) 757 | { 758 | winVersion = "Windows 8.1"; 759 | } 760 | else if (userAgent.indexOf("Windows NT 10") > -1) 761 | { 762 | winVersion = "Windows 10"; 763 | } 764 | 765 | if (userAgent.indexOf("WOW64") > -1) 766 | { 767 | winBit = " 64-bit"; 768 | } 769 | else 770 | { 771 | winBit = " 32-bit"; 772 | } 773 | } 774 | 775 | return winVersion + winBit; 776 | } 777 | else if ((navigator.platform == "MacIntel") || (navigator.platform == "Macintosh")) 778 | { 779 | var result = "Mac OS X"; 780 | 781 | if (userAgent.indexOf("Mac OS X") > -1) 782 | { 783 | result = userAgent.substring(userAgent.indexOf("Mac OS X"), userAgent.indexOf(")")); 784 | result = result.replace(/_/g, "."); 785 | } 786 | 787 | return result; 788 | } 789 | 790 | return "Unknown Operation System"; 791 | }; 792 | 793 | /** 794 | * Opens a page in the default system browser. 795 | * 796 | * Since 4.2.0 797 | * 798 | * @param url The URL of the page/file to open, or the email address. 799 | * Must use HTTP/HTTPS/file/mailto protocol. For example: 800 | * "http://www.adobe.com" 801 | * "https://github.com" 802 | * "file:///C:/log.txt" 803 | * "mailto:test@adobe.com" 804 | * 805 | * @return One of these error codes:\n 806 | * \n 812 | */ 813 | CSInterface.prototype.openURLInDefaultBrowser = function(url) 814 | { 815 | return cep.util.openURLInDefaultBrowser(url); 816 | }; 817 | 818 | /** 819 | * Retrieves extension ID. 820 | * 821 | * Since 4.2.0 822 | * 823 | * @return extension ID. 824 | */ 825 | CSInterface.prototype.getExtensionID = function() 826 | { 827 | return window.__adobe_cep__.getExtensionId(); 828 | }; 829 | 830 | /** 831 | * Retrieves the scale factor of screen. 832 | * On Windows platform, the value of scale factor might be different from operating system's scale factor, 833 | * since host application may use its self-defined scale factor. 834 | * 835 | * Since 4.2.0 836 | * 837 | * @return One of the following float number. 838 | * \n 843 | */ 844 | CSInterface.prototype.getScaleFactor = function() 845 | { 846 | return window.__adobe_cep__.getScaleFactor(); 847 | }; 848 | 849 | /** 850 | * Set a handler to detect any changes of scale factor. This only works on Mac. 851 | * 852 | * Since 4.2.0 853 | * 854 | * @param handler The function to be called when scale factor is changed. 855 | * 856 | */ 857 | CSInterface.prototype.setScaleFactorChangedHandler = function(handler) 858 | { 859 | window.__adobe_cep__.setScaleFactorChangedHandler(handler); 860 | }; 861 | 862 | /** 863 | * Retrieves current API version. 864 | * 865 | * Since 4.2.0 866 | * 867 | * @return ApiVersion object. 868 | * 869 | */ 870 | CSInterface.prototype.getCurrentApiVersion = function() 871 | { 872 | var apiVersion = JSON.parse(window.__adobe_cep__.getCurrentApiVersion()); 873 | return apiVersion; 874 | }; 875 | 876 | /** 877 | * Set panel flyout menu by an XML. 878 | * 879 | * Since 5.2.0 880 | * 881 | * Register a callback function for "com.adobe.csxs.events.flyoutMenuClicked" to get notified when a 882 | * menu item is clicked. 883 | * The "data" attribute of event is an object which contains "menuId" and "menuName" attributes. 884 | * 885 | * Register callback functions for "com.adobe.csxs.events.flyoutMenuOpened" and "com.adobe.csxs.events.flyoutMenuClosed" 886 | * respectively to get notified when flyout menu is opened or closed. 887 | * 888 | * @param menu A XML string which describes menu structure. 889 | * An example menu XML: 890 | * 891 | * 892 | * 893 | * 894 | * 895 | * 896 | * 897 | * 898 | * 899 | * 900 | * 901 | * 902 | */ 903 | CSInterface.prototype.setPanelFlyoutMenu = function(menu) 904 | { 905 | if ("string" != typeof menu) 906 | { 907 | return; 908 | } 909 | 910 | window.__adobe_cep__.invokeSync("setPanelFlyoutMenu", menu); 911 | }; 912 | 913 | /** 914 | * Updates a menu item in the extension window's flyout menu, by setting the enabled 915 | * and selection status. 916 | * 917 | * Since 5.2.0 918 | * 919 | * @param menuItemLabel The menu item label. 920 | * @param enabled True to enable the item, false to disable it (gray it out). 921 | * @param checked True to select the item, false to deselect it. 922 | * 923 | * @return false when the host application does not support this functionality (HostCapabilities.EXTENDED_PANEL_MENU is false). 924 | * Fails silently if menu label is invalid. 925 | * 926 | * @see HostCapabilities.EXTENDED_PANEL_MENU 927 | */ 928 | CSInterface.prototype.updatePanelMenuItem = function(menuItemLabel, enabled, checked) 929 | { 930 | var ret = false; 931 | if (this.getHostCapabilities().EXTENDED_PANEL_MENU) 932 | { 933 | var itemStatus = new MenuItemStatus(menuItemLabel, enabled, checked); 934 | ret = window.__adobe_cep__.invokeSync("updatePanelMenuItem", JSON.stringify(itemStatus)); 935 | } 936 | return ret; 937 | }; 938 | 939 | 940 | /** 941 | * Set context menu by XML string. 942 | * 943 | * Since 5.2.0 944 | * 945 | * There are a number of conventions used to communicate what type of menu item to create and how it should be handled. 946 | * - an item without menu ID or menu name is disabled and is not shown. 947 | * - if the item name is "---" (three hyphens) then it is treated as a separator. The menu ID in this case will always be NULL. 948 | * - Checkable attribute takes precedence over Checked attribute. 949 | * - a PNG icon. For optimal display results please supply a 16 x 16px icon as larger dimensions will increase the size of the menu item. 950 | The Chrome extension contextMenus API was taken as a reference. 951 | https://developer.chrome.com/extensions/contextMenus 952 | * - the items with icons and checkable items cannot coexist on the same menu level. The former take precedences over the latter. 953 | * 954 | * @param menu A XML string which describes menu structure. 955 | * @param callback The callback function which is called when a menu item is clicked. The only parameter is the returned ID of clicked menu item. 956 | * 957 | * @description An example menu XML: 958 | * 959 | * 960 | * 961 | * 962 | * 963 | * 964 | * 965 | * 966 | * 967 | * 968 | * 969 | */ 970 | CSInterface.prototype.setContextMenu = function(menu, callback) 971 | { 972 | if ("string" != typeof menu) 973 | { 974 | return; 975 | } 976 | 977 | window.__adobe_cep__.invokeAsync("setContextMenu", menu, callback); 978 | }; 979 | 980 | /** 981 | * Set context menu by JSON string. 982 | * 983 | * Since 6.0.0 984 | * 985 | * There are a number of conventions used to communicate what type of menu item to create and how it should be handled. 986 | * - an item without menu ID or menu name is disabled and is not shown. 987 | * - if the item label is "---" (three hyphens) then it is treated as a separator. The menu ID in this case will always be NULL. 988 | * - Checkable attribute takes precedence over Checked attribute. 989 | * - a PNG icon. For optimal display results please supply a 16 x 16px icon as larger dimensions will increase the size of the menu item. 990 | The Chrome extension contextMenus API was taken as a reference. 991 | * - the items with icons and checkable items cannot coexist on the same menu level. The former take precedences over the latter. 992 | https://developer.chrome.com/extensions/contextMenus 993 | * 994 | * @param menu A JSON string which describes menu structure. 995 | * @param callback The callback function which is called when a menu item is clicked. The only parameter is the returned ID of clicked menu item. 996 | * 997 | * @description An example menu JSON: 998 | * 999 | * { 1000 | * "menu": [ 1001 | * { 1002 | * "id": "menuItemId1", 1003 | * "label": "testExample1", 1004 | * "enabled": true, 1005 | * "checkable": true, 1006 | * "checked": false, 1007 | * "icon": "./image/small_16X16.png" 1008 | * }, 1009 | * { 1010 | * "id": "menuItemId2", 1011 | * "label": "testExample2", 1012 | * "menu": [ 1013 | * { 1014 | * "id": "menuItemId2-1", 1015 | * "label": "testExample2-1", 1016 | * "menu": [ 1017 | * { 1018 | * "id": "menuItemId2-1-1", 1019 | * "label": "testExample2-1-1", 1020 | * "enabled": false, 1021 | * "checkable": true, 1022 | * "checked": true 1023 | * } 1024 | * ] 1025 | * }, 1026 | * { 1027 | * "id": "menuItemId2-2", 1028 | * "label": "testExample2-2", 1029 | * "enabled": true, 1030 | * "checkable": true, 1031 | * "checked": true 1032 | * } 1033 | * ] 1034 | * }, 1035 | * { 1036 | * "label": "---" 1037 | * }, 1038 | * { 1039 | * "id": "menuItemId3", 1040 | * "label": "testExample3", 1041 | * "enabled": false, 1042 | * "checkable": true, 1043 | * "checked": false 1044 | * } 1045 | * ] 1046 | * } 1047 | * 1048 | */ 1049 | CSInterface.prototype.setContextMenuByJSON = function(menu, callback) 1050 | { 1051 | if ("string" != typeof menu) 1052 | { 1053 | return; 1054 | } 1055 | 1056 | window.__adobe_cep__.invokeAsync("setContextMenuByJSON", menu, callback); 1057 | }; 1058 | 1059 | /** 1060 | * Updates a context menu item by setting the enabled and selection status. 1061 | * 1062 | * Since 5.2.0 1063 | * 1064 | * @param menuItemID The menu item ID. 1065 | * @param enabled True to enable the item, false to disable it (gray it out). 1066 | * @param checked True to select the item, false to deselect it. 1067 | */ 1068 | CSInterface.prototype.updateContextMenuItem = function(menuItemID, enabled, checked) 1069 | { 1070 | var itemStatus = new ContextMenuItemStatus(menuItemID, enabled, checked); 1071 | ret = window.__adobe_cep__.invokeSync("updateContextMenuItem", JSON.stringify(itemStatus)); 1072 | }; 1073 | 1074 | /** 1075 | * Get the visibility status of an extension window. 1076 | * 1077 | * Since 6.0.0 1078 | * 1079 | * @return true if the extension window is visible; false if the extension window is hidden. 1080 | */ 1081 | CSInterface.prototype.isWindowVisible = function() 1082 | { 1083 | return window.__adobe_cep__.invokeSync("isWindowVisible", ""); 1084 | }; 1085 | 1086 | /** 1087 | * Resize extension's content to the specified dimensions. 1088 | * 1. Works with modal and modeless extensions in all Adobe products. 1089 | * 2. Extension's manifest min/max size constraints apply and take precedence. 1090 | * 3. For panel extensions 1091 | * 3.1 This works in all Adobe products except: 1092 | * * Premiere Pro 1093 | * * Prelude 1094 | * * After Effects 1095 | * 3.2 When the panel is in certain states (especially when being docked), 1096 | * it will not change to the desired dimensions even when the 1097 | * specified size satisfies min/max constraints. 1098 | * 1099 | * Since 6.0.0 1100 | * 1101 | * @param width The new width 1102 | * @param height The new height 1103 | */ 1104 | CSInterface.prototype.resizeContent = function(width, height) 1105 | { 1106 | window.__adobe_cep__.resizeContent(width, height); 1107 | }; 1108 | 1109 | /** 1110 | * Register the invalid certificate callback for an extension. 1111 | * This callback will be triggered when the extension tries to access the web site that contains the invalid certificate on the main frame. 1112 | * But if the extension does not call this function and tries to access the web site containing the invalid certificate, a default error page will be shown. 1113 | * 1114 | * Since 6.1.0 1115 | * 1116 | * @param callback the callback function 1117 | */ 1118 | CSInterface.prototype.registerInvalidCertificateCallback = function(callback) 1119 | { 1120 | return window.__adobe_cep__.registerInvalidCertificateCallback(callback); 1121 | }; 1122 | 1123 | /** 1124 | * Register an interest in some key events to prevent them from being sent to the host application. 1125 | * 1126 | * This function works with modeless extensions and panel extensions. 1127 | * Generally all the key events will be sent to the host application for these two extensions if the current focused element 1128 | * is not text input or dropdown, 1129 | * If you want to intercept some key events and want them to be handled in the extension, please call this function 1130 | * in advance to prevent them being sent to the host application. 1131 | * 1132 | * Since 6.1.0 1133 | * 1134 | * @param keyEventsInterest A JSON string describing those key events you are interested in. A null object or 1135 | an empty string will lead to removing the interest 1136 | * 1137 | * This JSON string should be an array, each object has following keys: 1138 | * 1139 | * keyCode: [Required] represents an OS system dependent virtual key code identifying 1140 | * the unmodified value of the pressed key. 1141 | * ctrlKey: [optional] a Boolean that indicates if the control key was pressed (true) or not (false) when the event occurred. 1142 | * altKey: [optional] a Boolean that indicates if the alt key was pressed (true) or not (false) when the event occurred. 1143 | * shiftKey: [optional] a Boolean that indicates if the shift key was pressed (true) or not (false) when the event occurred. 1144 | * metaKey: [optional] (Mac Only) a Boolean that indicates if the Meta key was pressed (true) or not (false) when the event occurred. 1145 | * On Macintosh keyboards, this is the command key. To detect Windows key on Windows, please use keyCode instead. 1146 | * An example JSON string: 1147 | * 1148 | * [ 1149 | * { 1150 | * "keyCode": 48 1151 | * }, 1152 | * { 1153 | * "keyCode": 123, 1154 | * "ctrlKey": true 1155 | * }, 1156 | * { 1157 | * "keyCode": 123, 1158 | * "ctrlKey": true, 1159 | * "metaKey": true 1160 | * } 1161 | * ] 1162 | * 1163 | */ 1164 | CSInterface.prototype.registerKeyEventsInterest = function(keyEventsInterest) 1165 | { 1166 | return window.__adobe_cep__.registerKeyEventsInterest(keyEventsInterest); 1167 | }; 1168 | 1169 | /** 1170 | * Set the title of the extension window. 1171 | * This function works with modal and modeless extensions in all Adobe products, and panel extensions in Photoshop, InDesign, InCopy, Illustrator, Flash Pro and Dreamweaver. 1172 | * 1173 | * Since 6.1.0 1174 | * 1175 | * @param title The window title. 1176 | */ 1177 | CSInterface.prototype.setWindowTitle = function(title) 1178 | { 1179 | window.__adobe_cep__.invokeSync("setWindowTitle", title); 1180 | }; 1181 | 1182 | /** 1183 | * Get the title of the extension window. 1184 | * This function works with modal and modeless extensions in all Adobe products, and panel extensions in Photoshop, InDesign, InCopy, Illustrator, Flash Pro and Dreamweaver. 1185 | * 1186 | * Since 6.1.0 1187 | * 1188 | * @return The window title. 1189 | */ 1190 | CSInterface.prototype.getWindowTitle = function() 1191 | { 1192 | return window.__adobe_cep__.invokeSync("getWindowTitle", ""); 1193 | }; -------------------------------------------------------------------------------- /src/python/premiere-opentimelineio.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | 3 | import argparse 4 | import os 5 | import sys 6 | import tempfile 7 | 8 | try: 9 | import opentimelineio as otio 10 | except ImportError: 11 | raise SystemExit('It appears OpenTimelineIO is not installed on the selected Python interpreter. As such, ' 12 | 'I cannot really run anything, so fix it.') 13 | 14 | 15 | class OTIOTools(object): 16 | def __init__(self): 17 | self.arg_parser = argparse.ArgumentParser() 18 | self.arg_parser.add_argument( 19 | '-v', '--verbosity', 20 | type=int, 21 | default=3, 22 | help='Verbosity level between 1 and 4. 1 = errors and warning, 4 = everything') 23 | 24 | self.subparsers = self.arg_parser.add_subparsers() 25 | self.set_up_subparser_export_file() 26 | self.set_up_subparser_convert_file() 27 | 28 | def parse(self, args): 29 | if not len(args): 30 | args = ['-h'] 31 | 32 | parsed = self.arg_parser.parse_args(args) 33 | 34 | return parsed.func(vars(parsed)) 35 | 36 | def set_up_subparser_export_file(self): 37 | parser = self.subparsers.add_parser( 38 | 'export-file', 39 | help='Export the given file to the given location.') 40 | 41 | parser.add_argument( 42 | '-i', '--input', 43 | help='Path to input file.') 44 | 45 | parser.add_argument( 46 | '-o', '--output', 47 | help='Path to place the output OTIO file.') 48 | 49 | parser.set_defaults(func=self.export_file) 50 | 51 | def set_up_subparser_convert_file(self): 52 | adapters = otio.adapters.available_adapter_names() 53 | 54 | parser = self.subparsers.add_parser( 55 | 'convert-file', 56 | help='Convert a file to another file.') 57 | 58 | parser.add_argument( 59 | '-f', '--format', 60 | choices=adapters, 61 | help='The file type to convert to.') 62 | 63 | parser.add_argument( 64 | '-i', '--input', 65 | help='Path to the input file.') 66 | 67 | parser.add_argument( 68 | '-t', '--temp-file', 69 | required=False, 70 | help='Path to temporary file.') 71 | 72 | parser.set_defaults(func=self.convert_file) 73 | 74 | 75 | def export_file(self, kwargs): 76 | input_path = kwargs.get('input') 77 | output_path = kwargs.get('output') 78 | 79 | timeline = otio.adapters.read_from_file(input_path) 80 | otio.adapters.write_to_file(timeline, output_path) 81 | 82 | print(input_path) 83 | return input_path 84 | 85 | def convert_file(self, kwargs): 86 | file_format = kwargs.get('format') 87 | input_path = kwargs.get('input') 88 | temp_file = kwargs.get('temp_file') 89 | 90 | # if not temp_file: 91 | # temp_file = tempfile.TemporaryFile(prefix='OTIO_') 92 | 93 | timeline = otio.adapters.read_from_file(input_path) 94 | otio.adapters.write_to_file(timeline, temp_file, adapter_name=file_format) 95 | 96 | print(temp_file) 97 | return temp_file 98 | 99 | 100 | def main(args, exit=False): 101 | tools = OTIOTools() 102 | exitCode = tools.parse(args[1:]) 103 | if exit: 104 | sys.exit(exitCode) 105 | return exitCode 106 | 107 | if __name__ == '__main__': 108 | main(sys.argv) 109 | -------------------------------------------------------------------------------- /src/styles/_variables.scss: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | 3 | $primary-bg : #232323; -------------------------------------------------------------------------------- /src/styles/opentimelineio.scss: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | 3 | @import "variables"; 4 | @import "vendor/wing"; 5 | @import "vendor/font-awesome"; 6 | @import "partials/base"; 7 | @import "partials/buttons"; 8 | @import "partials/icons"; 9 | @import "page_overrides/opentimelineio"; 10 | -------------------------------------------------------------------------------- /src/styles/page_overrides/opentimelineio.scss: -------------------------------------------------------------------------------- 1 | body { 2 | padding-right: 20px; 3 | margin-right: 24px; 4 | color: #f5f5f5; 5 | } 6 | 7 | textarea { 8 | background-color: #111111; 9 | border: 0px; 10 | } 11 | 12 | #dev-info-panel { 13 | padding: 0px 0px 0px 1rem; 14 | margin: 0px 0px 0px 0px; 15 | } 16 | 17 | -------------------------------------------------------------------------------- /src/styles/partials/_base.scss: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: $primary-bg; 3 | min-width: 200px; 4 | overflow-y: hidden; 5 | } 6 | 7 | .accordion { 8 | background-color: transparent; 9 | color: ghostwhite; 10 | cursor: pointer; 11 | width: 100%; 12 | text-align: left; 13 | border: none; 14 | outline: none; 15 | transition: 0.4s; 16 | 17 | &:active, &:hover { 18 | background-color: transparent; 19 | } 20 | } 21 | 22 | .panel { 23 | background-color: #363636; 24 | display: none; 25 | overflow: hidden; 26 | padding-left: 1rem; 27 | padding-right: 2rem; 28 | padding-bottom: 1rem; 29 | } 30 | 31 | #status-field { 32 | width: 95%; 33 | margin-right: 5%; 34 | border: 1px solid transparent; 35 | 36 | &:hover { 37 | border: 1px solid transparent; 38 | } 39 | } 40 | 41 | #dev-info-box { 42 | background-color: #363636; 43 | } 44 | 45 | #github-text, #docs-text, #python-text, #terminal-text { 46 | padding-bottom: 0.5rem; 47 | color: dimgray; 48 | margin-left: 20px; 49 | width: 55%; 50 | display: inline-block; 51 | 52 | a { 53 | color: dimgray; 54 | &:hover { 55 | cursor: pointer; 56 | text-decoration: underline; 57 | } 58 | } 59 | 60 | &:hover { 61 | color: dimgray; 62 | } 63 | } 64 | 65 | -------------------------------------------------------------------------------- /src/styles/partials/_buttons.scss: -------------------------------------------------------------------------------- 1 | .btn { 2 | width: 200px; 3 | -webkit-border-radius: 20px; 4 | -moz-border-radius: 20px; 5 | border-radius: 20px; 6 | border: 1px solid transparent; 7 | 8 | &:hover { 9 | border: 1px solid whitesmoke; 10 | } 11 | } 12 | 13 | @media only screen and (max-width:650px) { 14 | .btn { 15 | width: 100%; 16 | } 17 | } -------------------------------------------------------------------------------- /src/styles/partials/_icons.scss: -------------------------------------------------------------------------------- 1 | #python-icon, #terminal-icon, #github-icon, #docs-icon { 2 | margin-top: 4px; 3 | margin-bottom: -7px; 4 | width: 20px; 5 | float: left; 6 | } 7 | -------------------------------------------------------------------------------- /src/styles/vendor/devicon.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Devicons 1.8.0 made by Theodore Vorillas / http://vorillaz.com 3 | */ 4 | @font-face { 5 | font-family: 'devicons'; 6 | src: url("../fonts/devicons.eot?xqxft6"); 7 | src: url("../fonts/devicons.eot?#iefixxqxft6") format("embedded-opentype"), url("../fonts/devicons.woff?xqxft6") format("woff"), url("../fonts/devicons.ttf?xqxft6") format("truetype"), url("../fonts/devicons.svg?xqxft6#devicons") format("svg"); 8 | font-weight: normal; 9 | font-style: normal; 10 | } 11 | 12 | .devicons { 13 | font-family: 'devicons'; 14 | speak: none; 15 | font-style: normal; 16 | font-weight: normal; 17 | font-variant: normal; 18 | text-transform: none; 19 | line-height: 1; 20 | /* Better Font Rendering =========== */ 21 | -webkit-font-smoothing: antialiased; 22 | -moz-osx-font-smoothing: grayscale; 23 | } 24 | 25 | .devicons-git:before { 26 | content: "\e602"; 27 | } 28 | 29 | .devicons-git_compare:before { 30 | content: "\e628"; 31 | } 32 | 33 | .devicons-git_branch:before { 34 | content: "\e625"; 35 | } 36 | 37 | .devicons-git_commit:before { 38 | content: "\e629"; 39 | } 40 | 41 | .devicons-git_pull_request:before { 42 | content: "\e626"; 43 | } 44 | 45 | .devicons-git_merge:before { 46 | content: "\e627"; 47 | } 48 | 49 | .devicons-bitbucket:before { 50 | content: "\e603"; 51 | } 52 | 53 | .devicons-github_alt:before { 54 | content: "\e608"; 55 | } 56 | 57 | .devicons-github_badge:before { 58 | content: "\e609"; 59 | } 60 | 61 | .devicons-github:before { 62 | content: "\e60a"; 63 | } 64 | 65 | .devicons-github_full:before { 66 | content: "\e617"; 67 | } 68 | 69 | .devicons-java:before { 70 | content: "\e638"; 71 | } 72 | 73 | .devicons-ruby:before { 74 | content: "\e639"; 75 | } 76 | 77 | .devicons-scala:before { 78 | content: "\e637"; 79 | } 80 | 81 | .devicons-python:before { 82 | content: "\e63c"; 83 | } 84 | 85 | .devicons-go:before { 86 | content: "\e624"; 87 | } 88 | 89 | .devicons-ruby_on_rails:before { 90 | content: "\e63b"; 91 | } 92 | 93 | .devicons-django:before { 94 | content: "\e61d"; 95 | } 96 | 97 | .devicons-markdown:before { 98 | content: "\e63e"; 99 | } 100 | 101 | .devicons-php:before { 102 | content: "\e63d"; 103 | } 104 | 105 | .devicons-mysql:before { 106 | content: "\e604"; 107 | } 108 | 109 | .devicons-streamline:before { 110 | content: "\e605"; 111 | } 112 | 113 | .devicons-database:before { 114 | content: "\e606"; 115 | } 116 | 117 | .devicons-laravel:before { 118 | content: "\e63f"; 119 | } 120 | 121 | .devicons-javascript:before { 122 | content: "\e64e"; 123 | } 124 | 125 | .devicons-angular:before { 126 | content: "\e653"; 127 | } 128 | 129 | .devicons-backbone:before { 130 | content: "\e652"; 131 | } 132 | 133 | .devicons-coffeescript:before { 134 | content: "\e651"; 135 | } 136 | 137 | .devicons-jquery:before { 138 | content: "\e650"; 139 | } 140 | 141 | .devicons-modernizr:before { 142 | content: "\e620"; 143 | } 144 | 145 | .devicons-jquery_ui:before { 146 | content: "\e654"; 147 | } 148 | 149 | .devicons-ember:before { 150 | content: "\e61b"; 151 | } 152 | 153 | .devicons-dojo:before { 154 | content: "\e61c"; 155 | } 156 | 157 | .devicons-nodejs:before { 158 | content: "\e619"; 159 | } 160 | 161 | .devicons-nodejs_small:before { 162 | content: "\e618"; 163 | } 164 | 165 | .devicons-javascript_shield:before { 166 | content: "\e64f"; 167 | } 168 | 169 | .devicons-bootstrap:before { 170 | content: "\e647"; 171 | } 172 | 173 | .devicons-sass:before { 174 | content: "\e64b"; 175 | } 176 | 177 | .devicons-css3_full:before { 178 | content: "\e64a"; 179 | } 180 | 181 | .devicons-css3:before { 182 | content: "\e649"; 183 | } 184 | 185 | .devicons-html5:before { 186 | content: "\e636"; 187 | } 188 | 189 | .devicons-html5_multimedia:before { 190 | content: "\e632"; 191 | } 192 | 193 | .devicons-html5_device_access:before { 194 | content: "\e633"; 195 | } 196 | 197 | .devicons-html5_3d_effects:before { 198 | content: "\e635"; 199 | } 200 | 201 | .devicons-html5_connectivity:before { 202 | content: "\e634"; 203 | } 204 | 205 | .devicons-ghost_small:before { 206 | content: "\e614"; 207 | } 208 | 209 | .devicons-ghost:before { 210 | content: "\e61f"; 211 | } 212 | 213 | .devicons-magento:before { 214 | content: "\e640"; 215 | } 216 | 217 | .devicons-joomla:before { 218 | content: "\e641"; 219 | } 220 | 221 | .devicons-jekyll_small:before { 222 | content: "\e60d"; 223 | } 224 | 225 | .devicons-drupal:before { 226 | content: "\e642"; 227 | } 228 | 229 | .devicons-wordpress:before { 230 | content: "\e60b"; 231 | } 232 | 233 | .devicons-grunt:before { 234 | content: "\e64c"; 235 | } 236 | 237 | .devicons-bower:before { 238 | content: "\e64d"; 239 | } 240 | 241 | .devicons-npm:before { 242 | content: "\e61e"; 243 | } 244 | 245 | .devicons-yahoo_small:before { 246 | content: "\e62b"; 247 | } 248 | 249 | .devicons-yahoo:before { 250 | content: "\e615"; 251 | } 252 | 253 | .devicons-bing_small:before { 254 | content: "\e600"; 255 | } 256 | 257 | .devicons-windows:before { 258 | content: "\e60f"; 259 | } 260 | 261 | .devicons-linux:before { 262 | content: "\e612"; 263 | } 264 | 265 | .devicons-ubuntu:before { 266 | content: "\e63a"; 267 | } 268 | 269 | .devicons-android:before { 270 | content: "\e60e"; 271 | } 272 | 273 | .devicons-apple:before { 274 | content: "\e611"; 275 | } 276 | 277 | .devicons-appstore:before { 278 | content: "\e613"; 279 | } 280 | 281 | .devicons-phonegap:before { 282 | content: "\e630"; 283 | } 284 | 285 | .devicons-blackberry:before { 286 | content: "\e623"; 287 | } 288 | 289 | .devicons-stackoverflow:before { 290 | content: "\e610"; 291 | } 292 | 293 | .devicons-techcrunch:before { 294 | content: "\e62c"; 295 | } 296 | 297 | .devicons-codrops:before { 298 | content: "\e62f"; 299 | } 300 | 301 | .devicons-css_tricks:before { 302 | content: "\e601"; 303 | } 304 | 305 | .devicons-smashing_magazine:before { 306 | content: "\e62d"; 307 | } 308 | 309 | .devicons-netmagazine:before { 310 | content: "\e62e"; 311 | } 312 | 313 | .devicons-codepen:before { 314 | content: "\e616"; 315 | } 316 | 317 | .devicons-cssdeck:before { 318 | content: "\e62a"; 319 | } 320 | 321 | .devicons-hackernews:before { 322 | content: "\e61a"; 323 | } 324 | 325 | .devicons-dropbox:before { 326 | content: "\e607"; 327 | } 328 | 329 | .devicons-google_drive:before { 330 | content: "\e631"; 331 | } 332 | 333 | .devicons-visualstudio:before { 334 | content: "\e60c"; 335 | } 336 | 337 | .devicons-unity_small:before { 338 | content: "\e621"; 339 | } 340 | 341 | .devicons-raspberry_pi:before { 342 | content: "\e622"; 343 | } 344 | 345 | .devicons-chrome:before { 346 | content: "\e643"; 347 | } 348 | 349 | .devicons-ie:before { 350 | content: "\e644"; 351 | } 352 | 353 | .devicons-firefox:before { 354 | content: "\e645"; 355 | } 356 | 357 | .devicons-opera:before { 358 | content: "\e646"; 359 | } 360 | 361 | .devicons-safari:before { 362 | content: "\e648"; 363 | } 364 | 365 | .devicons-swift:before { 366 | content: "\e655"; 367 | } 368 | 369 | .devicons-symfony:before { 370 | content: "\e656"; 371 | } 372 | 373 | .devicons-symfony_badge:before { 374 | content: "\e657"; 375 | } 376 | 377 | .devicons-less:before { 378 | content: "\e658"; 379 | } 380 | 381 | .devicons-stylus:before { 382 | content: "\e659"; 383 | } 384 | 385 | .devicons-trello:before { 386 | content: "\e65a"; 387 | } 388 | 389 | .devicons-atlassian:before { 390 | content: "\e65b"; 391 | } 392 | 393 | .devicons-jira:before { 394 | content: "\e65c"; 395 | } 396 | 397 | .devicons-envato:before { 398 | content: "\e65d"; 399 | } 400 | 401 | .devicons-snap_svg:before { 402 | content: "\e65e"; 403 | } 404 | 405 | .devicons-raphael:before { 406 | content: "\e65f"; 407 | } 408 | 409 | .devicons-google_analytics:before { 410 | content: "\e660"; 411 | } 412 | 413 | .devicons-compass:before { 414 | content: "\e661"; 415 | } 416 | 417 | .devicons-onedrive:before { 418 | content: "\e662"; 419 | } 420 | 421 | .devicons-gulp:before { 422 | content: "\e663"; 423 | } 424 | 425 | .devicons-atom:before { 426 | content: "\e664"; 427 | } 428 | 429 | .devicons-cisco:before { 430 | content: "\e665"; 431 | } 432 | 433 | .devicons-nancy:before { 434 | content: "\e666"; 435 | } 436 | 437 | .devicons-clojure:before { 438 | content: "\e668"; 439 | } 440 | 441 | .devicons-clojure_alt:before { 442 | content: "\e66a"; 443 | } 444 | 445 | .devicons-perl:before { 446 | content: "\e669"; 447 | } 448 | 449 | .devicons-celluloid:before { 450 | content: "\e66b"; 451 | } 452 | 453 | .devicons-w3c:before { 454 | content: "\e66c"; 455 | } 456 | 457 | .devicons-redis:before { 458 | content: "\e66d"; 459 | } 460 | 461 | .devicons-postgresql:before { 462 | content: "\e66e"; 463 | } 464 | 465 | .devicons-webplatform:before { 466 | content: "\e66f"; 467 | } 468 | 469 | .devicons-jenkins:before { 470 | content: "\e667"; 471 | } 472 | 473 | .devicons-requirejs:before { 474 | content: "\e670"; 475 | } 476 | 477 | .devicons-opensource:before { 478 | content: "\e671"; 479 | } 480 | 481 | .devicons-typo3:before { 482 | content: "\e672"; 483 | } 484 | 485 | .devicons-uikit:before { 486 | content: "\e673"; 487 | } 488 | 489 | .devicons-doctrine:before { 490 | content: "\e674"; 491 | } 492 | 493 | .devicons-groovy:before { 494 | content: "\e675"; 495 | } 496 | 497 | .devicons-nginx:before { 498 | content: "\e676"; 499 | } 500 | 501 | .devicons-haskell:before { 502 | content: "\e677"; 503 | } 504 | 505 | .devicons-zend:before { 506 | content: "\e678"; 507 | } 508 | 509 | .devicons-gnu:before { 510 | content: "\e679"; 511 | } 512 | 513 | .devicons-yeoman:before { 514 | content: "\e67a"; 515 | } 516 | 517 | .devicons-heroku:before { 518 | content: "\e67b"; 519 | } 520 | 521 | .devicons-debian:before { 522 | content: "\e67d"; 523 | } 524 | 525 | .devicons-travis:before { 526 | content: "\e67e"; 527 | } 528 | 529 | .devicons-dotnet:before { 530 | content: "\e67f"; 531 | } 532 | 533 | .devicons-codeigniter:before { 534 | content: "\e680"; 535 | } 536 | 537 | .devicons-javascript_badge:before { 538 | content: "\e681"; 539 | } 540 | 541 | .devicons-yii:before { 542 | content: "\e682"; 543 | } 544 | 545 | .devicons-msql_server:before { 546 | content: "\e67c"; 547 | } 548 | 549 | .devicons-composer:before { 550 | content: "\e683"; 551 | } 552 | 553 | .devicons-krakenjs_badge:before { 554 | content: "\e684"; 555 | } 556 | 557 | .devicons-krakenjs:before { 558 | content: "\e685"; 559 | } 560 | 561 | .devicons-mozilla:before { 562 | content: "\e686"; 563 | } 564 | 565 | .devicons-firebase:before { 566 | content: "\e687"; 567 | } 568 | 569 | .devicons-sizzlejs:before { 570 | content: "\e688"; 571 | } 572 | 573 | .devicons-creativecommons:before { 574 | content: "\e689"; 575 | } 576 | 577 | .devicons-creativecommons_badge:before { 578 | content: "\e68a"; 579 | } 580 | 581 | .devicons-mitlicence:before { 582 | content: "\e68b"; 583 | } 584 | 585 | .devicons-senchatouch:before { 586 | content: "\e68c"; 587 | } 588 | 589 | .devicons-bugsense:before { 590 | content: "\e68d"; 591 | } 592 | 593 | .devicons-extjs:before { 594 | content: "\e68e"; 595 | } 596 | 597 | .devicons-mootools_badge:before { 598 | content: "\e68f"; 599 | } 600 | 601 | .devicons-mootools:before { 602 | content: "\e690"; 603 | } 604 | 605 | .devicons-ruby_rough:before { 606 | content: "\e691"; 607 | } 608 | 609 | .devicons-komodo:before { 610 | content: "\e692"; 611 | } 612 | 613 | .devicons-coda:before { 614 | content: "\e693"; 615 | } 616 | 617 | .devicons-bintray:before { 618 | content: "\e694"; 619 | } 620 | 621 | .devicons-terminal:before { 622 | content: "\e695"; 623 | } 624 | 625 | .devicons-code:before { 626 | content: "\e696"; 627 | } 628 | 629 | .devicons-responsive:before { 630 | content: "\e697"; 631 | } 632 | 633 | .devicons-dart:before { 634 | content: "\e698"; 635 | } 636 | 637 | .devicons-aptana:before { 638 | content: "\e699"; 639 | } 640 | 641 | .devicons-mailchimp:before { 642 | content: "\e69a"; 643 | } 644 | 645 | .devicons-netbeans:before { 646 | content: "\e69b"; 647 | } 648 | 649 | .devicons-dreamweaver:before { 650 | content: "\e69c"; 651 | } 652 | 653 | .devicons-brackets:before { 654 | content: "\e69d"; 655 | } 656 | 657 | .devicons-eclipse:before { 658 | content: "\e69e"; 659 | } 660 | 661 | .devicons-cloud9:before { 662 | content: "\e69f"; 663 | } 664 | 665 | .devicons-scrum:before { 666 | content: "\e6a0"; 667 | } 668 | 669 | .devicons-prolog:before { 670 | content: "\e6a1"; 671 | } 672 | 673 | .devicons-terminal_badge:before { 674 | content: "\e6a2"; 675 | } 676 | 677 | .devicons-code_badge:before { 678 | content: "\e6a3"; 679 | } 680 | 681 | .devicons-mongodb:before { 682 | content: "\e6a4"; 683 | } 684 | 685 | .devicons-meteor:before { 686 | content: "\e6a5"; 687 | } 688 | 689 | .devicons-meteorfull:before { 690 | content: "\e6a6"; 691 | } 692 | 693 | .devicons-fsharp:before { 694 | content: "\e6a7"; 695 | } 696 | 697 | .devicons-rust:before { 698 | content: "\e6a8"; 699 | } 700 | 701 | .devicons-ionic:before { 702 | content: "\e6a9"; 703 | } 704 | 705 | .devicons-sublime:before { 706 | content: "\e6aa"; 707 | } 708 | 709 | .devicons-appcelerator:before { 710 | content: "\e6ab"; 711 | } 712 | 713 | .devicons-asterisk:before { 714 | content: "\e6ac"; 715 | } 716 | 717 | .devicons-aws:before { 718 | content: "\e6ad"; 719 | } 720 | 721 | .devicons-digital-ocean:before { 722 | content: "\e6ae"; 723 | } 724 | 725 | .devicons-dlang:before { 726 | content: "\e6af"; 727 | } 728 | 729 | .devicons-docker:before { 730 | content: "\e6b0"; 731 | } 732 | 733 | .devicons-erlang:before { 734 | content: "\e6b1"; 735 | } 736 | 737 | .devicons-google-cloud-platform:before { 738 | content: "\e6b2"; 739 | } 740 | 741 | .devicons-grails:before { 742 | content: "\e6b3"; 743 | } 744 | 745 | .devicons-illustrator:before { 746 | content: "\e6b4"; 747 | } 748 | 749 | .devicons-intellij:before { 750 | content: "\e6b5"; 751 | } 752 | 753 | .devicons-materializecss:before { 754 | content: "\e6b6"; 755 | } 756 | 757 | .devicons-openshift:before { 758 | content: "\e6b7"; 759 | } 760 | 761 | .devicons-photoshop:before { 762 | content: "\e6b8"; 763 | } 764 | 765 | .devicons-rackspace:before { 766 | content: "\e6b9"; 767 | } 768 | 769 | .devicons-react:before { 770 | content: "\e6ba"; 771 | } 772 | 773 | .devicons-redhat:before { 774 | content: "\e6bb"; 775 | } 776 | 777 | .devicons-scriptcs:before { 778 | content: "\e6bc"; 779 | } 780 | 781 | .devicons-sqllite:before { 782 | content: "\e6c4"; 783 | } 784 | 785 | .devicons-vim:before { 786 | content: "\e6c5"; 787 | } 788 | -------------------------------------------------------------------------------- /src/styles/vendor/wing.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Wing 1.0.0-beta 3 | * Copyright 2016, Kabir Shah 4 | * http://usewing.ml/ 5 | * Free to use under the MIT license. 6 | * https://kingpixil.github.io/license 7 | */ 8 | 9 | /*------------------------------------------------------------ 10 | Base Style 11 | ------------------------------------------------------------*/ 12 | 13 | html { 14 | box-sizing: border-box; 15 | font-size: 62.5%; 16 | margin: 0; 17 | padding: 0; 18 | } 19 | 20 | body { 21 | letter-spacing: 0.01em; 22 | line-height: 1.6; 23 | font-size: 1.5em; 24 | font-weight: 400; 25 | font-family: -apple-system, BlinkMacSystemFont, Avenir, "Avenir Next", "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; 26 | } 27 | 28 | /*------------------------------------------------------------ 29 | Typography 30 | ------------------------------------------------------------*/ 31 | 32 | h1, 33 | h2, 34 | h3, 35 | h4, 36 | h5, 37 | h6 { 38 | font-weight: 400; 39 | font-family: -apple-system, BlinkMacSystemFont, Avenir, "Avenir Next", "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; 40 | } 41 | 42 | h1, h2, h3 { 43 | letter-spacing: -.1rem; 44 | } 45 | 46 | h1 { 47 | font-size: 4.0rem; 48 | line-height: 1.2; 49 | } 50 | 51 | h2 { 52 | font-size: 3.6rem; 53 | line-height: 1.25; 54 | } 55 | 56 | h3 { 57 | font-size: 3.0rem; 58 | line-height: 1.3; 59 | } 60 | 61 | h4 { 62 | font-size: 2.4rem; 63 | line-height: 1.35; 64 | letter-spacing: -.08rem; 65 | } 66 | 67 | h5 { 68 | font-size: 1.8rem; 69 | line-height: 1.5; 70 | letter-spacing: -.05rem; 71 | } 72 | 73 | h6 { 74 | font-size: 1.5rem; 75 | line-height: 1.6; 76 | letter-spacing: 0; 77 | } 78 | 79 | @media (min-width: 550px) { 80 | h1 { 81 | font-size: 5.0rem; 82 | } 83 | h2 { 84 | font-size: 4.2rem; 85 | } 86 | h3 { 87 | font-size: 3.6rem; 88 | } 89 | h4 { 90 | font-size: 3.0rem; 91 | } 92 | h5 { 93 | font-size: 2.4rem; 94 | } 95 | h6 { 96 | font-size: 1.5rem; 97 | } 98 | } 99 | 100 | /*------------------------------------------------------------ 101 | Links 102 | ------------------------------------------------------------*/ 103 | a { 104 | color: #104cfb; 105 | transition: all .1s ease; 106 | } 107 | 108 | a:hover { 109 | color: #222222; 110 | } 111 | 112 | /*------------------------------------------------------------ 113 | Buttons 114 | ------------------------------------------------------------*/ 115 | 116 | button, [type=submit] { 117 | padding: 1.1rem 3.5rem; 118 | margin: 1rem 0; 119 | background: #111111; 120 | color: #f5f5f5; 121 | border-radius: 2px; 122 | border: none; 123 | font-size: 1.3rem; 124 | transition: all .2s ease; 125 | } 126 | 127 | button.outline, [type=submit].outline { 128 | padding: 1.1rem 3.5rem; 129 | background: none; 130 | color: #111111; 131 | border: 1.5px solid #111111; 132 | } 133 | 134 | button:hover, [type=submit]:hover { 135 | background: #222222; 136 | } 137 | 138 | button.outline:hover, [type=submit].outline:hover { 139 | background: none; 140 | border: 1.5px solid #444444; 141 | color: #444444; 142 | } 143 | 144 | button:focus, [type=submit]:focus { 145 | outline: none; 146 | } 147 | 148 | button:active, [type=submit]:active { 149 | transform: scale(.99); 150 | } 151 | 152 | /*------------------------------------------------------------ 153 | Forms 154 | ------------------------------------------------------------*/ 155 | 156 | input[type=text], input[type=password], input[type=email], input[type=search], input[type=number], input[type=file], input[type=tel], select, textarea, textarea[type=text] { 157 | margin: 1rem 0; 158 | width: 100%; 159 | max-width: 100%; 160 | border-radius: 2px; 161 | border: 1px solid #a4a4a4; 162 | font-size: 1.3rem; 163 | transition: all .2s ease; 164 | } 165 | 166 | input[type=text]:hover, input[type=password]:hover, input[type=email]:hover, input[type=search]:hover, input[type=number]:hover, input[type=file], input[type=tel], select:hover, textarea:hover, textarea[type=text]:hover { 167 | border: 1px solid #111111; 168 | } 169 | 170 | input[type=text]:focus, input[type=password]:focus, input[type=email]:focus, input[type=search]:focus, input[type=number], input[type=file], input[type=tel], select:focus textarea:focus, textarea[type=text]:focus { 171 | outline: none; 172 | border: 1px solid #104cfb; 173 | } 174 | 175 | input[type=text], input[type=password], input[type=email], input[type=search], input[type=number], input[type=file], input[type=tel], select { 176 | padding: 1.1rem; 177 | } 178 | 179 | textarea, textarea[type=text] { 180 | height: 10rem; 181 | padding: 14px 20px; 182 | } 183 | 184 | .container { 185 | max-width: 960px; 186 | margin: 0 auto; 187 | width: 80%; 188 | } 189 | 190 | .row { 191 | display: flex; 192 | flex-flow: row wrap; 193 | justify-content: space-between; 194 | } 195 | 196 | .row > :first-child { 197 | margin-left: 0; 198 | } 199 | 200 | .row > :last-child { 201 | margin-right: 0; 202 | } 203 | 204 | .col { 205 | -webkit-box-flex: 1; 206 | -moz-box-flex: 1; 207 | -webkit-flex: 1; 208 | -ms-flex: 1; 209 | flex: 1; 210 | } 211 | 212 | .col, [class^='col-'], [class*=" col-"] { 213 | margin: 1rem; 214 | } 215 | 216 | .col-1 { 217 | flex: 1; 218 | } 219 | 220 | .col-2 { 221 | flex: 2; 222 | } 223 | 224 | .col-3 { 225 | flex: 3; 226 | } 227 | 228 | .col-4 { 229 | flex: 4; 230 | } 231 | 232 | .col-5 { 233 | flex: 5; 234 | } 235 | 236 | .col-6 { 237 | flex: 6; 238 | } 239 | 240 | .col-7 { 241 | flex: 7; 242 | } 243 | 244 | .col-8 { 245 | flex: 8; 246 | } 247 | 248 | .col-9 { 249 | flex: 9; 250 | } 251 | 252 | .col-10 { 253 | flex: 10; 254 | } 255 | 256 | .col-11 { 257 | flex: 11; 258 | } 259 | 260 | .col-12 { 261 | flex: 12; 262 | } 263 | 264 | @media screen and (max-width: 768px) { 265 | .col, [class^='col-'], [class*=" col-"] { 266 | margin: 0; 267 | flex: 0 0 100%; 268 | } 269 | } 270 | 271 | /*------------------------------------------------------------ 272 | Lists 273 | ------------------------------------------------------------*/ 274 | 275 | ul { 276 | list-style: circle inside; 277 | } 278 | 279 | ol { 280 | list-style: decimal inside; 281 | } 282 | 283 | /*------------------------------------------------------------ 284 | Tables 285 | ------------------------------------------------------------*/ 286 | 287 | .table { 288 | width: 100%; 289 | border: none; 290 | border-collapse: collapse; 291 | border-spacing: 0; 292 | text-align: left; 293 | } 294 | 295 | .table th, .table td { 296 | vertical-align: middle; 297 | padding: 12px 4px; 298 | } 299 | 300 | .table thead { 301 | border-bottom: 2px solid #333030; 302 | } 303 | 304 | /* responsive tables */ 305 | @media screen and (max-width: 768px) { 306 | .table.responsive { 307 | position: relative; 308 | display: block; 309 | } 310 | .table.responsive th, .table.responsive td { 311 | margin: 0; 312 | } 313 | .table.responsive thead { 314 | display: block; 315 | float: left; 316 | border: 0; 317 | } 318 | .table.responsive thead tr { 319 | display: block; 320 | padding: 0 10px 0 0; 321 | border-right: 2px solid #333030; 322 | } 323 | .table.responsive th { 324 | display: block; 325 | text-align: right; 326 | } 327 | .table.responsive tbody { 328 | display: block; 329 | overflow-x: auto; 330 | white-space: nowrap; 331 | } 332 | .table.responsive tbody tr { 333 | display: inline-block; 334 | } 335 | .table.responsive td { 336 | display: block; 337 | min-height: 16px; 338 | text-align: left; 339 | } 340 | .table.responsive tr { 341 | padding: 0 10px; 342 | } 343 | } 344 | 345 | /*------------------------------------------------------------ 346 | Utilities 347 | ------------------------------------------------------------*/ 348 | 349 | .pull-right { 350 | float: right; 351 | } 352 | 353 | .pull-left { 354 | float: left; 355 | } 356 | 357 | .text-center { 358 | text-align: center; 359 | } 360 | 361 | .full-screen { 362 | width: 100%; 363 | min-height: 100vh; 364 | } 365 | 366 | .vertical-align { 367 | display: flex; 368 | align-items: center; 369 | } 370 | 371 | .horizontal-align { 372 | display: flex; 373 | justify-content: center; 374 | } 375 | 376 | .center { 377 | display: flex; 378 | align-items: center; 379 | justify-content: center; 380 | } 381 | 382 | .right { 383 | display: flex; 384 | align-items: center; 385 | justify-content: flex-end; 386 | } 387 | 388 | .left { 389 | display: flex; 390 | align-items: center; 391 | justify-content: flex-start; 392 | } 393 | 394 | .fixed { 395 | position: fixed; 396 | width: 100%; 397 | } 398 | 399 | @media screen and (max-width: 400px) { 400 | .hide-phone { display: none; } 401 | } 402 | 403 | @media screen and (max-width: 768px) { 404 | .hide-tablet { display: none; } 405 | } 406 | 407 | /*------------------------------------------------------------ 408 | Misc 409 | ------------------------------------------------------------*/ 410 | 411 | code { 412 | padding: 0.2rem 0.5rem; 413 | margin: 0 0.2rem; 414 | font-size: 90%; 415 | white-space: nowrap; 416 | background: #F1F1F1; 417 | border: 1px solid #E1E1E1; 418 | border-radius: 4px; 419 | font-family: "Consolas", "Monaco", "Menlo", monospace; 420 | } 421 | 422 | pre > code { 423 | display: block; 424 | padding: 1rem 1.5rem; 425 | white-space: pre-wrap; 426 | white-space: -moz-pre-wrap; 427 | white-space: -pre-wrap; 428 | white-space: -o-pre-wrap; 429 | word-wrap: break-word; 430 | } 431 | 432 | /*------------------------------------------------------------ 433 | Navigation 434 | ------------------------------------------------------------*/ 435 | 436 | .nav { 437 | position: relative; 438 | display: flex; 439 | flex-wrap: wrap; 440 | align-items: center; 441 | padding: 1rem; 442 | } 443 | 444 | .nav-menu, 445 | .nav-brand { 446 | display: flex; 447 | } 448 | 449 | .nav-menu { 450 | flex-flow: row; 451 | flex: 1 0 auto; 452 | } 453 | 454 | .nav-item { 455 | padding: 1rem 2rem; 456 | } 457 | 458 | .nav-logo { 459 | font-weight: bolder; 460 | font-size: 2rem; 461 | line-height: 0; 462 | } 463 | --------------------------------------------------------------------------------