├── .gitignore
├── dist
├── kscope.zip
└── kscope
│ ├── js
│ ├── kscope.min.js
│ ├── apex_pages
│ │ ├── p2.min.js
│ │ └── p2.js
│ ├── kscope.js
│ ├── bundle.min.js
│ └── bundle.js
│ └── apex_pages
│ └── js
│ ├── p2.min.js
│ └── p2.js
├── config.json
├── src
└── js
│ ├── applications
│ └── kscope
│ │ ├── kscope.js
│ │ └── apex_pages
│ │ └── p2.js
│ ├── modules
│ ├── keyboardShortcuts.js
│ └── message.js
│ └── widgets
│ └── customReport.js
├── gulpfile.js
├── gulp-tasks
├── deploy.js
├── scripts.js
└── bundle.js
├── package.json
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/*
2 |
--------------------------------------------------------------------------------
/dist/kscope.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mennooo/apex-modular-js/HEAD/dist/kscope.zip
--------------------------------------------------------------------------------
/dist/kscope/js/kscope.min.js:
--------------------------------------------------------------------------------
1 | !function(){apex.jQuery(document).ready(function(){$(".t-IRR-region").customReport(),kscope.keyboardShortcuts.defaultShortcuts()})}();
--------------------------------------------------------------------------------
/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "applications": [
3 | {
4 | "alias": "kscope",
5 | "modules": ["message", "keyboardShortcuts"],
6 | "widgets": ["customReport"]
7 | }
8 | ]
9 | }
10 |
--------------------------------------------------------------------------------
/dist/kscope/apex_pages/js/p2.min.js:
--------------------------------------------------------------------------------
1 | !function(){apex.jQuery(document).ready(function(){$("#MESSAGE").on("click",function(){kscope.message.info({title:"Hi Kscope",text:"Now I'm loaded by the page.js"})})})}();
--------------------------------------------------------------------------------
/dist/kscope/js/apex_pages/p2.min.js:
--------------------------------------------------------------------------------
1 | !function(){apex.jQuery(document).ready(function(){$("#MESSAGE").on("click",function(){kscope.message.info({title:"Hi Kscope",text:"Now I'm loaded by the page.js"})})})}();
--------------------------------------------------------------------------------
/src/js/applications/kscope/kscope.js:
--------------------------------------------------------------------------------
1 | apex.jQuery(document).ready(function() {
2 |
3 | // Make row clickable
4 | $('.t-IRR-region').customReport();
5 |
6 | // Set defaultShortcuts
7 | kscope.keyboardShortcuts.defaultShortcuts();
8 |
9 | });
10 |
--------------------------------------------------------------------------------
/dist/kscope/js/kscope.js:
--------------------------------------------------------------------------------
1 | (function localScope() {apex.jQuery(document).ready(function() {
2 |
3 | // Make row clickable
4 | $('.t-IRR-region').customReport();
5 |
6 | // Set defaultShortcuts
7 | kscope.keyboardShortcuts.defaultShortcuts();
8 |
9 | });
10 | })();
--------------------------------------------------------------------------------
/src/js/applications/kscope/apex_pages/p2.js:
--------------------------------------------------------------------------------
1 | apex.jQuery(document).ready(function(){
2 |
3 | // Show message on button click
4 | $('#MESSAGE').on('click', function() {
5 | kscope.message.info({
6 | title: "Hi Kscope",
7 | text: "Now I'm loaded by the page.js"
8 | });
9 | });
10 |
11 | });
12 |
--------------------------------------------------------------------------------
/dist/kscope/apex_pages/js/p2.js:
--------------------------------------------------------------------------------
1 | (function localScope() {apex.jQuery(document).ready(function(){
2 |
3 | // Show message on button click
4 | $('#MESSAGE').on('click', function() {
5 | kscope.message.info({
6 | title: "Hi Kscope",
7 | text: "Now I'm loaded by the page.js"
8 | });
9 | });
10 |
11 | });
12 | })();
--------------------------------------------------------------------------------
/dist/kscope/js/apex_pages/p2.js:
--------------------------------------------------------------------------------
1 | (function localScope() {apex.jQuery(document).ready(function(){
2 |
3 | // Show message on button click
4 | $('#MESSAGE').on('click', function() {
5 | kscope.message.info({
6 | title: "Hi Kscope",
7 | text: "Now I'm loaded by the page.js"
8 | });
9 | });
10 |
11 | });
12 | })();
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | var gulp = require('gulp');
2 | var requireDir = require('require-dir');
3 |
4 | // relative file paths
5 | require('app-module-path').addPath(__dirname);
6 |
7 | requireDir('gulp-tasks');
8 |
9 | gulp.task('bundle', ['bundle:watch']);
10 |
11 | gulp.task('scripts', ['scripts:watch']);
12 |
13 | gulp.task('start', ['bundle:bundleAll', 'scripts:apps', 'scripts:pages', 'deploy'])
14 |
15 | gulp.task('default', ['start', 'bundle', 'scripts']);
16 |
--------------------------------------------------------------------------------
/gulp-tasks/deploy.js:
--------------------------------------------------------------------------------
1 | var gulp = require('gulp');
2 | var notify = require('gulp-notify');
3 | var zip = require('gulp-zip');
4 |
5 | gulp.task('deploy', ['bundle:bundleAll'],function() {
6 | return gulp.src(['dist/**/*', '!dist/*.zip'], {
7 | base: 'dist'
8 | })
9 | .pipe(zip('kscope.zip'))
10 | .pipe(gulp.dest('dist'))
11 | /*
12 | In this sample we only create a zipfile to upload to APEX Static Application files manually
13 | You might want to use ftp to upload files to your webserver like this:
14 |
15 |
16 |
17 | */
18 | .pipe(notify('Please upload the file dist/kscope/kscope.zip to APEX Static Application Files in the sample application.'));
19 | });
20 |
--------------------------------------------------------------------------------
/src/js/modules/keyboardShortcuts.js:
--------------------------------------------------------------------------------
1 | var key = require("keymaster");
2 | var message = require("modules/message");
3 |
4 | // Add shortcut coe
5 | var _addShortcut = function(selectedKey,cb) {
6 | key(selectedKey, cb);
7 | }
8 |
9 | var _clickButton = function(selector) {
10 | $(selector).trigger('click');
11 | return false;
12 | }
13 |
14 | // Add shortcuts for selectors
15 | var defaultShortcuts = function() {
16 | _addShortcut('ctrl+s', function(event,handler){
17 | _clickButton('.t-Button[onclick*="\'CREATE\'"],.t-Button#CREATE');
18 | });
19 | _addShortcut('ctrl+s', function(event,handler){
20 | _clickButton('.t-Button[onclick*="\'SAVE\'"],.t-Button#SAVE');
21 | });
22 | _addShortcut('ctrl+d', function(event,handler){
23 | _clickButton('.t-Button[onclick*="\'DELETE\'"],.t-Button#DELETE');
24 | });
25 | _addShortcut('ctrl+m', function(event,handler){
26 | message.info({
27 | title: "A key was pressed",
28 | text: "ctrl+m"
29 | });
30 | });
31 | };
32 |
33 | module.exports = {
34 | defaultShortcuts: defaultShortcuts
35 | }
36 |
--------------------------------------------------------------------------------
/src/js/modules/message.js:
--------------------------------------------------------------------------------
1 | var $ = require("jquery"); // being shimmed
2 | var PNotify = require("pnotify");
3 |
4 |
5 | // Button defaults
6 | var defaults = {
7 | hide: false,
8 | closer: true,
9 | buttons: {
10 | closer_hover: false,
11 | sticker: false,
12 | labels: {
13 | close: 'Sluit melding'
14 | }
15 | }
16 | };
17 |
18 |
19 | var getSettings = function(options) {
20 |
21 | switch (typeof options) {
22 | case "string":
23 | return $.extend({
24 | title: options
25 | }, defaults);
26 | case "object":
27 | return $.extend(options, defaults);
28 | default:
29 | return defaults;
30 | };
31 |
32 | };
33 |
34 | var info = function(options) {
35 | return new PNotify($.extend(getSettings(options), {
36 | type: 'info',
37 | hide: true
38 | }));
39 | };
40 |
41 | var success = function(options) {
42 | return new PNotify($.extend(getSettings(options), {
43 | type: 'success',
44 | hide: true
45 | }));
46 | };
47 |
48 | var warning = function(options) {
49 | return new PNotify($.extend(getSettings(options), {
50 | type: 'warning'
51 | }));
52 | };
53 |
54 | var error = function(options) {
55 | return new PNotify($.extend(getSettings(options), {
56 | type: 'error'
57 | }));
58 | };
59 |
60 | module.exports = {
61 | info: info,
62 | success: success,
63 | warning: warning,
64 | error: error
65 | };
66 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "apex-modular-js",
3 | "version": "1.0.0",
4 | "description": "Using modular JavaScript with Oracle Application Express",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "gulp"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "git+https://github.com/mennooo/apex-modular-js.git"
12 | },
13 | "keywords": [
14 | "orclapex"
15 | ],
16 | "author": "M.Hoogendijk (iAdvise)",
17 | "license": "ISC",
18 | "devDependencies": {
19 | "app-module-path": "^1.0.6",
20 | "async": "^1.5.2",
21 | "browserify": "^13.0.1",
22 | "browserify-global-shim": "^1.0.3",
23 | "browserify-shim": "^3.8.12",
24 | "event-stream": "^3.3.2",
25 | "gulp": "^3.9.1",
26 | "gulp-filesize": "0.0.6",
27 | "gulp-insert": "^0.5.0",
28 | "gulp-notify": "^2.2.0",
29 | "gulp-plumber": "^1.1.0",
30 | "gulp-rename": "^1.2.2",
31 | "gulp-sourcemaps": "^1.6.0",
32 | "gulp-uglify": "^1.5.3",
33 | "gulp-util": "^3.0.7",
34 | "gulp-watch": "^4.3.5",
35 | "gulp-zip": "^3.2.0",
36 | "lodash.assign": "^4.0.8",
37 | "remapify": "^2.1.0",
38 | "require-dir": "^0.3.0",
39 | "require-reload": "^0.2.2",
40 | "underscore": "^1.8.3",
41 | "vinyl": "^1.1.1",
42 | "vinyl-buffer": "^1.0.0",
43 | "vinyl-source-stream": "^1.1.0",
44 | "watchify": "^3.7.0"
45 | },
46 | "dependencies": {
47 | "keymaster": "^1.6.2",
48 | "pnotify": "^3.0.0"
49 | },
50 | "bugs": {
51 | "url": "https://github.com/mennooo/apex-modular-js/issues"
52 | },
53 | "homepage": "https://github.com/mennooo/apex-modular-js#readme"
54 | }
55 |
--------------------------------------------------------------------------------
/gulp-tasks/scripts.js:
--------------------------------------------------------------------------------
1 | var gulp = require('gulp');
2 | var gutil = require('gulp-util');
3 | var uglify = require('gulp-uglify');
4 | var rename = require('gulp-rename');
5 | var plumber = require('gulp-plumber');
6 | var insert = require('gulp-insert');
7 |
8 |
9 | // relative file paths
10 | require('app-module-path').addPath(__dirname);
11 |
12 |
13 | var onError = function(error) {
14 | gutil.log(error);
15 | this.emit('end');
16 | }
17 |
18 | var jsConfig = {
19 | base: 'src/js/applications',
20 | build: 'dist/',
21 | apps: 'src/js/applications/*/*.js',
22 | pages: 'src/js/applications/*/apex_pages/*.js'
23 | };
24 |
25 | // Two kinds of tasks: original JavaScript or minified JavaScript
26 | function execTask(options) {
27 |
28 | return gulp.src([
29 | options.source
30 | ], {
31 | base: jsConfig.base
32 | })
33 | .pipe(plumber({
34 | errorHandler: onError
35 | }))
36 | .pipe(insert.wrap('(function localScope() {', '})();')) // create local scope for these files
37 | .pipe(rename(function(path){
38 | // add js folder in dirname
39 | path.dirname = function(){
40 | var slugs = path.dirname.split('/');
41 | slugs.splice(1, 0, 'js');
42 | return slugs.join('/');
43 | }();
44 | }))
45 | .pipe(gulp.dest(jsConfig.build))
46 | .pipe(uglify())
47 | .pipe(rename({
48 | suffix: '.min'
49 | }))
50 | .pipe(gulp.dest(jsConfig.build));
51 |
52 | };
53 |
54 | gulp.task('scripts:apps', function() {
55 |
56 | return execTask({
57 | source: jsConfig.apps
58 | });
59 |
60 | });
61 |
62 | gulp.task('scripts:pages', function() {
63 |
64 | return execTask({
65 | source: jsConfig.pages
66 | });
67 |
68 | });
69 |
70 | gulp.task('scripts:watch', function() {
71 |
72 | gulp.watch(jsConfig.apps, ['scripts:apps', 'deploy']);
73 | gulp.watch(jsConfig.pages, ['scripts:pages', 'deploy']);
74 |
75 | });
76 |
--------------------------------------------------------------------------------
/src/js/widgets/customReport.js:
--------------------------------------------------------------------------------
1 | $.widget("custom.customReport", {
2 | options: {
3 | exceptClass: 'no-row-link',
4 | activeClass: 'active',
5 | columns: [],
6 | rowclick: function(e, data) {
7 | $(this).customReport('openLink', e);
8 | }
9 | },
10 |
11 | _create: function() {
12 | var self = this;
13 | // Check if report may have a row link
14 | if (this._rowLinkAllowed) {
15 | this._initRowClick();
16 | }
17 |
18 | $(this.element).on('apexafterrefresh',function(e){
19 | self._apexafterrefresh();
20 | });
21 |
22 | },
23 |
24 | _apexafterrefresh: function() {
25 | // Check if report may have a row link
26 | if (this._rowLinkAllowed) {
27 | this._initRowClick();
28 | }
29 | },
30 |
31 | _initRowClick: function(cb) {
32 |
33 | var self = this;
34 | var data;
35 | cb = $.proxy(cb, self);
36 |
37 | // Remove previous handlers
38 | this._off(this.element, 'click tr td:not(:has(a))');
39 | this._off(this.element, 'hover tr td:not(:has(a))');
40 |
41 | // Add new handler
42 | this._on(this.element, {
43 | 'mouseenter tr td:not(:has(a))': function(e) {
44 | $(e.target).css('cursor', 'pointer');
45 | }
46 | });
47 |
48 | // Add new handler
49 | this._on(this.element, {
50 | 'click tr td:not(:has(a))': function(e) {
51 | self._trigger('rowclick', e, data);
52 | e.stopImmediatePropagation();
53 | }
54 | });
55 |
56 | },
57 |
58 | // Use an a href value to redirect on row click in report
59 | openLink: function(event, options) {
60 |
61 | var self = this;
62 |
63 | var defaults = {
64 | aPos: 0 // which "a" element contains the link
65 | }
66 |
67 | var settings = $.extend(options, defaults);
68 |
69 | // Get link
70 | var $linkElem = self._getLinkElement(settings, event.currentTarget);
71 | var href = $linkElem.attr('href');
72 |
73 | if (href != undefined) {
74 | window.location.href = href;
75 | }
76 | },
77 |
78 | _getLinkElement: function(options, target) {
79 |
80 | var links = $(target).closest('tr').find('td:has(a)');
81 |
82 | // Raise exception if position of a element is invalid
83 | if (links.length < options.aPos) {
84 | apex.debug.error('Exception: ', options.aPos + 'th "a" element is not found in report row.');
85 | } else {
86 | return $(links[options.aPos]).find('a');
87 | }
88 |
89 | },
90 |
91 | // _rowLinkAllowed returns boolean
92 | _rowLinkAllowed: function() {
93 | return !$(this.element).hasClass(this.options.exceptClass);
94 | },
95 |
96 | _setActiveRow: function($row) {
97 | $($row).closest('table').find('td.'+this.options.activeClass).removeClass(this.options.activeClass);
98 | $($row).find('td').addClass(this.options.activeClass);
99 | },
100 |
101 | activeRow: function() {
102 | return $(this.element).find('td.'+this.options.activeClass).closest('tr');
103 | }
104 |
105 | });
106 |
--------------------------------------------------------------------------------
/gulp-tasks/bundle.js:
--------------------------------------------------------------------------------
1 | var gulp = require('gulp');
2 | var gutil = require('gulp-util');
3 | var browserify = require('browserify');
4 | var assign = require('lodash.assign');
5 | var remapify = require('remapify');
6 | var File = require("vinyl");
7 | var buffer = require('vinyl-buffer');
8 | var plumber = require('gulp-plumber');
9 | var source = require('vinyl-source-stream');
10 | var sourcemaps = require('gulp-sourcemaps');
11 | var rename = require('gulp-rename');
12 | var _ = require('underscore');
13 | var uglify = require('gulp-uglify');
14 | var async = require('async');
15 | var es = require('event-stream');
16 | var reload = require('require-reload')(require);
17 | var config = reload('config.json');
18 |
19 | // relative file paths
20 | require('app-module-path').addPath(__dirname);
21 |
22 | // jQuery shim
23 | var globalShim = require('browserify-global-shim').configure({
24 | 'jQuery': '$',
25 | 'jquery': '$'
26 | });
27 |
28 | var onError = function(error) {
29 | gutil.log(error);
30 | this.emit('end');
31 | }
32 |
33 | var bundleTask = function(app, resolve, reject) {
34 |
35 | gutil.log(app.alias, 'requires these modules: ', app.modules);
36 |
37 | // add custom browserify options here
38 | var customOpts = {
39 | debug: true
40 | };
41 |
42 | // Create new file to add modules & widgets to namespace on global scope
43 | var dynamicJs = [
44 | "var " + app.alias + " = {};",
45 | "window." + app.alias + " = " + app.alias + ";"
46 | ];
47 |
48 | // Add modules to new file
49 | app.modules.forEach(function(module) {
50 | dynamicJs.push(app.alias + "." + module + " = require('modules/"+ module + "');");
51 | });
52 |
53 | // Add widgets to new file
54 | app.widgets.forEach(function(widget) {
55 | dynamicJs.push("require('widgets/" + widget + "');");
56 | });
57 |
58 | gutil.log(dynamicJs.join(''));
59 |
60 | var temp = new File({
61 | contents: new Buffer(dynamicJs.join(''))
62 | });
63 |
64 | var b = browserify(temp, customOpts);
65 |
66 | // shims
67 | b.transform(globalShim, {
68 | global: true
69 | });
70 |
71 | // Browserify plugins
72 | b.plugin(remapify, [{
73 | cwd: 'src/js/modules', // set the directory to look in
74 | src: '*.js', // glob for the files to remap
75 | expose: 'modules'
76 | }, {
77 | cwd: 'src/js/widgets', // set the directory to look in
78 | src: '*.js', // glob for the files to remap
79 | expose: 'widgets'
80 | }]);
81 |
82 | return b.bundle()
83 | // log errors if they happen
84 | .on('error', onError)
85 | .pipe(plumber({
86 | errorHandler: onError
87 | }))
88 | .pipe(source('bundle.js'))
89 | .pipe(buffer())
90 | .pipe(sourcemaps.write('./'))
91 | .pipe(gulp.dest('dist/' + app.alias + '/js'))
92 | .pipe(uglify())
93 | .pipe(rename({
94 | suffix: '.min'
95 | }))
96 | .pipe(gulp.dest('dist/' + app.alias + '/js'))
97 | .on('end', function() {
98 | resolve();
99 | });
100 |
101 | };
102 |
103 | gulp.task('bundle:bundleAll', function() {
104 |
105 | config = reload('config.json');
106 |
107 | var tasks = config.applications.map(function(app) {
108 | return new Promise(function(resolve, reject) {
109 | bundleTask(app, resolve, reject);
110 | })
111 | })
112 |
113 | // Return bundled streams after all bundles have been created
114 | return Promise.all(tasks).then(function(results) {
115 | }).catch(function(err) {
116 | console.log(err);
117 | });
118 |
119 | });
120 |
121 |
122 |
123 | //moduleTasks
124 | gulp.task('bundle:watch', function() {
125 | gulp.watch(['src/js/modules/*.js', 'src/js/widgets/*.js', 'config.json'], ['bundle:bundleAll', 'deploy']);
126 | });
127 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # apex-modular-js
2 | Using modular JavaScript with Oracle Application Express
3 |
4 | This project is created for sample purposes only. It demonstrates how to create and use reusable JavaScript modules & widgets in an APEX application. It's developed with a combination of Node.js, Gulp and Browserify.
5 |
6 | The project was presented at the Kscope16 conference.
7 |
8 | Demo: https://apex.oracle.com/pls/apex/f?p=78682
9 |
10 | Blogpost: [https://blog.iadvise.eu/2016/08/12/apex-javascript-the-modular-way/](https://blog.iadvise.eu/2016/08/12/apex-javascript-the-modular-way/)
11 |
12 | Take a look at all the files to see how it works.
13 |
14 | ## System Requirements
15 | - [Node.js](https://nodejs.org)
16 | - [Oracle APEX](https://apex.oracle.com)
17 | - [Git](https://git-scm.com/downloads)
18 |
19 | ## Install this project
20 | - Open the command line
21 | - Go to your desired directory
22 | - Execute
23 | ```bash
24 | git clone https://github.com/mennooo/apex-modular-js.git
25 | cd apex-modular-js
26 | npm install
27 | ```
28 |
29 | ## Install APEX Sample application
30 | You can import the demo application in your APEX workspace. It works immediate without any further actions and is located at:
31 | ```
32 | apex/f103.sql
33 | ```
34 |
35 | ## Run
36 | **On the command line:**
37 | ```bash
38 | npm start
39 | ```
40 | ## Usage
41 | You can change the content of the JavaScript files to see how it affects the sample application.
42 | From the `src` folder you can create, edit or delete any files in:
43 | ```
44 | |-/src/
45 | |-js
46 | |-applications
47 | |-kscope
48 | |-modules
49 | |-widgets
50 | ```
51 | As you can see the `modules` and `widgets` folders are not placed inside the `applications` folder because we want to be able to use them in multiple applications.
52 | New modules are not added to your application immediate. If you want to include any module or widget in the sample application then change the following config file:
53 | ```
54 | config.json
55 | ```
56 | The names of modules and widgets must be the same as the corresponding `.js` files.
57 |
58 | Saving changes to file in the `src` folder will automatically compile your files to this folder structure:
59 | ```
60 | |-/dist/
61 | |-kscope
62 | ```
63 |
64 | A zip file called `kscope.zip` will be created every time you save a change:
65 | ```
66 | dist/kscope/kscope.zip
67 | ```
68 | Please upload the file `dist/kscope/kscope.zip` to APEX **Static Application Files** in the sample application. At this point you can use the new code in the application.
69 |
70 | In a real world project you can tell Gulp to upload the content of the dist folder to your webserver via sftp (most likely /i/ folder).
71 |
72 | ## About the APEX sample application
73 |
74 | The application includes 4 examples about using the Modular JavaScript approach. It's also available on apex.oracle.com:
75 | https://apex.oracle.com/pls/apex/f?p=78682
76 |
77 | It's based around integrating the pNotify plugin.
78 |
79 | Examples:
80 | - 1: Load nessecary files in page, put code inside dynamic action (not recommended)
81 | - 2: Encapsulate pNotify in kscope.message module, put code in p2.js file (a bit better)
82 | - 3: Encapsulate kscope.message module in dynamic action plugin, add dynamic action to page 3 (preferred solution)
83 | - 4: Encapsulate kscope.message module inside keyboardShortcuts module (advanced usage)
84 |
85 | In example 4, it's not longer needed to include the message module in the `config.json` because it will be bundled as part of the keyboardShortcuts module. Feel free to test this out.
86 |
87 | The JavaScript files from this project are added under **User Interface Details** in the **JavaScript File URLs**. The filenames are **Substitution Variables** defined in the **Application Definition**.
88 |
89 | ## How the project works
90 |
91 | ### Bundling all modules and widgets
92 |
93 | It's developed to demonstrate how to bundle required modules and widgets into a single file to add to your APEX application.
94 | A module dependency loader loads required client side modules as part of the module. In this case a module dependency loader called Browserify was used.
95 |
96 | For instance:
97 | - The module message.js requires jQuery and pNotify in order to work when bundled.
98 | - The module keyboardShortcuts.js requires keymaster and our message module in order to work when bundled.
99 |
100 | The external plugins are preinstalled and they are available as NPM packages. The installation was done as follows.
101 | Go to the root folder of this project and execute these commands:
102 | ```
103 | npm install --save pnotify
104 | npm install --save keymaster
105 | ```
106 |
107 | If you would like to add other external plugins, make sure they are available is NPM package at npmjs.com.
108 |
109 | The tasks to bundle the modules and widgets and place all files for deployment in the `dist` folder is done via an automated task runner called Gulp.
110 |
111 | Gulp itself is a Node.js module which works well together with Browserify. Feel free to take a look at the Gulp config in:
112 | ```
113 | gulpfile.js
114 | gulp-tasks/bundle.js
115 | gulp-tasks/deploy.js
116 | gulp-tasks/scripts.js
117 | ```
118 | I had to tweak the Browserify task in Gulp a little bit because Browserify typically creates one big bundle where all functions are only accessible within the file. In APEX we need to access the modules from dynamic actions and other external JavaScript files, so I've attached the modules and widgets to a global namespace called `kscope`.
119 |
--------------------------------------------------------------------------------
/dist/kscope/js/bundle.min.js:
--------------------------------------------------------------------------------
1 | !function t(i,e,o){function n(r,a){if(!e[r]){if(!i[r]){var c="function"==typeof require&&require;if(!a&&c)return c(r,!0);if(s)return s(r,!0);var h=new Error("Cannot find module '"+r+"'");throw h.code="MODULE_NOT_FOUND",h}var l=e[r]={exports:{}};i[r][0].call(l.exports,function(t){var e=i[r][1][t];return n(e?e:t)},l,l.exports,t,i,e,o)}return e[r].exports}for(var s="function"==typeof require&&require,r=0;r0;for(s in k)(!k[s]&&e(o.mods,+s)>-1||k[s]&&e(o.mods,+s)==-1)&&(a=!1);(0!=o.mods.length||k[16]||k[18]||k[17]||k[91])&&!a||o.method(t,o)===!1&&(t.preventDefault?t.preventDefault():t.returnValue=!1,t.stopPropagation&&t.stopPropagation(),t.cancelBubble&&(t.cancelBubble=!0))}}function r(t){var i,o=t.keyCode,n=e(R,o);if(n>=0&&R.splice(n,1),93!=o&&224!=o||(o=91),o in k){k[o]=!1;for(i in C)C[i]==o&&(c[i]=!1)}}function a(){for(w in k)k[w]=!1;for(w in C)c[w]=!1}function c(t,i,e){var o,n;o=y(t),void 0===e&&(e=i,i="all");for(var s=0;s1&&(n=g(t),t=[t[t.length-1]]),t=t[0],t=E(t),t in _||(_[t]=[]),_[t].push({shortcut:o[s],scope:i,method:e,key:o[s],mods:n})}function h(t,i){var e,n,s,r,a,c=[];for(e=y(t),r=0;r1&&(c=g(n),t=n[n.length-1]),t=E(t),void 0===i&&(i=f()),!_[t])return;for(s=0;s<_[t].length;s++)a=_[t][s],a.scope===i&&o(a.mods,c)&&(_[t][s]={})}}function l(t){return"string"==typeof t&&(t=E(t)),e(R,t)!=-1}function p(){return R.slice(0)}function u(t){var i=(t.target||t.srcElement).tagName;return!("INPUT"==i||"SELECT"==i||"TEXTAREA"==i)}function d(t){b=t||"all"}function f(){return b||"all"}function m(t){var i,e,o;for(i in _)for(e=_[i],o=0;o",{"class":"ui-pnotify-modal-overlay"});return e.prependTo(i.context),i.overlay_close&&e.click(function(){h.removeStack(i)}),e},h=function(t){this.parseOptions(t),this.init()};return t.extend(h.prototype,{version:"3.0.0",options:{title:!1,title_escape:!1,text:!1,text_escape:!1,styling:"brighttheme",addclass:"",cornerclass:"",auto_display:!0,width:"300px",min_height:"16px",type:"notice",icon:!0,animation:"fade",animate_speed:"normal",shadow:!0,hide:!0,delay:8e3,mouse_reset:!0,remove:!0,insert_brs:!0,destroy:!0,stack:s},modules:{},runModules:function(t,i){var e,o;for(o in this.modules)e="object"==typeof i&&o in i?i[o]:i,"function"==typeof this.modules[o][t]&&(this.modules[o].notice=this,this.modules[o].options="object"==typeof this.options[o]?this.options[o]:{},this.modules[o][t](this,"object"==typeof this.options[o]?this.options[o]:{},e))},state:"initializing",timer:null,animTimer:null,styles:null,elem:null,container:null,title_container:null,text_container:null,animating:!1,timerHide:!1,init:function(){var i=this;return this.modules={},t.extend(!0,this.modules,h.prototype.modules),this.styles="object"==typeof this.options.styling?this.options.styling:h.styling[this.options.styling],this.elem=t("",{"class":"ui-pnotify "+this.options.addclass,css:{display:"none"},"aria-live":"assertive","aria-role":"alertdialog",mouseenter:function(t){if(i.options.mouse_reset&&"out"===i.animating){if(!i.timerHide)return;i.cancelRemove()}i.options.hide&&i.options.mouse_reset&&i.cancelRemove()},mouseleave:function(t){i.options.hide&&i.options.mouse_reset&&"out"!==i.animating&&i.queueRemove(),h.positionAll()}}),"fade"===this.options.animation&&this.elem.addClass("ui-pnotify-fade-"+this.options.animate_speed),this.container=t("",{"class":this.styles.container+" ui-pnotify-container "+("error"===this.options.type?this.styles.error:"info"===this.options.type?this.styles.info:"success"===this.options.type?this.styles.success:this.styles.notice),role:"alert"}).appendTo(this.elem),""!==this.options.cornerclass&&this.container.removeClass("ui-corner-all").addClass(this.options.cornerclass),this.options.shadow&&this.container.addClass("ui-pnotify-shadow"),!1!==this.options.icon&&t("",{"class":"ui-pnotify-icon"}).append(t("",{"class":!0===this.options.icon?"error"===this.options.type?this.styles.error_icon:"info"===this.options.type?this.styles.info_icon:"success"===this.options.type?this.styles.success_icon:this.styles.notice_icon:this.options.icon})).prependTo(this.container),this.title_container=t("",{"class":"ui-pnotify-title"}).appendTo(this.container),!1===this.options.title?this.title_container.hide():this.options.title_escape?this.title_container.text(this.options.title):this.title_container.html(this.options.title),this.text_container=t("",{"class":"ui-pnotify-text","aria-role":"alert"}).appendTo(this.container),!1===this.options.text?this.text_container.hide():this.options.text_escape?this.text_container.text(this.options.text):this.text_container.html(this.options.insert_brs?String(this.options.text).replace(/\n/g,"
"):this.options.text),"string"==typeof this.options.width&&this.elem.css("width",this.options.width),"string"==typeof this.options.min_height&&this.container.css("min-height",this.options.min_height),h.notices="top"===this.options.stack.push?t.merge([this],h.notices):t.merge(h.notices,[this]),"top"===this.options.stack.push&&this.queuePosition(!1,1),this.options.stack.animation=!1,this.runModules("init"),this.options.auto_display&&this.open(),this},update:function(i){var e=this.options;return this.parseOptions(e,i),this.elem.removeClass("ui-pnotify-fade-slow ui-pnotify-fade-normal ui-pnotify-fade-fast"),"fade"===this.options.animation&&this.elem.addClass("ui-pnotify-fade-"+this.options.animate_speed),this.options.cornerclass!==e.cornerclass&&this.container.removeClass("ui-corner-all "+e.cornerclass).addClass(this.options.cornerclass),this.options.shadow!==e.shadow&&(this.options.shadow?this.container.addClass("ui-pnotify-shadow"):this.container.removeClass("ui-pnotify-shadow")),!1===this.options.addclass?this.elem.removeClass(e.addclass):this.options.addclass!==e.addclass&&this.elem.removeClass(e.addclass).addClass(this.options.addclass),!1===this.options.title?this.title_container.slideUp("fast"):this.options.title!==e.title&&(this.options.title_escape?this.title_container.text(this.options.title):this.title_container.html(this.options.title),!1===e.title&&this.title_container.slideDown(200)),!1===this.options.text?this.text_container.slideUp("fast"):this.options.text!==e.text&&(this.options.text_escape?this.text_container.text(this.options.text):this.text_container.html(this.options.insert_brs?String(this.options.text).replace(/\n/g,"
"):this.options.text),!1===e.text&&this.text_container.slideDown(200)),this.options.type!==e.type&&this.container.removeClass(this.styles.error+" "+this.styles.notice+" "+this.styles.success+" "+this.styles.info).addClass("error"===this.options.type?this.styles.error:"info"===this.options.type?this.styles.info:"success"===this.options.type?this.styles.success:this.styles.notice),(this.options.icon!==e.icon||!0===this.options.icon&&this.options.type!==e.type)&&(this.container.find("div.ui-pnotify-icon").remove(),!1!==this.options.icon&&t("",{"class":"ui-pnotify-icon"}).append(t("",{"class":!0===this.options.icon?"error"===this.options.type?this.styles.error_icon:"info"===this.options.type?this.styles.info_icon:"success"===this.options.type?this.styles.success_icon:this.styles.notice_icon:this.options.icon})).prependTo(this.container)),this.options.width!==e.width&&this.elem.animate({width:this.options.width}),this.options.min_height!==e.min_height&&this.container.animate({minHeight:this.options.min_height}),this.options.hide?e.hide||this.queueRemove():this.cancelRemove(),this.queuePosition(!0),this.runModules("update",e),this},open:function(){this.state="opening",this.runModules("beforeOpen");var t=this;return this.elem.parent().length||this.elem.appendTo(this.options.stack.context?this.options.stack.context:n),"top"!==this.options.stack.push&&this.position(!0),this.animateIn(function(){t.queuePosition(!0),t.options.hide&&t.queueRemove(),t.state="open",t.runModules("afterOpen")}),this},remove:function(e){this.state="closing",this.timerHide=!!e,this.runModules("beforeClose");var o=this;return this.timer&&(i.clearTimeout(this.timer),this.timer=null),this.animateOut(function(){if(o.state="closed",o.runModules("afterClose"),o.queuePosition(!0),o.options.remove&&o.elem.detach(),o.runModules("beforeDestroy"),o.options.destroy&&null!==h.notices){var i=t.inArray(o,h.notices);-1!==i&&h.notices.splice(i,1)}o.runModules("afterDestroy")}),this},get:function(){return this.elem},parseOptions:function(i,e){this.options=t.extend(!0,{},h.prototype.options),this.options.stack=h.prototype.options.stack;for(var o,n=[i,e],s=0;s(i.context.is(n)?r.height():i.context.prop("scrollHeight"))||"up"===i.dir1&&i.nextpos1+e.height()>(i.context.is(n)?r.height():i.context.prop("scrollHeight"))||"left"===i.dir1&&i.nextpos1+e.width()>(i.context.is(n)?r.width():i.context.prop("scrollWidth"))||"right"===i.dir1&&i.nextpos1+e.width()>(i.context.is(n)?r.width():i.context.prop("scrollWidth")))&&(i.nextpos1=i.firstpos1,i.nextpos2+=i.addpos2+("undefined"==typeof i.spacing2?25:i.spacing2),i.addpos2=0),"number"==typeof i.nextpos2&&(i.animation?e.css(a,i.nextpos2+"px"):(e.removeClass("ui-pnotify-move"),e.css(a,i.nextpos2+"px"),e.css(a),e.addClass("ui-pnotify-move"))),i.dir2){case"down":case"up":e.outerHeight(!0)>i.addpos2&&(i.addpos2=e.height());break;case"left":case"right":e.outerWidth(!0)>i.addpos2&&(i.addpos2=e.width())}switch("number"==typeof i.nextpos1&&(i.animation?e.css(s,i.nextpos1+"px"):(e.removeClass("ui-pnotify-move"),e.css(s,i.nextpos1+"px"),e.css(s),e.addClass("ui-pnotify-move"))),i.dir1){case"down":case"up":i.nextpos1+=e.height()+("undefined"==typeof i.spacing1?25:i.spacing1);break;case"left":case"right":i.nextpos1+=e.width()+("undefined"==typeof i.spacing1?25:i.spacing1)}}return this}},queuePosition:function(t,i){return o&&clearTimeout(o),i||(i=10),o=setTimeout(function(){h.positionAll(t)},i),this},cancelRemove:function(){return this.timer&&i.clearTimeout(this.timer),this.animTimer&&i.clearTimeout(this.animTimer),"closing"===this.state&&(this.state="open",this.animating=!1,this.elem.addClass("ui-pnotify-in"),"fade"===this.options.animation&&this.elem.addClass("ui-pnotify-fade-in")),this},queueRemove:function(){var t=this;return this.cancelRemove(),this.timer=i.setTimeout(function(){t.remove(!0)},isNaN(this.options.delay)?0:this.options.delay),this}}),t.extend(h,{notices:[],reload:e,removeAll:function(){t.each(h.notices,function(){this.remove&&this.remove(!1)})},removeStack:function(i){t.each(h.notices,function(){this.remove&&this.options.stack===i&&this.remove(!1)})},positionAll:function(i){if(o&&clearTimeout(o),o=null,h.notices&&h.notices.length)t.each(h.notices,function(){var t=this.options.stack;t&&(t.overlay&&t.overlay.hide(),t.nextpos1=t.firstpos1,t.nextpos2=t.firstpos2,t.addpos2=0,t.animation=i)}),t.each(h.notices,function(){this.position()});else{var e=h.prototype.options.stack;e&&(delete e.nextpos1,delete e.nextpos2)}},styling:{brighttheme:{container:"brighttheme",notice:"brighttheme-notice",notice_icon:"brighttheme-icon-notice",info:"brighttheme-info",info_icon:"brighttheme-icon-info",success:"brighttheme-success",success_icon:"brighttheme-icon-success",error:"brighttheme-error",error_icon:"brighttheme-icon-error"},jqueryui:{container:"ui-widget ui-widget-content ui-corner-all",notice:"ui-state-highlight",notice_icon:"ui-icon ui-icon-info",info:"",info_icon:"ui-icon ui-icon-info",success:"ui-state-default",success_icon:"ui-icon ui-icon-circle-check",error:"ui-state-error",error_icon:"ui-icon ui-icon-alert"},bootstrap3:{container:"alert",notice:"alert-warning",notice_icon:"glyphicon glyphicon-exclamation-sign",info:"alert-info",info_icon:"glyphicon glyphicon-info-sign",success:"alert-success",success_icon:"glyphicon glyphicon-ok-sign",error:"alert-danger",error_icon:"glyphicon glyphicon-warning-sign"}}}),h.styling.fontawesome=t.extend({},h.styling.bootstrap3),t.extend(h.styling.fontawesome,{notice_icon:"fa fa-exclamation-circle",info_icon:"fa fa-info",success_icon:"fa fa-check",error_icon:"fa fa-warning"}),i.document.body?a():t(a),h};return e(i)})}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}],4:[function(t,i,e){var o=t("keymaster"),n=t("modules/message"),s=function(t,i){o(t,i)},r=function(t){return $(t).trigger("click"),!1},a=function(){s("ctrl+s",function(t,i){r(".t-Button[onclick*=\"'CREATE'\"],.t-Button#CREATE")}),s("ctrl+s",function(t,i){r(".t-Button[onclick*=\"'SAVE'\"],.t-Button#SAVE")}),s("ctrl+d",function(t,i){r(".t-Button[onclick*=\"'DELETE'\"],.t-Button#DELETE")}),s("ctrl+m",function(t,i){n.info({title:"A key was pressed",text:"ctrl+m"})})};i.exports={defaultShortcuts:a}},{keymaster:2,"modules/message":5}],5:[function(t,i,e){var o=window.$,n=t("pnotify"),s={hide:!1,closer:!0,buttons:{closer_hover:!1,sticker:!1,labels:{close:"Sluit melding"}}},r=function(t){switch(typeof t){case"string":return o.extend({title:t},s);case"object":return o.extend(t,s);default:return s}},a=function(t){return new n(o.extend(r(t),{type:"info",hide:!0}))},c=function(t){return new n(o.extend(r(t),{type:"success",hide:!0}))},h=function(t){return new n(o.extend(r(t),{type:"warning"}))},l=function(t){return new n(o.extend(r(t),{type:"error"}))};i.exports={info:a,success:c,warning:h,error:l}},{pnotify:3}],6:[function(t,i,e){$.widget("custom.customReport",{options:{exceptClass:"no-row-link",activeClass:"active",columns:[],rowclick:function(t,i){$(this).customReport("openLink",t)}},_create:function(){var t=this;this._rowLinkAllowed&&this._initRowClick(),$(this.element).on("apexafterrefresh",function(i){t._apexafterrefresh()})},_apexafterrefresh:function(){this._rowLinkAllowed&&this._initRowClick()},_initRowClick:function(t){var i,e=this;t=$.proxy(t,e),this._off(this.element,"click tr td:not(:has(a))"),this._off(this.element,"hover tr td:not(:has(a))"),this._on(this.element,{"mouseenter tr td:not(:has(a))":function(t){$(t.target).css("cursor","pointer")}}),this._on(this.element,{"click tr td:not(:has(a))":function(t){e._trigger("rowclick",t,i),t.stopImmediatePropagation()}})},openLink:function(t,i){var e=this,o={aPos:0},n=$.extend(i,o),s=e._getLinkElement(n,t.currentTarget),r=s.attr("href");void 0!=r&&(window.location.href=r)},_getLinkElement:function(t,i){var e=$(i).closest("tr").find("td:has(a)");return e.length property to true and return
78 | if(key == 93 || key == 224) key = 91; // right command on webkit, command on Gecko
79 | if(key in _mods) {
80 | _mods[key] = true;
81 | // 'assignKey' from inside this closure is exported to window.key
82 | for(k in _MODIFIERS) if(_MODIFIERS[k] == key) assignKey[k] = true;
83 | return;
84 | }
85 | updateModifierKey(event);
86 |
87 | // see if we need to ignore the keypress (filter() can can be overridden)
88 | // by default ignore key presses if a select, textarea, or input is focused
89 | if(!assignKey.filter.call(this, event)) return;
90 |
91 | // abort if no potentially matching shortcuts found
92 | if (!(key in _handlers)) return;
93 |
94 | scope = getScope();
95 |
96 | // for each potential shortcut
97 | for (i = 0; i < _handlers[key].length; i++) {
98 | handler = _handlers[key][i];
99 |
100 | // see if it's in the current scope
101 | if(handler.scope == scope || handler.scope == 'all'){
102 | // check if modifiers match if any
103 | modifiersMatch = handler.mods.length > 0;
104 | for(k in _mods)
105 | if((!_mods[k] && index(handler.mods, +k) > -1) ||
106 | (_mods[k] && index(handler.mods, +k) == -1)) modifiersMatch = false;
107 | // call the handler and stop the event if neccessary
108 | if((handler.mods.length == 0 && !_mods[16] && !_mods[18] && !_mods[17] && !_mods[91]) || modifiersMatch){
109 | if(handler.method(event, handler)===false){
110 | if(event.preventDefault) event.preventDefault();
111 | else event.returnValue = false;
112 | if(event.stopPropagation) event.stopPropagation();
113 | if(event.cancelBubble) event.cancelBubble = true;
114 | }
115 | }
116 | }
117 | }
118 | };
119 |
120 | // unset modifier keys on keyup
121 | function clearModifier(event){
122 | var key = event.keyCode, k,
123 | i = index(_downKeys, key);
124 |
125 | // remove key from _downKeys
126 | if (i >= 0) {
127 | _downKeys.splice(i, 1);
128 | }
129 |
130 | if(key == 93 || key == 224) key = 91;
131 | if(key in _mods) {
132 | _mods[key] = false;
133 | for(k in _MODIFIERS) if(_MODIFIERS[k] == key) assignKey[k] = false;
134 | }
135 | };
136 |
137 | function resetModifiers() {
138 | for(k in _mods) _mods[k] = false;
139 | for(k in _MODIFIERS) assignKey[k] = false;
140 | };
141 |
142 | // parse and assign shortcut
143 | function assignKey(key, scope, method){
144 | var keys, mods;
145 | keys = getKeys(key);
146 | if (method === undefined) {
147 | method = scope;
148 | scope = 'all';
149 | }
150 |
151 | // for each shortcut
152 | for (var i = 0; i < keys.length; i++) {
153 | // set modifier keys if any
154 | mods = [];
155 | key = keys[i].split('+');
156 | if (key.length > 1){
157 | mods = getMods(key);
158 | key = [key[key.length-1]];
159 | }
160 | // convert to keycode and...
161 | key = key[0]
162 | key = code(key);
163 | // ...store handler
164 | if (!(key in _handlers)) _handlers[key] = [];
165 | _handlers[key].push({ shortcut: keys[i], scope: scope, method: method, key: keys[i], mods: mods });
166 | }
167 | };
168 |
169 | // unbind all handlers for given key in current scope
170 | function unbindKey(key, scope) {
171 | var multipleKeys, keys,
172 | mods = [],
173 | i, j, obj;
174 |
175 | multipleKeys = getKeys(key);
176 |
177 | for (j = 0; j < multipleKeys.length; j++) {
178 | keys = multipleKeys[j].split('+');
179 |
180 | if (keys.length > 1) {
181 | mods = getMods(keys);
182 | key = keys[keys.length - 1];
183 | }
184 |
185 | key = code(key);
186 |
187 | if (scope === undefined) {
188 | scope = getScope();
189 | }
190 | if (!_handlers[key]) {
191 | return;
192 | }
193 | for (i = 0; i < _handlers[key].length; i++) {
194 | obj = _handlers[key][i];
195 | // only clear handlers if correct scope and mods match
196 | if (obj.scope === scope && compareArray(obj.mods, mods)) {
197 | _handlers[key][i] = {};
198 | }
199 | }
200 | }
201 | };
202 |
203 | // Returns true if the key with code 'keyCode' is currently down
204 | // Converts strings into key codes.
205 | function isPressed(keyCode) {
206 | if (typeof(keyCode)=='string') {
207 | keyCode = code(keyCode);
208 | }
209 | return index(_downKeys, keyCode) != -1;
210 | }
211 |
212 | function getPressedKeyCodes() {
213 | return _downKeys.slice(0);
214 | }
215 |
216 | function filter(event){
217 | var tagName = (event.target || event.srcElement).tagName;
218 | // ignore keypressed in any elements that support keyboard data input
219 | return !(tagName == 'INPUT' || tagName == 'SELECT' || tagName == 'TEXTAREA');
220 | }
221 |
222 | // initialize key. to false
223 | for(k in _MODIFIERS) assignKey[k] = false;
224 |
225 | // set current scope (default 'all')
226 | function setScope(scope){ _scope = scope || 'all' };
227 | function getScope(){ return _scope || 'all' };
228 |
229 | // delete all handlers for a given scope
230 | function deleteScope(scope){
231 | var key, handlers, i;
232 |
233 | for (key in _handlers) {
234 | handlers = _handlers[key];
235 | for (i = 0; i < handlers.length; ) {
236 | if (handlers[i].scope === scope) handlers.splice(i, 1);
237 | else i++;
238 | }
239 | }
240 | };
241 |
242 | // abstract key logic for assign and unassign
243 | function getKeys(key) {
244 | var keys;
245 | key = key.replace(/\s/g, '');
246 | keys = key.split(',');
247 | if ((keys[keys.length - 1]) == '') {
248 | keys[keys.length - 2] += ',';
249 | }
250 | return keys;
251 | }
252 |
253 | // abstract mods logic for assign and unassign
254 | function getMods(key) {
255 | var mods = key.slice(0, key.length - 1);
256 | for (var mi = 0; mi < mods.length; mi++)
257 | mods[mi] = _MODIFIERS[mods[mi]];
258 | return mods;
259 | }
260 |
261 | // cross-browser events
262 | function addEvent(object, event, method) {
263 | if (object.addEventListener)
264 | object.addEventListener(event, method, false);
265 | else if(object.attachEvent)
266 | object.attachEvent('on'+event, function(){ method(window.event) });
267 | };
268 |
269 | // set the handlers globally on document
270 | addEvent(document, 'keydown', function(event) { dispatch(event) }); // Passing _scope to a callback to ensure it remains the same by execution. Fixes #48
271 | addEvent(document, 'keyup', clearModifier);
272 |
273 | // reset modifiers to false whenever the window is (re)focused.
274 | addEvent(window, 'focus', resetModifiers);
275 |
276 | // store previously defined key
277 | var previousKey = global.key;
278 |
279 | // restore previously defined key and return reference to our key object
280 | function noConflict() {
281 | var k = global.key;
282 | global.key = previousKey;
283 | return k;
284 | }
285 |
286 | // set window.key and window.key.set/get/deleteScope, and the default filter
287 | global.key = assignKey;
288 | global.key.setScope = setScope;
289 | global.key.getScope = getScope;
290 | global.key.deleteScope = deleteScope;
291 | global.key.filter = filter;
292 | global.key.isPressed = isPressed;
293 | global.key.getPressedKeyCodes = getPressedKeyCodes;
294 | global.key.noConflict = noConflict;
295 | global.key.unbind = unbindKey;
296 |
297 | if(typeof module !== 'undefined') module.exports = assignKey;
298 |
299 | })(this);
300 |
301 | },{}],3:[function(require,module,exports){
302 | (function (global){
303 | /*
304 | PNotify 3.0.0 sciactive.com/pnotify/
305 | (C) 2015 Hunter Perrin; Google, Inc.
306 | license Apache-2.0
307 | */
308 | (function(b,k){"function"===typeof define&&define.amd?define("pnotify",["jquery"],function(q){return k(q,b)}):"object"===typeof exports&&"undefined"!==typeof module?module.exports=k((window.$),global||b):b.PNotify=k(b.jQuery,b)})(this,function(b,k){var q=function(l){var k={dir1:"down",dir2:"left",push:"bottom",spacing1:36,spacing2:36,context:b("body"),modal:!1},g,h,n=b(l),r=function(){h=b("body");d.prototype.options.stack.context=h;n=b(l);n.bind("resize",function(){g&&clearTimeout(g);g=setTimeout(function(){d.positionAll(!0)},
309 | 10)})},s=function(c){var a=b("",{"class":"ui-pnotify-modal-overlay"});a.prependTo(c.context);c.overlay_close&&a.click(function(){d.removeStack(c)});return a},d=function(c){this.parseOptions(c);this.init()};b.extend(d.prototype,{version:"3.0.0",options:{title:!1,title_escape:!1,text:!1,text_escape:!1,styling:"brighttheme",addclass:"",cornerclass:"",auto_display:!0,width:"300px",min_height:"16px",type:"notice",icon:!0,animation:"fade",animate_speed:"normal",shadow:!0,hide:!0,delay:8E3,mouse_reset:!0,
310 | remove:!0,insert_brs:!0,destroy:!0,stack:k},modules:{},runModules:function(c,a){var p,b;for(b in this.modules)p="object"===typeof a&&b in a?a[b]:a,"function"===typeof this.modules[b][c]&&(this.modules[b].notice=this,this.modules[b].options="object"===typeof this.options[b]?this.options[b]:{},this.modules[b][c](this,"object"===typeof this.options[b]?this.options[b]:{},p))},state:"initializing",timer:null,animTimer:null,styles:null,elem:null,container:null,title_container:null,text_container:null,animating:!1,
311 | timerHide:!1,init:function(){var c=this;this.modules={};b.extend(!0,this.modules,d.prototype.modules);this.styles="object"===typeof this.options.styling?this.options.styling:d.styling[this.options.styling];this.elem=b("",{"class":"ui-pnotify "+this.options.addclass,css:{display:"none"},"aria-live":"assertive","aria-role":"alertdialog",mouseenter:function(a){if(c.options.mouse_reset&&"out"===c.animating){if(!c.timerHide)return;c.cancelRemove()}c.options.hide&&c.options.mouse_reset&&c.cancelRemove()},
312 | mouseleave:function(a){c.options.hide&&c.options.mouse_reset&&"out"!==c.animating&&c.queueRemove();d.positionAll()}});"fade"===this.options.animation&&this.elem.addClass("ui-pnotify-fade-"+this.options.animate_speed);this.container=b("",{"class":this.styles.container+" ui-pnotify-container "+("error"===this.options.type?this.styles.error:"info"===this.options.type?this.styles.info:"success"===this.options.type?this.styles.success:this.styles.notice),role:"alert"}).appendTo(this.elem);""!==
313 | this.options.cornerclass&&this.container.removeClass("ui-corner-all").addClass(this.options.cornerclass);this.options.shadow&&this.container.addClass("ui-pnotify-shadow");!1!==this.options.icon&&b("",{"class":"ui-pnotify-icon"}).append(b("",{"class":!0===this.options.icon?"error"===this.options.type?this.styles.error_icon:"info"===this.options.type?this.styles.info_icon:"success"===this.options.type?this.styles.success_icon:this.styles.notice_icon:this.options.icon})).prependTo(this.container);
314 | this.title_container=b("",{"class":"ui-pnotify-title"}).appendTo(this.container);!1===this.options.title?this.title_container.hide():this.options.title_escape?this.title_container.text(this.options.title):this.title_container.html(this.options.title);this.text_container=b("",{"class":"ui-pnotify-text","aria-role":"alert"}).appendTo(this.container);!1===this.options.text?this.text_container.hide():this.options.text_escape?this.text_container.text(this.options.text):this.text_container.html(this.options.insert_brs?
315 | String(this.options.text).replace(/\n/g,"
"):this.options.text);"string"===typeof this.options.width&&this.elem.css("width",this.options.width);"string"===typeof this.options.min_height&&this.container.css("min-height",this.options.min_height);d.notices="top"===this.options.stack.push?b.merge([this],d.notices):b.merge(d.notices,[this]);"top"===this.options.stack.push&&this.queuePosition(!1,1);this.options.stack.animation=!1;this.runModules("init");this.options.auto_display&&this.open();return this},
316 | update:function(c){var a=this.options;this.parseOptions(a,c);this.elem.removeClass("ui-pnotify-fade-slow ui-pnotify-fade-normal ui-pnotify-fade-fast");"fade"===this.options.animation&&this.elem.addClass("ui-pnotify-fade-"+this.options.animate_speed);this.options.cornerclass!==a.cornerclass&&this.container.removeClass("ui-corner-all "+a.cornerclass).addClass(this.options.cornerclass);this.options.shadow!==a.shadow&&(this.options.shadow?this.container.addClass("ui-pnotify-shadow"):this.container.removeClass("ui-pnotify-shadow"));
317 | !1===this.options.addclass?this.elem.removeClass(a.addclass):this.options.addclass!==a.addclass&&this.elem.removeClass(a.addclass).addClass(this.options.addclass);!1===this.options.title?this.title_container.slideUp("fast"):this.options.title!==a.title&&(this.options.title_escape?this.title_container.text(this.options.title):this.title_container.html(this.options.title),!1===a.title&&this.title_container.slideDown(200));!1===this.options.text?this.text_container.slideUp("fast"):this.options.text!==
318 | a.text&&(this.options.text_escape?this.text_container.text(this.options.text):this.text_container.html(this.options.insert_brs?String(this.options.text).replace(/\n/g,"
"):this.options.text),!1===a.text&&this.text_container.slideDown(200));this.options.type!==a.type&&this.container.removeClass(this.styles.error+" "+this.styles.notice+" "+this.styles.success+" "+this.styles.info).addClass("error"===this.options.type?this.styles.error:"info"===this.options.type?this.styles.info:"success"===this.options.type?
319 | this.styles.success:this.styles.notice);if(this.options.icon!==a.icon||!0===this.options.icon&&this.options.type!==a.type)this.container.find("div.ui-pnotify-icon").remove(),!1!==this.options.icon&&b("",{"class":"ui-pnotify-icon"}).append(b("",{"class":!0===this.options.icon?"error"===this.options.type?this.styles.error_icon:"info"===this.options.type?this.styles.info_icon:"success"===this.options.type?this.styles.success_icon:this.styles.notice_icon:this.options.icon})).prependTo(this.container);
320 | this.options.width!==a.width&&this.elem.animate({width:this.options.width});this.options.min_height!==a.min_height&&this.container.animate({minHeight:this.options.min_height});this.options.hide?a.hide||this.queueRemove():this.cancelRemove();this.queuePosition(!0);this.runModules("update",a);return this},open:function(){this.state="opening";this.runModules("beforeOpen");var c=this;this.elem.parent().length||this.elem.appendTo(this.options.stack.context?this.options.stack.context:h);"top"!==this.options.stack.push&&
321 | this.position(!0);this.animateIn(function(){c.queuePosition(!0);c.options.hide&&c.queueRemove();c.state="open";c.runModules("afterOpen")});return this},remove:function(c){this.state="closing";this.timerHide=!!c;this.runModules("beforeClose");var a=this;this.timer&&(l.clearTimeout(this.timer),this.timer=null);this.animateOut(function(){a.state="closed";a.runModules("afterClose");a.queuePosition(!0);a.options.remove&&a.elem.detach();a.runModules("beforeDestroy");if(a.options.destroy&&null!==d.notices){var c=
322 | b.inArray(a,d.notices);-1!==c&&d.notices.splice(c,1)}a.runModules("afterDestroy")});return this},get:function(){return this.elem},parseOptions:function(c,a){this.options=b.extend(!0,{},d.prototype.options);this.options.stack=d.prototype.options.stack;for(var p=[c,a],m,f=0;f(a.context.is(h)?n.height():a.context.prop("scrollHeight"))||"up"===a.dir1&&a.nextpos1+b.height()>
327 | (a.context.is(h)?n.height():a.context.prop("scrollHeight"))||"left"===a.dir1&&a.nextpos1+b.width()>(a.context.is(h)?n.width():a.context.prop("scrollWidth"))||"right"===a.dir1&&a.nextpos1+b.width()>(a.context.is(h)?n.width():a.context.prop("scrollWidth")))a.nextpos1=a.firstpos1,a.nextpos2+=a.addpos2+("undefined"===typeof a.spacing2?25:a.spacing2),a.addpos2=0;"number"===typeof a.nextpos2&&(a.animation?b.css(e,a.nextpos2+"px"):(b.removeClass("ui-pnotify-move"),b.css(e,a.nextpos2+"px"),b.css(e),b.addClass("ui-pnotify-move")));
328 | switch(a.dir2){case "down":case "up":b.outerHeight(!0)>a.addpos2&&(a.addpos2=b.height());break;case "left":case "right":b.outerWidth(!0)>a.addpos2&&(a.addpos2=b.width())}"number"===typeof a.nextpos1&&(a.animation?b.css(f,a.nextpos1+"px"):(b.removeClass("ui-pnotify-move"),b.css(f,a.nextpos1+"px"),b.css(f),b.addClass("ui-pnotify-move")));switch(a.dir1){case "down":case "up":a.nextpos1+=b.height()+("undefined"===typeof a.spacing1?25:a.spacing1);break;case "left":case "right":a.nextpos1+=b.width()+("undefined"===
329 | typeof a.spacing1?25:a.spacing1)}}return this}},queuePosition:function(b,a){g&&clearTimeout(g);a||(a=10);g=setTimeout(function(){d.positionAll(b)},a);return this},cancelRemove:function(){this.timer&&l.clearTimeout(this.timer);this.animTimer&&l.clearTimeout(this.animTimer);"closing"===this.state&&(this.state="open",this.animating=!1,this.elem.addClass("ui-pnotify-in"),"fade"===this.options.animation&&this.elem.addClass("ui-pnotify-fade-in"));return this},queueRemove:function(){var b=this;this.cancelRemove();
330 | this.timer=l.setTimeout(function(){b.remove(!0)},isNaN(this.options.delay)?0:this.options.delay);return this}});b.extend(d,{notices:[],reload:q,removeAll:function(){b.each(d.notices,function(){this.remove&&this.remove(!1)})},removeStack:function(c){b.each(d.notices,function(){this.remove&&this.options.stack===c&&this.remove(!1)})},positionAll:function(c){g&&clearTimeout(g);g=null;if(d.notices&&d.notices.length)b.each(d.notices,function(){var a=this.options.stack;a&&(a.overlay&&a.overlay.hide(),a.nextpos1=
331 | a.firstpos1,a.nextpos2=a.firstpos2,a.addpos2=0,a.animation=c)}),b.each(d.notices,function(){this.position()});else{var a=d.prototype.options.stack;a&&(delete a.nextpos1,delete a.nextpos2)}},styling:{brighttheme:{container:"brighttheme",notice:"brighttheme-notice",notice_icon:"brighttheme-icon-notice",info:"brighttheme-info",info_icon:"brighttheme-icon-info",success:"brighttheme-success",success_icon:"brighttheme-icon-success",error:"brighttheme-error",error_icon:"brighttheme-icon-error"},jqueryui:{container:"ui-widget ui-widget-content ui-corner-all",
332 | notice:"ui-state-highlight",notice_icon:"ui-icon ui-icon-info",info:"",info_icon:"ui-icon ui-icon-info",success:"ui-state-default",success_icon:"ui-icon ui-icon-circle-check",error:"ui-state-error",error_icon:"ui-icon ui-icon-alert"},bootstrap3:{container:"alert",notice:"alert-warning",notice_icon:"glyphicon glyphicon-exclamation-sign",info:"alert-info",info_icon:"glyphicon glyphicon-info-sign",success:"alert-success",success_icon:"glyphicon glyphicon-ok-sign",error:"alert-danger",error_icon:"glyphicon glyphicon-warning-sign"}}});
333 | d.styling.fontawesome=b.extend({},d.styling.bootstrap3);b.extend(d.styling.fontawesome,{notice_icon:"fa fa-exclamation-circle",info_icon:"fa fa-info",success_icon:"fa fa-check",error_icon:"fa fa-warning"});l.document.body?r():b(r);return d};return q(k)});
334 |
335 | }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
336 |
337 | },{}],4:[function(require,module,exports){
338 | var key = require("keymaster");
339 | var message = require("modules/message");
340 |
341 | // Add shortcut coe
342 | var _addShortcut = function(selectedKey,cb) {
343 | key(selectedKey, cb);
344 | }
345 |
346 | var _clickButton = function(selector) {
347 | $(selector).trigger('click');
348 | return false;
349 | }
350 |
351 | // Add shortcuts for selectors
352 | var defaultShortcuts = function() {
353 | _addShortcut('ctrl+s', function(event,handler){
354 | _clickButton('.t-Button[onclick*="\'CREATE\'"],.t-Button#CREATE');
355 | });
356 | _addShortcut('ctrl+s', function(event,handler){
357 | _clickButton('.t-Button[onclick*="\'SAVE\'"],.t-Button#SAVE');
358 | });
359 | _addShortcut('ctrl+d', function(event,handler){
360 | _clickButton('.t-Button[onclick*="\'DELETE\'"],.t-Button#DELETE');
361 | });
362 | _addShortcut('ctrl+m', function(event,handler){
363 | message.info({
364 | title: "A key was pressed",
365 | text: "ctrl+m"
366 | });
367 | });
368 | };
369 |
370 | module.exports = {
371 | defaultShortcuts: defaultShortcuts
372 | }
373 |
374 | },{"keymaster":2,"modules/message":5}],5:[function(require,module,exports){
375 | var $ = (window.$); // being shimmed
376 | var PNotify = require("pnotify");
377 |
378 |
379 | // Button defaults
380 | var defaults = {
381 | hide: false,
382 | closer: true,
383 | buttons: {
384 | closer_hover: false,
385 | sticker: false,
386 | labels: {
387 | close: 'Sluit melding'
388 | }
389 | }
390 | };
391 |
392 |
393 | var getSettings = function(options) {
394 |
395 | switch (typeof options) {
396 | case "string":
397 | return $.extend({
398 | title: options
399 | }, defaults);
400 | case "object":
401 | return $.extend(options, defaults);
402 | default:
403 | return defaults;
404 | };
405 |
406 | };
407 |
408 | var info = function(options) {
409 | return new PNotify($.extend(getSettings(options), {
410 | type: 'info',
411 | hide: true
412 | }));
413 | };
414 |
415 | var success = function(options) {
416 | return new PNotify($.extend(getSettings(options), {
417 | type: 'success',
418 | hide: true
419 | }));
420 | };
421 |
422 | var warning = function(options) {
423 | return new PNotify($.extend(getSettings(options), {
424 | type: 'warning'
425 | }));
426 | };
427 |
428 | var error = function(options) {
429 | return new PNotify($.extend(getSettings(options), {
430 | type: 'error'
431 | }));
432 | };
433 |
434 | module.exports = {
435 | info: info,
436 | success: success,
437 | warning: warning,
438 | error: error
439 | };
440 |
441 | },{"pnotify":3}],6:[function(require,module,exports){
442 | $.widget("custom.customReport", {
443 | options: {
444 | exceptClass: 'no-row-link',
445 | activeClass: 'active',
446 | columns: [],
447 | rowclick: function(e, data) {
448 | $(this).customReport('openLink', e);
449 | }
450 | },
451 |
452 | _create: function() {
453 | var self = this;
454 | // Check if report may have a row link
455 | if (this._rowLinkAllowed) {
456 | this._initRowClick();
457 | }
458 |
459 | $(this.element).on('apexafterrefresh',function(e){
460 | self._apexafterrefresh();
461 | });
462 |
463 | },
464 |
465 | _apexafterrefresh: function() {
466 | // Check if report may have a row link
467 | if (this._rowLinkAllowed) {
468 | this._initRowClick();
469 | }
470 | },
471 |
472 | _initRowClick: function(cb) {
473 |
474 | var self = this;
475 | var data;
476 | cb = $.proxy(cb, self);
477 |
478 | // Remove previous handlers
479 | this._off(this.element, 'click tr td:not(:has(a))');
480 | this._off(this.element, 'hover tr td:not(:has(a))');
481 |
482 | // Add new handler
483 | this._on(this.element, {
484 | 'mouseenter tr td:not(:has(a))': function(e) {
485 | $(e.target).css('cursor', 'pointer');
486 | }
487 | });
488 |
489 | // Add new handler
490 | this._on(this.element, {
491 | 'click tr td:not(:has(a))': function(e) {
492 | self._trigger('rowclick', e, data);
493 | e.stopImmediatePropagation();
494 | }
495 | });
496 |
497 | },
498 |
499 | // Use an a href value to redirect on row click in report
500 | openLink: function(event, options) {
501 |
502 | var self = this;
503 |
504 | var defaults = {
505 | aPos: 0 // which "a" element contains the link
506 | }
507 |
508 | var settings = $.extend(options, defaults);
509 |
510 | // Get link
511 | var $linkElem = self._getLinkElement(settings, event.currentTarget);
512 | var href = $linkElem.attr('href');
513 |
514 | if (href != undefined) {
515 | window.location.href = href;
516 | }
517 | },
518 |
519 | _getLinkElement: function(options, target) {
520 |
521 | var links = $(target).closest('tr').find('td:has(a)');
522 |
523 | // Raise exception if position of a element is invalid
524 | if (links.length < options.aPos) {
525 | apex.debug.error('Exception: ', options.aPos + 'th "a" element is not found in report row.');
526 | } else {
527 | return $(links[options.aPos]).find('a');
528 | }
529 |
530 | },
531 |
532 | // _rowLinkAllowed returns boolean
533 | _rowLinkAllowed: function() {
534 | return !$(this.element).hasClass(this.options.exceptClass);
535 | },
536 |
537 | _setActiveRow: function($row) {
538 | $($row).closest('table').find('td.'+this.options.activeClass).removeClass(this.options.activeClass);
539 | $($row).find('td').addClass(this.options.activeClass);
540 | },
541 |
542 | activeRow: function() {
543 | return $(this.element).find('td.'+this.options.activeClass).closest('tr');
544 | }
545 |
546 | });
547 |
548 | },{}]},{},[1])
549 | //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCJfc3RyZWFtXzAuanMiLCJub2RlX21vZHVsZXMva2V5bWFzdGVyL2tleW1hc3Rlci5qcyIsIm5vZGVfbW9kdWxlcy9wbm90aWZ5L2Rpc3QvcG5vdGlmeS5qcyIsInNyYy9qcy9tb2R1bGVzL2tleWJvYXJkU2hvcnRjdXRzLmpzIiwic3JjL2pzL21vZHVsZXMvbWVzc2FnZS5qcyIsInNyYy9qcy93aWRnZXRzL2N1c3RvbVJlcG9ydC5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTtBQ0FBOztBQ0FBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7O0FDeFNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7QUMvQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ25DQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDakVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBIiwiZmlsZSI6ImdlbmVyYXRlZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzQ29udGVudCI6WyIoZnVuY3Rpb24gZSh0LG4scil7ZnVuY3Rpb24gcyhvLHUpe2lmKCFuW29dKXtpZighdFtvXSl7dmFyIGE9dHlwZW9mIHJlcXVpcmU9PVwiZnVuY3Rpb25cIiYmcmVxdWlyZTtpZighdSYmYSlyZXR1cm4gYShvLCEwKTtpZihpKXJldHVybiBpKG8sITApO3ZhciBmPW5ldyBFcnJvcihcIkNhbm5vdCBmaW5kIG1vZHVsZSAnXCIrbytcIidcIik7dGhyb3cgZi5jb2RlPVwiTU9EVUxFX05PVF9GT1VORFwiLGZ9dmFyIGw9bltvXT17ZXhwb3J0czp7fX07dFtvXVswXS5jYWxsKGwuZXhwb3J0cyxmdW5jdGlvbihlKXt2YXIgbj10W29dWzFdW2VdO3JldHVybiBzKG4/bjplKX0sbCxsLmV4cG9ydHMsZSx0LG4scil9cmV0dXJuIG5bb10uZXhwb3J0c312YXIgaT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2Zvcih2YXIgbz0wO288ci5sZW5ndGg7bysrKXMocltvXSk7cmV0dXJuIHN9KSIsInZhciBrc2NvcGUgPSB7fTt3aW5kb3cua3Njb3BlID0ga3Njb3BlO2tzY29wZS5tZXNzYWdlID0gcmVxdWlyZSgnbW9kdWxlcy9tZXNzYWdlJyk7a3Njb3BlLmtleWJvYXJkU2hvcnRjdXRzID0gcmVxdWlyZSgnbW9kdWxlcy9rZXlib2FyZFNob3J0Y3V0cycpO3JlcXVpcmUoJ3dpZGdldHMvY3VzdG9tUmVwb3J0Jyk7IiwiLy8gICAgIGtleW1hc3Rlci5qc1xuLy8gICAgIChjKSAyMDExLTIwMTMgVGhvbWFzIEZ1Y2hzXG4vLyAgICAga2V5bWFzdGVyLmpzIG1heSBiZSBmcmVlbHkgZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIE1JVCBsaWNlbnNlLlxuXG47KGZ1bmN0aW9uKGdsb2JhbCl7XG4gIHZhciBrLFxuICAgIF9oYW5kbGVycyA9IHt9LFxuICAgIF9tb2RzID0geyAxNjogZmFsc2UsIDE4OiBmYWxzZSwgMTc6IGZhbHNlLCA5MTogZmFsc2UgfSxcbiAgICBfc2NvcGUgPSAnYWxsJyxcbiAgICAvLyBtb2RpZmllciBrZXlzXG4gICAgX01PRElGSUVSUyA9IHtcbiAgICAgICfih6cnOiAxNiwgc2hpZnQ6IDE2LFxuICAgICAgJ+KMpSc6IDE4LCBhbHQ6IDE4LCBvcHRpb246IDE4LFxuICAgICAgJ+KMgyc6IDE3LCBjdHJsOiAxNywgY29udHJvbDogMTcsXG4gICAgICAn4oyYJzogOTEsIGNvbW1hbmQ6IDkxXG4gICAgfSxcbiAgICAvLyBzcGVjaWFsIGtleXNcbiAgICBfTUFQID0ge1xuICAgICAgYmFja3NwYWNlOiA4LCB0YWI6IDksIGNsZWFyOiAxMixcbiAgICAgIGVudGVyOiAxMywgJ3JldHVybic6IDEzLFxuICAgICAgZXNjOiAyNywgZXNjYXBlOiAyNywgc3BhY2U6IDMyLFxuICAgICAgbGVmdDogMzcsIHVwOiAzOCxcbiAgICAgIHJpZ2h0OiAzOSwgZG93bjogNDAsXG4gICAgICBkZWw6IDQ2LCAnZGVsZXRlJzogNDYsXG4gICAgICBob21lOiAzNiwgZW5kOiAzNSxcbiAgICAgIHBhZ2V1cDogMzMsIHBhZ2Vkb3duOiAzNCxcbiAgICAgICcsJzogMTg4LCAnLic6IDE5MCwgJy8nOiAxOTEsXG4gICAgICAnYCc6IDE5MiwgJy0nOiAxODksICc9JzogMTg3LFxuICAgICAgJzsnOiAxODYsICdcXCcnOiAyMjIsXG4gICAgICAnWyc6IDIxOSwgJ10nOiAyMjEsICdcXFxcJzogMjIwXG4gICAgfSxcbiAgICBjb2RlID0gZnVuY3Rpb24oeCl7XG4gICAgICByZXR1cm4gX01BUFt4XSB8fCB4LnRvVXBwZXJDYXNlKCkuY2hhckNvZGVBdCgwKTtcbiAgICB9LFxuICAgIF9kb3duS2V5cyA9IFtdO1xuXG4gIGZvcihrPTE7azwyMDtrKyspIF9NQVBbJ2YnK2tdID0gMTExK2s7XG5cbiAgLy8gSUUgZG9lc24ndCBzdXBwb3J0IEFycmF5I2luZGV4T2YsIHNvIGhhdmUgYSBzaW1wbGUgcmVwbGFjZW1lbnRcbiAgZnVuY3Rpb24gaW5kZXgoYXJyYXksIGl0ZW0pe1xuICAgIHZhciBpID0gYXJyYXkubGVuZ3RoO1xuICAgIHdoaWxlKGktLSkgaWYoYXJyYXlbaV09PT1pdGVtKSByZXR1cm4gaTtcbiAgICByZXR1cm4gLTE7XG4gIH1cblxuICAvLyBmb3IgY29tcGFyaW5nIG1vZHMgYmVmb3JlIHVuYXNzaWdubWVudFxuICBmdW5jdGlvbiBjb21wYXJlQXJyYXkoYTEsIGEyKSB7XG4gICAgaWYgKGExLmxlbmd0aCAhPSBhMi5sZW5ndGgpIHJldHVybiBmYWxzZTtcbiAgICBmb3IgKHZhciBpID0gMDsgaSA8IGExLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgIGlmIChhMVtpXSAhPT0gYTJbaV0pIHJldHVybiBmYWxzZTtcbiAgICB9XG4gICAgcmV0dXJuIHRydWU7XG4gIH1cblxuICB2YXIgbW9kaWZpZXJNYXAgPSB7XG4gICAgICAxNjonc2hpZnRLZXknLFxuICAgICAgMTg6J2FsdEtleScsXG4gICAgICAxNzonY3RybEtleScsXG4gICAgICA5MTonbWV0YUtleSdcbiAgfTtcbiAgZnVuY3Rpb24gdXBkYXRlTW9kaWZpZXJLZXkoZXZlbnQpIHtcbiAgICAgIGZvcihrIGluIF9tb2RzKSBfbW9kc1trXSA9IGV2ZW50W21vZGlmaWVyTWFwW2tdXTtcbiAgfTtcblxuICAvLyBoYW5kbGUga2V5ZG93biBldmVudFxuICBmdW5jdGlvbiBkaXNwYXRjaChldmVudCkge1xuICAgIHZhciBrZXksIGhhbmRsZXIsIGssIGksIG1vZGlmaWVyc01hdGNoLCBzY29wZTtcbiAgICBrZXkgPSBldmVudC5rZXlDb2RlO1xuXG4gICAgaWYgKGluZGV4KF9kb3duS2V5cywga2V5KSA9PSAtMSkge1xuICAgICAgICBfZG93bktleXMucHVzaChrZXkpO1xuICAgIH1cblxuICAgIC8vIGlmIGEgbW9kaWZpZXIga2V5LCBzZXQgdGhlIGtleS48bW9kaWZpZXJrZXluYW1lPiBwcm9wZXJ0eSB0byB0cnVlIGFuZCByZXR1cm5cbiAgICBpZihrZXkgPT0gOTMgfHwga2V5ID09IDIyNCkga2V5ID0gOTE7IC8vIHJpZ2h0IGNvbW1hbmQgb24gd2Via2l0LCBjb21tYW5kIG9uIEdlY2tvXG4gICAgaWYoa2V5IGluIF9tb2RzKSB7XG4gICAgICBfbW9kc1trZXldID0gdHJ1ZTtcbiAgICAgIC8vICdhc3NpZ25LZXknIGZyb20gaW5zaWRlIHRoaXMgY2xvc3VyZSBpcyBleHBvcnRlZCB0byB3aW5kb3cua2V5XG4gICAgICBmb3IoayBpbiBfTU9ESUZJRVJTKSBpZihfTU9ESUZJRVJTW2tdID09IGtleSkgYXNzaWduS2V5W2tdID0gdHJ1ZTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgdXBkYXRlTW9kaWZpZXJLZXkoZXZlbnQpO1xuXG4gICAgLy8gc2VlIGlmIHdlIG5lZWQgdG8gaWdub3JlIHRoZSBrZXlwcmVzcyAoZmlsdGVyKCkgY2FuIGNhbiBiZSBvdmVycmlkZGVuKVxuICAgIC8vIGJ5IGRlZmF1bHQgaWdub3JlIGtleSBwcmVzc2VzIGlmIGEgc2VsZWN0LCB0ZXh0YXJlYSwgb3IgaW5wdXQgaXMgZm9jdXNlZFxuICAgIGlmKCFhc3NpZ25LZXkuZmlsdGVyLmNhbGwodGhpcywgZXZlbnQpKSByZXR1cm47XG5cbiAgICAvLyBhYm9ydCBpZiBubyBwb3RlbnRpYWxseSBtYXRjaGluZyBzaG9ydGN1dHMgZm91bmRcbiAgICBpZiAoIShrZXkgaW4gX2hhbmRsZXJzKSkgcmV0dXJuO1xuXG4gICAgc2NvcGUgPSBnZXRTY29wZSgpO1xuXG4gICAgLy8gZm9yIGVhY2ggcG90ZW50aWFsIHNob3J0Y3V0XG4gICAgZm9yIChpID0gMDsgaSA8IF9oYW5kbGVyc1trZXldLmxlbmd0aDsgaSsrKSB7XG4gICAgICBoYW5kbGVyID0gX2hhbmRsZXJzW2tleV1baV07XG5cbiAgICAgIC8vIHNlZSBpZiBpdCdzIGluIHRoZSBjdXJyZW50IHNjb3BlXG4gICAgICBpZihoYW5kbGVyLnNjb3BlID09IHNjb3BlIHx8IGhhbmRsZXIuc2NvcGUgPT0gJ2FsbCcpe1xuICAgICAgICAvLyBjaGVjayBpZiBtb2RpZmllcnMgbWF0Y2ggaWYgYW55XG4gICAgICAgIG1vZGlmaWVyc01hdGNoID0gaGFuZGxlci5tb2RzLmxlbmd0aCA+IDA7XG4gICAgICAgIGZvcihrIGluIF9tb2RzKVxuICAgICAgICAgIGlmKCghX21vZHNba10gJiYgaW5kZXgoaGFuZGxlci5tb2RzLCAraykgPiAtMSkgfHxcbiAgICAgICAgICAgIChfbW9kc1trXSAmJiBpbmRleChoYW5kbGVyLm1vZHMsICtrKSA9PSAtMSkpIG1vZGlmaWVyc01hdGNoID0gZmFsc2U7XG4gICAgICAgIC8vIGNhbGwgdGhlIGhhbmRsZXIgYW5kIHN0b3AgdGhlIGV2ZW50IGlmIG5lY2Nlc3NhcnlcbiAgICAgICAgaWYoKGhhbmRsZXIubW9kcy5sZW5ndGggPT0gMCAmJiAhX21vZHNbMTZdICYmICFfbW9kc1sxOF0gJiYgIV9tb2RzWzE3XSAmJiAhX21vZHNbOTFdKSB8fCBtb2RpZmllcnNNYXRjaCl7XG4gICAgICAgICAgaWYoaGFuZGxlci5tZXRob2QoZXZlbnQsIGhhbmRsZXIpPT09ZmFsc2Upe1xuICAgICAgICAgICAgaWYoZXZlbnQucHJldmVudERlZmF1bHQpIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG4gICAgICAgICAgICAgIGVsc2UgZXZlbnQucmV0dXJuVmFsdWUgPSBmYWxzZTtcbiAgICAgICAgICAgIGlmKGV2ZW50LnN0b3BQcm9wYWdhdGlvbikgZXZlbnQuc3RvcFByb3BhZ2F0aW9uKCk7XG4gICAgICAgICAgICBpZihldmVudC5jYW5jZWxCdWJibGUpIGV2ZW50LmNhbmNlbEJ1YmJsZSA9IHRydWU7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICB9O1xuXG4gIC8vIHVuc2V0IG1vZGlmaWVyIGtleXMgb24ga2V5dXBcbiAgZnVuY3Rpb24gY2xlYXJNb2RpZmllcihldmVudCl7XG4gICAgdmFyIGtleSA9IGV2ZW50LmtleUNvZGUsIGssXG4gICAgICAgIGkgPSBpbmRleChfZG93bktleXMsIGtleSk7XG5cbiAgICAvLyByZW1vdmUga2V5IGZyb20gX2Rvd25LZXlzXG4gICAgaWYgKGkgPj0gMCkge1xuICAgICAgICBfZG93bktleXMuc3BsaWNlKGksIDEpO1xuICAgIH1cblxuICAgIGlmKGtleSA9PSA5MyB8fCBrZXkgPT0gMjI0KSBrZXkgPSA5MTtcbiAgICBpZihrZXkgaW4gX21vZHMpIHtcbiAgICAgIF9tb2RzW2tleV0gPSBmYWxzZTtcbiAgICAgIGZvcihrIGluIF9NT0RJRklFUlMpIGlmKF9NT0RJRklFUlNba10gPT0ga2V5KSBhc3NpZ25LZXlba10gPSBmYWxzZTtcbiAgICB9XG4gIH07XG5cbiAgZnVuY3Rpb24gcmVzZXRNb2RpZmllcnMoKSB7XG4gICAgZm9yKGsgaW4gX21vZHMpIF9tb2RzW2tdID0gZmFsc2U7XG4gICAgZm9yKGsgaW4gX01PRElGSUVSUykgYXNzaWduS2V5W2tdID0gZmFsc2U7XG4gIH07XG5cbiAgLy8gcGFyc2UgYW5kIGFzc2lnbiBzaG9ydGN1dFxuICBmdW5jdGlvbiBhc3NpZ25LZXkoa2V5LCBzY29wZSwgbWV0aG9kKXtcbiAgICB2YXIga2V5cywgbW9kcztcbiAgICBrZXlzID0gZ2V0S2V5cyhrZXkpO1xuICAgIGlmIChtZXRob2QgPT09IHVuZGVmaW5lZCkge1xuICAgICAgbWV0aG9kID0gc2NvcGU7XG4gICAgICBzY29wZSA9ICdhbGwnO1xuICAgIH1cblxuICAgIC8vIGZvciBlYWNoIHNob3J0Y3V0XG4gICAgZm9yICh2YXIgaSA9IDA7IGkgPCBrZXlzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAvLyBzZXQgbW9kaWZpZXIga2V5cyBpZiBhbnlcbiAgICAgIG1vZHMgPSBbXTtcbiAgICAgIGtleSA9IGtleXNbaV0uc3BsaXQoJysnKTtcbiAgICAgIGlmIChrZXkubGVuZ3RoID4gMSl7XG4gICAgICAgIG1vZHMgPSBnZXRNb2RzKGtleSk7XG4gICAgICAgIGtleSA9IFtrZXlba2V5Lmxlbmd0aC0xXV07XG4gICAgICB9XG4gICAgICAvLyBjb252ZXJ0IHRvIGtleWNvZGUgYW5kLi4uXG4gICAgICBrZXkgPSBrZXlbMF1cbiAgICAgIGtleSA9IGNvZGUoa2V5KTtcbiAgICAgIC8vIC4uLnN0b3JlIGhhbmRsZXJcbiAgICAgIGlmICghKGtleSBpbiBfaGFuZGxlcnMpKSBfaGFuZGxlcnNba2V5XSA9IFtdO1xuICAgICAgX2hhbmRsZXJzW2tleV0ucHVzaCh7IHNob3J0Y3V0OiBrZXlzW2ldLCBzY29wZTogc2NvcGUsIG1ldGhvZDogbWV0aG9kLCBrZXk6IGtleXNbaV0sIG1vZHM6IG1vZHMgfSk7XG4gICAgfVxuICB9O1xuXG4gIC8vIHVuYmluZCBhbGwgaGFuZGxlcnMgZm9yIGdpdmVuIGtleSBpbiBjdXJyZW50IHNjb3BlXG4gIGZ1bmN0aW9uIHVuYmluZEtleShrZXksIHNjb3BlKSB7XG4gICAgdmFyIG11bHRpcGxlS2V5cywga2V5cyxcbiAgICAgIG1vZHMgPSBbXSxcbiAgICAgIGksIGosIG9iajtcblxuICAgIG11bHRpcGxlS2V5cyA9IGdldEtleXMoa2V5KTtcblxuICAgIGZvciAoaiA9IDA7IGogPCBtdWx0aXBsZUtleXMubGVuZ3RoOyBqKyspIHtcbiAgICAgIGtleXMgPSBtdWx0aXBsZUtleXNbal0uc3BsaXQoJysnKTtcblxuICAgICAgaWYgKGtleXMubGVuZ3RoID4gMSkge1xuICAgICAgICBtb2RzID0gZ2V0TW9kcyhrZXlzKTtcbiAgICAgICAga2V5ID0ga2V5c1trZXlzLmxlbmd0aCAtIDFdO1xuICAgICAgfVxuXG4gICAgICBrZXkgPSBjb2RlKGtleSk7XG5cbiAgICAgIGlmIChzY29wZSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIHNjb3BlID0gZ2V0U2NvcGUoKTtcbiAgICAgIH1cbiAgICAgIGlmICghX2hhbmRsZXJzW2tleV0pIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgZm9yIChpID0gMDsgaSA8IF9oYW5kbGVyc1trZXldLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgIG9iaiA9IF9oYW5kbGVyc1trZXldW2ldO1xuICAgICAgICAvLyBvbmx5IGNsZWFyIGhhbmRsZXJzIGlmIGNvcnJlY3Qgc2NvcGUgYW5kIG1vZHMgbWF0Y2hcbiAgICAgICAgaWYgKG9iai5zY29wZSA9PT0gc2NvcGUgJiYgY29tcGFyZUFycmF5KG9iai5tb2RzLCBtb2RzKSkge1xuICAgICAgICAgIF9oYW5kbGVyc1trZXldW2ldID0ge307XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gIH07XG5cbiAgLy8gUmV0dXJucyB0cnVlIGlmIHRoZSBrZXkgd2l0aCBjb2RlICdrZXlDb2RlJyBpcyBjdXJyZW50bHkgZG93blxuICAvLyBDb252ZXJ0cyBzdHJpbmdzIGludG8ga2V5IGNvZGVzLlxuICBmdW5jdGlvbiBpc1ByZXNzZWQoa2V5Q29kZSkge1xuICAgICAgaWYgKHR5cGVvZihrZXlDb2RlKT09J3N0cmluZycpIHtcbiAgICAgICAga2V5Q29kZSA9IGNvZGUoa2V5Q29kZSk7XG4gICAgICB9XG4gICAgICByZXR1cm4gaW5kZXgoX2Rvd25LZXlzLCBrZXlDb2RlKSAhPSAtMTtcbiAgfVxuXG4gIGZ1bmN0aW9uIGdldFByZXNzZWRLZXlDb2RlcygpIHtcbiAgICAgIHJldHVybiBfZG93bktleXMuc2xpY2UoMCk7XG4gIH1cblxuICBmdW5jdGlvbiBmaWx0ZXIoZXZlbnQpe1xuICAgIHZhciB0YWdOYW1lID0gKGV2ZW50LnRhcmdldCB8fCBldmVudC5zcmNFbGVtZW50KS50YWdOYW1lO1xuICAgIC8vIGlnbm9yZSBrZXlwcmVzc2VkIGluIGFueSBlbGVtZW50cyB0aGF0IHN1cHBvcnQga2V5Ym9hcmQgZGF0YSBpbnB1dFxuICAgIHJldHVybiAhKHRhZ05hbWUgPT0gJ0lOUFVUJyB8fCB0YWdOYW1lID09ICdTRUxFQ1QnIHx8IHRhZ05hbWUgPT0gJ1RFWFRBUkVBJyk7XG4gIH1cblxuICAvLyBpbml0aWFsaXplIGtleS48bW9kaWZpZXI+IHRvIGZhbHNlXG4gIGZvcihrIGluIF9NT0RJRklFUlMpIGFzc2lnbktleVtrXSA9IGZhbHNlO1xuXG4gIC8vIHNldCBjdXJyZW50IHNjb3BlIChkZWZhdWx0ICdhbGwnKVxuICBmdW5jdGlvbiBzZXRTY29wZShzY29wZSl7IF9zY29wZSA9IHNjb3BlIHx8ICdhbGwnIH07XG4gIGZ1bmN0aW9uIGdldFNjb3BlKCl7IHJldHVybiBfc2NvcGUgfHwgJ2FsbCcgfTtcblxuICAvLyBkZWxldGUgYWxsIGhhbmRsZXJzIGZvciBhIGdpdmVuIHNjb3BlXG4gIGZ1bmN0aW9uIGRlbGV0ZVNjb3BlKHNjb3BlKXtcbiAgICB2YXIga2V5LCBoYW5kbGVycywgaTtcblxuICAgIGZvciAoa2V5IGluIF9oYW5kbGVycykge1xuICAgICAgaGFuZGxlcnMgPSBfaGFuZGxlcnNba2V5XTtcbiAgICAgIGZvciAoaSA9IDA7IGkgPCBoYW5kbGVycy5sZW5ndGg7ICkge1xuICAgICAgICBpZiAoaGFuZGxlcnNbaV0uc2NvcGUgPT09IHNjb3BlKSBoYW5kbGVycy5zcGxpY2UoaSwgMSk7XG4gICAgICAgIGVsc2UgaSsrO1xuICAgICAgfVxuICAgIH1cbiAgfTtcblxuICAvLyBhYnN0cmFjdCBrZXkgbG9naWMgZm9yIGFzc2lnbiBhbmQgdW5hc3NpZ25cbiAgZnVuY3Rpb24gZ2V0S2V5cyhrZXkpIHtcbiAgICB2YXIga2V5cztcbiAgICBrZXkgPSBrZXkucmVwbGFjZSgvXFxzL2csICcnKTtcbiAgICBrZXlzID0ga2V5LnNwbGl0KCcsJyk7XG4gICAgaWYgKChrZXlzW2tleXMubGVuZ3RoIC0gMV0pID09ICcnKSB7XG4gICAgICBrZXlzW2tleXMubGVuZ3RoIC0gMl0gKz0gJywnO1xuICAgIH1cbiAgICByZXR1cm4ga2V5cztcbiAgfVxuXG4gIC8vIGFic3RyYWN0IG1vZHMgbG9naWMgZm9yIGFzc2lnbiBhbmQgdW5hc3NpZ25cbiAgZnVuY3Rpb24gZ2V0TW9kcyhrZXkpIHtcbiAgICB2YXIgbW9kcyA9IGtleS5zbGljZSgwLCBrZXkubGVuZ3RoIC0gMSk7XG4gICAgZm9yICh2YXIgbWkgPSAwOyBtaSA8IG1vZHMubGVuZ3RoOyBtaSsrKVxuICAgIG1vZHNbbWldID0gX01PRElGSUVSU1ttb2RzW21pXV07XG4gICAgcmV0dXJuIG1vZHM7XG4gIH1cblxuICAvLyBjcm9zcy1icm93c2VyIGV2ZW50c1xuICBmdW5jdGlvbiBhZGRFdmVudChvYmplY3QsIGV2ZW50LCBtZXRob2QpIHtcbiAgICBpZiAob2JqZWN0LmFkZEV2ZW50TGlzdGVuZXIpXG4gICAgICBvYmplY3QuYWRkRXZlbnRMaXN0ZW5lcihldmVudCwgbWV0aG9kLCBmYWxzZSk7XG4gICAgZWxzZSBpZihvYmplY3QuYXR0YWNoRXZlbnQpXG4gICAgICBvYmplY3QuYXR0YWNoRXZlbnQoJ29uJytldmVudCwgZnVuY3Rpb24oKXsgbWV0aG9kKHdpbmRvdy5ldmVudCkgfSk7XG4gIH07XG5cbiAgLy8gc2V0IHRoZSBoYW5kbGVycyBnbG9iYWxseSBvbiBkb2N1bWVudFxuICBhZGRFdmVudChkb2N1bWVudCwgJ2tleWRvd24nLCBmdW5jdGlvbihldmVudCkgeyBkaXNwYXRjaChldmVudCkgfSk7IC8vIFBhc3NpbmcgX3Njb3BlIHRvIGEgY2FsbGJhY2sgdG8gZW5zdXJlIGl0IHJlbWFpbnMgdGhlIHNhbWUgYnkgZXhlY3V0aW9uLiBGaXhlcyAjNDhcbiAgYWRkRXZlbnQoZG9jdW1lbnQsICdrZXl1cCcsIGNsZWFyTW9kaWZpZXIpO1xuXG4gIC8vIHJlc2V0IG1vZGlmaWVycyB0byBmYWxzZSB3aGVuZXZlciB0aGUgd2luZG93IGlzIChyZSlmb2N1c2VkLlxuICBhZGRFdmVudCh3aW5kb3csICdmb2N1cycsIHJlc2V0TW9kaWZpZXJzKTtcblxuICAvLyBzdG9yZSBwcmV2aW91c2x5IGRlZmluZWQga2V5XG4gIHZhciBwcmV2aW91c0tleSA9IGdsb2JhbC5rZXk7XG5cbiAgLy8gcmVzdG9yZSBwcmV2aW91c2x5IGRlZmluZWQga2V5IGFuZCByZXR1cm4gcmVmZXJlbmNlIHRvIG91ciBrZXkgb2JqZWN0XG4gIGZ1bmN0aW9uIG5vQ29uZmxpY3QoKSB7XG4gICAgdmFyIGsgPSBnbG9iYWwua2V5O1xuICAgIGdsb2JhbC5rZXkgPSBwcmV2aW91c0tleTtcbiAgICByZXR1cm4gaztcbiAgfVxuXG4gIC8vIHNldCB3aW5kb3cua2V5IGFuZCB3aW5kb3cua2V5LnNldC9nZXQvZGVsZXRlU2NvcGUsIGFuZCB0aGUgZGVmYXVsdCBmaWx0ZXJcbiAgZ2xvYmFsLmtleSA9IGFzc2lnbktleTtcbiAgZ2xvYmFsLmtleS5zZXRTY29wZSA9IHNldFNjb3BlO1xuICBnbG9iYWwua2V5LmdldFNjb3BlID0gZ2V0U2NvcGU7XG4gIGdsb2JhbC5rZXkuZGVsZXRlU2NvcGUgPSBkZWxldGVTY29wZTtcbiAgZ2xvYmFsLmtleS5maWx0ZXIgPSBmaWx0ZXI7XG4gIGdsb2JhbC5rZXkuaXNQcmVzc2VkID0gaXNQcmVzc2VkO1xuICBnbG9iYWwua2V5LmdldFByZXNzZWRLZXlDb2RlcyA9IGdldFByZXNzZWRLZXlDb2RlcztcbiAgZ2xvYmFsLmtleS5ub0NvbmZsaWN0ID0gbm9Db25mbGljdDtcbiAgZ2xvYmFsLmtleS51bmJpbmQgPSB1bmJpbmRLZXk7XG5cbiAgaWYodHlwZW9mIG1vZHVsZSAhPT0gJ3VuZGVmaW5lZCcpIG1vZHVsZS5leHBvcnRzID0gYXNzaWduS2V5O1xuXG59KSh0aGlzKTtcbiIsIi8qXG5QTm90aWZ5IDMuMC4wIHNjaWFjdGl2ZS5jb20vcG5vdGlmeS9cbihDKSAyMDE1IEh1bnRlciBQZXJyaW47IEdvb2dsZSwgSW5jLlxubGljZW5zZSBBcGFjaGUtMi4wXG4qL1xuKGZ1bmN0aW9uKGIsayl7XCJmdW5jdGlvblwiPT09dHlwZW9mIGRlZmluZSYmZGVmaW5lLmFtZD9kZWZpbmUoXCJwbm90aWZ5XCIsW1wianF1ZXJ5XCJdLGZ1bmN0aW9uKHEpe3JldHVybiBrKHEsYil9KTpcIm9iamVjdFwiPT09dHlwZW9mIGV4cG9ydHMmJlwidW5kZWZpbmVkXCIhPT10eXBlb2YgbW9kdWxlP21vZHVsZS5leHBvcnRzPWsoKHdpbmRvdy4kKSxnbG9iYWx8fGIpOmIuUE5vdGlmeT1rKGIualF1ZXJ5LGIpfSkodGhpcyxmdW5jdGlvbihiLGspe3ZhciBxPWZ1bmN0aW9uKGwpe3ZhciBrPXtkaXIxOlwiZG93blwiLGRpcjI6XCJsZWZ0XCIscHVzaDpcImJvdHRvbVwiLHNwYWNpbmcxOjM2LHNwYWNpbmcyOjM2LGNvbnRleHQ6YihcImJvZHlcIiksbW9kYWw6ITF9LGcsaCxuPWIobCkscj1mdW5jdGlvbigpe2g9YihcImJvZHlcIik7ZC5wcm90b3R5cGUub3B0aW9ucy5zdGFjay5jb250ZXh0PWg7bj1iKGwpO24uYmluZChcInJlc2l6ZVwiLGZ1bmN0aW9uKCl7ZyYmY2xlYXJUaW1lb3V0KGcpO2c9c2V0VGltZW91dChmdW5jdGlvbigpe2QucG9zaXRpb25BbGwoITApfSxcbjEwKX0pfSxzPWZ1bmN0aW9uKGMpe3ZhciBhPWIoXCI8ZGl2IC8+XCIse1wiY2xhc3NcIjpcInVpLXBub3RpZnktbW9kYWwtb3ZlcmxheVwifSk7YS5wcmVwZW5kVG8oYy5jb250ZXh0KTtjLm92ZXJsYXlfY2xvc2UmJmEuY2xpY2soZnVuY3Rpb24oKXtkLnJlbW92ZVN0YWNrKGMpfSk7cmV0dXJuIGF9LGQ9ZnVuY3Rpb24oYyl7dGhpcy5wYXJzZU9wdGlvbnMoYyk7dGhpcy5pbml0KCl9O2IuZXh0ZW5kKGQucHJvdG90eXBlLHt2ZXJzaW9uOlwiMy4wLjBcIixvcHRpb25zOnt0aXRsZTohMSx0aXRsZV9lc2NhcGU6ITEsdGV4dDohMSx0ZXh0X2VzY2FwZTohMSxzdHlsaW5nOlwiYnJpZ2h0dGhlbWVcIixhZGRjbGFzczpcIlwiLGNvcm5lcmNsYXNzOlwiXCIsYXV0b19kaXNwbGF5OiEwLHdpZHRoOlwiMzAwcHhcIixtaW5faGVpZ2h0OlwiMTZweFwiLHR5cGU6XCJub3RpY2VcIixpY29uOiEwLGFuaW1hdGlvbjpcImZhZGVcIixhbmltYXRlX3NwZWVkOlwibm9ybWFsXCIsc2hhZG93OiEwLGhpZGU6ITAsZGVsYXk6OEUzLG1vdXNlX3Jlc2V0OiEwLFxucmVtb3ZlOiEwLGluc2VydF9icnM6ITAsZGVzdHJveTohMCxzdGFjazprfSxtb2R1bGVzOnt9LHJ1bk1vZHVsZXM6ZnVuY3Rpb24oYyxhKXt2YXIgcCxiO2ZvcihiIGluIHRoaXMubW9kdWxlcylwPVwib2JqZWN0XCI9PT10eXBlb2YgYSYmYiBpbiBhP2FbYl06YSxcImZ1bmN0aW9uXCI9PT10eXBlb2YgdGhpcy5tb2R1bGVzW2JdW2NdJiYodGhpcy5tb2R1bGVzW2JdLm5vdGljZT10aGlzLHRoaXMubW9kdWxlc1tiXS5vcHRpb25zPVwib2JqZWN0XCI9PT10eXBlb2YgdGhpcy5vcHRpb25zW2JdP3RoaXMub3B0aW9uc1tiXTp7fSx0aGlzLm1vZHVsZXNbYl1bY10odGhpcyxcIm9iamVjdFwiPT09dHlwZW9mIHRoaXMub3B0aW9uc1tiXT90aGlzLm9wdGlvbnNbYl06e30scCkpfSxzdGF0ZTpcImluaXRpYWxpemluZ1wiLHRpbWVyOm51bGwsYW5pbVRpbWVyOm51bGwsc3R5bGVzOm51bGwsZWxlbTpudWxsLGNvbnRhaW5lcjpudWxsLHRpdGxlX2NvbnRhaW5lcjpudWxsLHRleHRfY29udGFpbmVyOm51bGwsYW5pbWF0aW5nOiExLFxudGltZXJIaWRlOiExLGluaXQ6ZnVuY3Rpb24oKXt2YXIgYz10aGlzO3RoaXMubW9kdWxlcz17fTtiLmV4dGVuZCghMCx0aGlzLm1vZHVsZXMsZC5wcm90b3R5cGUubW9kdWxlcyk7dGhpcy5zdHlsZXM9XCJvYmplY3RcIj09PXR5cGVvZiB0aGlzLm9wdGlvbnMuc3R5bGluZz90aGlzLm9wdGlvbnMuc3R5bGluZzpkLnN0eWxpbmdbdGhpcy5vcHRpb25zLnN0eWxpbmddO3RoaXMuZWxlbT1iKFwiPGRpdiAvPlwiLHtcImNsYXNzXCI6XCJ1aS1wbm90aWZ5IFwiK3RoaXMub3B0aW9ucy5hZGRjbGFzcyxjc3M6e2Rpc3BsYXk6XCJub25lXCJ9LFwiYXJpYS1saXZlXCI6XCJhc3NlcnRpdmVcIixcImFyaWEtcm9sZVwiOlwiYWxlcnRkaWFsb2dcIixtb3VzZWVudGVyOmZ1bmN0aW9uKGEpe2lmKGMub3B0aW9ucy5tb3VzZV9yZXNldCYmXCJvdXRcIj09PWMuYW5pbWF0aW5nKXtpZighYy50aW1lckhpZGUpcmV0dXJuO2MuY2FuY2VsUmVtb3ZlKCl9Yy5vcHRpb25zLmhpZGUmJmMub3B0aW9ucy5tb3VzZV9yZXNldCYmYy5jYW5jZWxSZW1vdmUoKX0sXG5tb3VzZWxlYXZlOmZ1bmN0aW9uKGEpe2Mub3B0aW9ucy5oaWRlJiZjLm9wdGlvbnMubW91c2VfcmVzZXQmJlwib3V0XCIhPT1jLmFuaW1hdGluZyYmYy5xdWV1ZVJlbW92ZSgpO2QucG9zaXRpb25BbGwoKX19KTtcImZhZGVcIj09PXRoaXMub3B0aW9ucy5hbmltYXRpb24mJnRoaXMuZWxlbS5hZGRDbGFzcyhcInVpLXBub3RpZnktZmFkZS1cIit0aGlzLm9wdGlvbnMuYW5pbWF0ZV9zcGVlZCk7dGhpcy5jb250YWluZXI9YihcIjxkaXYgLz5cIix7XCJjbGFzc1wiOnRoaXMuc3R5bGVzLmNvbnRhaW5lcitcIiB1aS1wbm90aWZ5LWNvbnRhaW5lciBcIisoXCJlcnJvclwiPT09dGhpcy5vcHRpb25zLnR5cGU/dGhpcy5zdHlsZXMuZXJyb3I6XCJpbmZvXCI9PT10aGlzLm9wdGlvbnMudHlwZT90aGlzLnN0eWxlcy5pbmZvOlwic3VjY2Vzc1wiPT09dGhpcy5vcHRpb25zLnR5cGU/dGhpcy5zdHlsZXMuc3VjY2Vzczp0aGlzLnN0eWxlcy5ub3RpY2UpLHJvbGU6XCJhbGVydFwifSkuYXBwZW5kVG8odGhpcy5lbGVtKTtcIlwiIT09XG50aGlzLm9wdGlvbnMuY29ybmVyY2xhc3MmJnRoaXMuY29udGFpbmVyLnJlbW92ZUNsYXNzKFwidWktY29ybmVyLWFsbFwiKS5hZGRDbGFzcyh0aGlzLm9wdGlvbnMuY29ybmVyY2xhc3MpO3RoaXMub3B0aW9ucy5zaGFkb3cmJnRoaXMuY29udGFpbmVyLmFkZENsYXNzKFwidWktcG5vdGlmeS1zaGFkb3dcIik7ITEhPT10aGlzLm9wdGlvbnMuaWNvbiYmYihcIjxkaXYgLz5cIix7XCJjbGFzc1wiOlwidWktcG5vdGlmeS1pY29uXCJ9KS5hcHBlbmQoYihcIjxzcGFuIC8+XCIse1wiY2xhc3NcIjohMD09PXRoaXMub3B0aW9ucy5pY29uP1wiZXJyb3JcIj09PXRoaXMub3B0aW9ucy50eXBlP3RoaXMuc3R5bGVzLmVycm9yX2ljb246XCJpbmZvXCI9PT10aGlzLm9wdGlvbnMudHlwZT90aGlzLnN0eWxlcy5pbmZvX2ljb246XCJzdWNjZXNzXCI9PT10aGlzLm9wdGlvbnMudHlwZT90aGlzLnN0eWxlcy5zdWNjZXNzX2ljb246dGhpcy5zdHlsZXMubm90aWNlX2ljb246dGhpcy5vcHRpb25zLmljb259KSkucHJlcGVuZFRvKHRoaXMuY29udGFpbmVyKTtcbnRoaXMudGl0bGVfY29udGFpbmVyPWIoXCI8aDQgLz5cIix7XCJjbGFzc1wiOlwidWktcG5vdGlmeS10aXRsZVwifSkuYXBwZW5kVG8odGhpcy5jb250YWluZXIpOyExPT09dGhpcy5vcHRpb25zLnRpdGxlP3RoaXMudGl0bGVfY29udGFpbmVyLmhpZGUoKTp0aGlzLm9wdGlvbnMudGl0bGVfZXNjYXBlP3RoaXMudGl0bGVfY29udGFpbmVyLnRleHQodGhpcy5vcHRpb25zLnRpdGxlKTp0aGlzLnRpdGxlX2NvbnRhaW5lci5odG1sKHRoaXMub3B0aW9ucy50aXRsZSk7dGhpcy50ZXh0X2NvbnRhaW5lcj1iKFwiPGRpdiAvPlwiLHtcImNsYXNzXCI6XCJ1aS1wbm90aWZ5LXRleHRcIixcImFyaWEtcm9sZVwiOlwiYWxlcnRcIn0pLmFwcGVuZFRvKHRoaXMuY29udGFpbmVyKTshMT09PXRoaXMub3B0aW9ucy50ZXh0P3RoaXMudGV4dF9jb250YWluZXIuaGlkZSgpOnRoaXMub3B0aW9ucy50ZXh0X2VzY2FwZT90aGlzLnRleHRfY29udGFpbmVyLnRleHQodGhpcy5vcHRpb25zLnRleHQpOnRoaXMudGV4dF9jb250YWluZXIuaHRtbCh0aGlzLm9wdGlvbnMuaW5zZXJ0X2Jycz9cblN0cmluZyh0aGlzLm9wdGlvbnMudGV4dCkucmVwbGFjZSgvXFxuL2csXCI8YnIgLz5cIik6dGhpcy5vcHRpb25zLnRleHQpO1wic3RyaW5nXCI9PT10eXBlb2YgdGhpcy5vcHRpb25zLndpZHRoJiZ0aGlzLmVsZW0uY3NzKFwid2lkdGhcIix0aGlzLm9wdGlvbnMud2lkdGgpO1wic3RyaW5nXCI9PT10eXBlb2YgdGhpcy5vcHRpb25zLm1pbl9oZWlnaHQmJnRoaXMuY29udGFpbmVyLmNzcyhcIm1pbi1oZWlnaHRcIix0aGlzLm9wdGlvbnMubWluX2hlaWdodCk7ZC5ub3RpY2VzPVwidG9wXCI9PT10aGlzLm9wdGlvbnMuc3RhY2sucHVzaD9iLm1lcmdlKFt0aGlzXSxkLm5vdGljZXMpOmIubWVyZ2UoZC5ub3RpY2VzLFt0aGlzXSk7XCJ0b3BcIj09PXRoaXMub3B0aW9ucy5zdGFjay5wdXNoJiZ0aGlzLnF1ZXVlUG9zaXRpb24oITEsMSk7dGhpcy5vcHRpb25zLnN0YWNrLmFuaW1hdGlvbj0hMTt0aGlzLnJ1bk1vZHVsZXMoXCJpbml0XCIpO3RoaXMub3B0aW9ucy5hdXRvX2Rpc3BsYXkmJnRoaXMub3BlbigpO3JldHVybiB0aGlzfSxcbnVwZGF0ZTpmdW5jdGlvbihjKXt2YXIgYT10aGlzLm9wdGlvbnM7dGhpcy5wYXJzZU9wdGlvbnMoYSxjKTt0aGlzLmVsZW0ucmVtb3ZlQ2xhc3MoXCJ1aS1wbm90aWZ5LWZhZGUtc2xvdyB1aS1wbm90aWZ5LWZhZGUtbm9ybWFsIHVpLXBub3RpZnktZmFkZS1mYXN0XCIpO1wiZmFkZVwiPT09dGhpcy5vcHRpb25zLmFuaW1hdGlvbiYmdGhpcy5lbGVtLmFkZENsYXNzKFwidWktcG5vdGlmeS1mYWRlLVwiK3RoaXMub3B0aW9ucy5hbmltYXRlX3NwZWVkKTt0aGlzLm9wdGlvbnMuY29ybmVyY2xhc3MhPT1hLmNvcm5lcmNsYXNzJiZ0aGlzLmNvbnRhaW5lci5yZW1vdmVDbGFzcyhcInVpLWNvcm5lci1hbGwgXCIrYS5jb3JuZXJjbGFzcykuYWRkQ2xhc3ModGhpcy5vcHRpb25zLmNvcm5lcmNsYXNzKTt0aGlzLm9wdGlvbnMuc2hhZG93IT09YS5zaGFkb3cmJih0aGlzLm9wdGlvbnMuc2hhZG93P3RoaXMuY29udGFpbmVyLmFkZENsYXNzKFwidWktcG5vdGlmeS1zaGFkb3dcIik6dGhpcy5jb250YWluZXIucmVtb3ZlQ2xhc3MoXCJ1aS1wbm90aWZ5LXNoYWRvd1wiKSk7XG4hMT09PXRoaXMub3B0aW9ucy5hZGRjbGFzcz90aGlzLmVsZW0ucmVtb3ZlQ2xhc3MoYS5hZGRjbGFzcyk6dGhpcy5vcHRpb25zLmFkZGNsYXNzIT09YS5hZGRjbGFzcyYmdGhpcy5lbGVtLnJlbW92ZUNsYXNzKGEuYWRkY2xhc3MpLmFkZENsYXNzKHRoaXMub3B0aW9ucy5hZGRjbGFzcyk7ITE9PT10aGlzLm9wdGlvbnMudGl0bGU/dGhpcy50aXRsZV9jb250YWluZXIuc2xpZGVVcChcImZhc3RcIik6dGhpcy5vcHRpb25zLnRpdGxlIT09YS50aXRsZSYmKHRoaXMub3B0aW9ucy50aXRsZV9lc2NhcGU/dGhpcy50aXRsZV9jb250YWluZXIudGV4dCh0aGlzLm9wdGlvbnMudGl0bGUpOnRoaXMudGl0bGVfY29udGFpbmVyLmh0bWwodGhpcy5vcHRpb25zLnRpdGxlKSwhMT09PWEudGl0bGUmJnRoaXMudGl0bGVfY29udGFpbmVyLnNsaWRlRG93bigyMDApKTshMT09PXRoaXMub3B0aW9ucy50ZXh0P3RoaXMudGV4dF9jb250YWluZXIuc2xpZGVVcChcImZhc3RcIik6dGhpcy5vcHRpb25zLnRleHQhPT1cbmEudGV4dCYmKHRoaXMub3B0aW9ucy50ZXh0X2VzY2FwZT90aGlzLnRleHRfY29udGFpbmVyLnRleHQodGhpcy5vcHRpb25zLnRleHQpOnRoaXMudGV4dF9jb250YWluZXIuaHRtbCh0aGlzLm9wdGlvbnMuaW5zZXJ0X2Jycz9TdHJpbmcodGhpcy5vcHRpb25zLnRleHQpLnJlcGxhY2UoL1xcbi9nLFwiPGJyIC8+XCIpOnRoaXMub3B0aW9ucy50ZXh0KSwhMT09PWEudGV4dCYmdGhpcy50ZXh0X2NvbnRhaW5lci5zbGlkZURvd24oMjAwKSk7dGhpcy5vcHRpb25zLnR5cGUhPT1hLnR5cGUmJnRoaXMuY29udGFpbmVyLnJlbW92ZUNsYXNzKHRoaXMuc3R5bGVzLmVycm9yK1wiIFwiK3RoaXMuc3R5bGVzLm5vdGljZStcIiBcIit0aGlzLnN0eWxlcy5zdWNjZXNzK1wiIFwiK3RoaXMuc3R5bGVzLmluZm8pLmFkZENsYXNzKFwiZXJyb3JcIj09PXRoaXMub3B0aW9ucy50eXBlP3RoaXMuc3R5bGVzLmVycm9yOlwiaW5mb1wiPT09dGhpcy5vcHRpb25zLnR5cGU/dGhpcy5zdHlsZXMuaW5mbzpcInN1Y2Nlc3NcIj09PXRoaXMub3B0aW9ucy50eXBlP1xudGhpcy5zdHlsZXMuc3VjY2Vzczp0aGlzLnN0eWxlcy5ub3RpY2UpO2lmKHRoaXMub3B0aW9ucy5pY29uIT09YS5pY29ufHwhMD09PXRoaXMub3B0aW9ucy5pY29uJiZ0aGlzLm9wdGlvbnMudHlwZSE9PWEudHlwZSl0aGlzLmNvbnRhaW5lci5maW5kKFwiZGl2LnVpLXBub3RpZnktaWNvblwiKS5yZW1vdmUoKSwhMSE9PXRoaXMub3B0aW9ucy5pY29uJiZiKFwiPGRpdiAvPlwiLHtcImNsYXNzXCI6XCJ1aS1wbm90aWZ5LWljb25cIn0pLmFwcGVuZChiKFwiPHNwYW4gLz5cIix7XCJjbGFzc1wiOiEwPT09dGhpcy5vcHRpb25zLmljb24/XCJlcnJvclwiPT09dGhpcy5vcHRpb25zLnR5cGU/dGhpcy5zdHlsZXMuZXJyb3JfaWNvbjpcImluZm9cIj09PXRoaXMub3B0aW9ucy50eXBlP3RoaXMuc3R5bGVzLmluZm9faWNvbjpcInN1Y2Nlc3NcIj09PXRoaXMub3B0aW9ucy50eXBlP3RoaXMuc3R5bGVzLnN1Y2Nlc3NfaWNvbjp0aGlzLnN0eWxlcy5ub3RpY2VfaWNvbjp0aGlzLm9wdGlvbnMuaWNvbn0pKS5wcmVwZW5kVG8odGhpcy5jb250YWluZXIpO1xudGhpcy5vcHRpb25zLndpZHRoIT09YS53aWR0aCYmdGhpcy5lbGVtLmFuaW1hdGUoe3dpZHRoOnRoaXMub3B0aW9ucy53aWR0aH0pO3RoaXMub3B0aW9ucy5taW5faGVpZ2h0IT09YS5taW5faGVpZ2h0JiZ0aGlzLmNvbnRhaW5lci5hbmltYXRlKHttaW5IZWlnaHQ6dGhpcy5vcHRpb25zLm1pbl9oZWlnaHR9KTt0aGlzLm9wdGlvbnMuaGlkZT9hLmhpZGV8fHRoaXMucXVldWVSZW1vdmUoKTp0aGlzLmNhbmNlbFJlbW92ZSgpO3RoaXMucXVldWVQb3NpdGlvbighMCk7dGhpcy5ydW5Nb2R1bGVzKFwidXBkYXRlXCIsYSk7cmV0dXJuIHRoaXN9LG9wZW46ZnVuY3Rpb24oKXt0aGlzLnN0YXRlPVwib3BlbmluZ1wiO3RoaXMucnVuTW9kdWxlcyhcImJlZm9yZU9wZW5cIik7dmFyIGM9dGhpczt0aGlzLmVsZW0ucGFyZW50KCkubGVuZ3RofHx0aGlzLmVsZW0uYXBwZW5kVG8odGhpcy5vcHRpb25zLnN0YWNrLmNvbnRleHQ/dGhpcy5vcHRpb25zLnN0YWNrLmNvbnRleHQ6aCk7XCJ0b3BcIiE9PXRoaXMub3B0aW9ucy5zdGFjay5wdXNoJiZcbnRoaXMucG9zaXRpb24oITApO3RoaXMuYW5pbWF0ZUluKGZ1bmN0aW9uKCl7Yy5xdWV1ZVBvc2l0aW9uKCEwKTtjLm9wdGlvbnMuaGlkZSYmYy5xdWV1ZVJlbW92ZSgpO2Muc3RhdGU9XCJvcGVuXCI7Yy5ydW5Nb2R1bGVzKFwiYWZ0ZXJPcGVuXCIpfSk7cmV0dXJuIHRoaXN9LHJlbW92ZTpmdW5jdGlvbihjKXt0aGlzLnN0YXRlPVwiY2xvc2luZ1wiO3RoaXMudGltZXJIaWRlPSEhYzt0aGlzLnJ1bk1vZHVsZXMoXCJiZWZvcmVDbG9zZVwiKTt2YXIgYT10aGlzO3RoaXMudGltZXImJihsLmNsZWFyVGltZW91dCh0aGlzLnRpbWVyKSx0aGlzLnRpbWVyPW51bGwpO3RoaXMuYW5pbWF0ZU91dChmdW5jdGlvbigpe2Euc3RhdGU9XCJjbG9zZWRcIjthLnJ1bk1vZHVsZXMoXCJhZnRlckNsb3NlXCIpO2EucXVldWVQb3NpdGlvbighMCk7YS5vcHRpb25zLnJlbW92ZSYmYS5lbGVtLmRldGFjaCgpO2EucnVuTW9kdWxlcyhcImJlZm9yZURlc3Ryb3lcIik7aWYoYS5vcHRpb25zLmRlc3Ryb3kmJm51bGwhPT1kLm5vdGljZXMpe3ZhciBjPVxuYi5pbkFycmF5KGEsZC5ub3RpY2VzKTstMSE9PWMmJmQubm90aWNlcy5zcGxpY2UoYywxKX1hLnJ1bk1vZHVsZXMoXCJhZnRlckRlc3Ryb3lcIil9KTtyZXR1cm4gdGhpc30sZ2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuZWxlbX0scGFyc2VPcHRpb25zOmZ1bmN0aW9uKGMsYSl7dGhpcy5vcHRpb25zPWIuZXh0ZW5kKCEwLHt9LGQucHJvdG90eXBlLm9wdGlvbnMpO3RoaXMub3B0aW9ucy5zdGFjaz1kLnByb3RvdHlwZS5vcHRpb25zLnN0YWNrO2Zvcih2YXIgcD1bYyxhXSxtLGY9MDtmPHAubGVuZ3RoO2YrKyl7bT1wW2ZdO2lmKFwidW5kZWZpbmVkXCI9PT10eXBlb2YgbSlicmVhaztpZihcIm9iamVjdFwiIT09dHlwZW9mIG0pdGhpcy5vcHRpb25zLnRleHQ9bTtlbHNlIGZvcih2YXIgZSBpbiBtKXRoaXMubW9kdWxlc1tlXT9iLmV4dGVuZCghMCx0aGlzLm9wdGlvbnNbZV0sbVtlXSk6dGhpcy5vcHRpb25zW2VdPW1bZV19fSxhbmltYXRlSW46ZnVuY3Rpb24oYyl7dGhpcy5hbmltYXRpbmc9XG5cImluXCI7dmFyIGE9dGhpcztjPWZ1bmN0aW9uKCl7YS5hbmltVGltZXImJmNsZWFyVGltZW91dChhLmFuaW1UaW1lcik7XCJpblwiPT09YS5hbmltYXRpbmcmJihhLmVsZW0uaXMoXCI6dmlzaWJsZVwiKT8odGhpcyYmdGhpcy5jYWxsKCksYS5hbmltYXRpbmc9ITEpOmEuYW5pbVRpbWVyPXNldFRpbWVvdXQoYyw0MCkpfS5iaW5kKGMpO1wiZmFkZVwiPT09dGhpcy5vcHRpb25zLmFuaW1hdGlvbj8odGhpcy5lbGVtLm9uZShcIndlYmtpdFRyYW5zaXRpb25FbmQgbW96VHJhbnNpdGlvbkVuZCBNU1RyYW5zaXRpb25FbmQgb1RyYW5zaXRpb25FbmQgdHJhbnNpdGlvbmVuZFwiLGMpLmFkZENsYXNzKFwidWktcG5vdGlmeS1pblwiKSx0aGlzLmVsZW0uY3NzKFwib3BhY2l0eVwiKSx0aGlzLmVsZW0uYWRkQ2xhc3MoXCJ1aS1wbm90aWZ5LWZhZGUtaW5cIiksdGhpcy5hbmltVGltZXI9c2V0VGltZW91dChjLDY1MCkpOih0aGlzLmVsZW0uYWRkQ2xhc3MoXCJ1aS1wbm90aWZ5LWluXCIpLGMoKSl9LGFuaW1hdGVPdXQ6ZnVuY3Rpb24oYyl7dGhpcy5hbmltYXRpbmc9XG5cIm91dFwiO3ZhciBhPXRoaXM7Yz1mdW5jdGlvbigpe2EuYW5pbVRpbWVyJiZjbGVhclRpbWVvdXQoYS5hbmltVGltZXIpO1wib3V0XCI9PT1hLmFuaW1hdGluZyYmKFwiMFwiIT1hLmVsZW0uY3NzKFwib3BhY2l0eVwiKSYmYS5lbGVtLmlzKFwiOnZpc2libGVcIik/YS5hbmltVGltZXI9c2V0VGltZW91dChjLDQwKTooYS5lbGVtLnJlbW92ZUNsYXNzKFwidWktcG5vdGlmeS1pblwiKSx0aGlzJiZ0aGlzLmNhbGwoKSxhLmFuaW1hdGluZz0hMSkpfS5iaW5kKGMpO1wiZmFkZVwiPT09dGhpcy5vcHRpb25zLmFuaW1hdGlvbj8odGhpcy5lbGVtLm9uZShcIndlYmtpdFRyYW5zaXRpb25FbmQgbW96VHJhbnNpdGlvbkVuZCBNU1RyYW5zaXRpb25FbmQgb1RyYW5zaXRpb25FbmQgdHJhbnNpdGlvbmVuZFwiLGMpLnJlbW92ZUNsYXNzKFwidWktcG5vdGlmeS1mYWRlLWluXCIpLHRoaXMuYW5pbVRpbWVyPXNldFRpbWVvdXQoYyw2NTApKToodGhpcy5lbGVtLnJlbW92ZUNsYXNzKFwidWktcG5vdGlmeS1pblwiKSxjKCkpfSxwb3NpdGlvbjpmdW5jdGlvbihjKXt2YXIgYT1cbnRoaXMub3B0aW9ucy5zdGFjayxiPXRoaXMuZWxlbTtcInVuZGVmaW5lZFwiPT09dHlwZW9mIGEuY29udGV4dCYmKGEuY29udGV4dD1oKTtpZihhKXtcIm51bWJlclwiIT09dHlwZW9mIGEubmV4dHBvczEmJihhLm5leHRwb3MxPWEuZmlyc3Rwb3MxKTtcIm51bWJlclwiIT09dHlwZW9mIGEubmV4dHBvczImJihhLm5leHRwb3MyPWEuZmlyc3Rwb3MyKTtcIm51bWJlclwiIT09dHlwZW9mIGEuYWRkcG9zMiYmKGEuYWRkcG9zMj0wKTt2YXIgZD0hYi5oYXNDbGFzcyhcInVpLXBub3RpZnktaW5cIik7aWYoIWR8fGMpe2EubW9kYWwmJihhLm92ZXJsYXk/YS5vdmVybGF5LnNob3coKTphLm92ZXJsYXk9cyhhKSk7Yi5hZGRDbGFzcyhcInVpLXBub3RpZnktbW92ZVwiKTt2YXIgZjtzd2l0Y2goYS5kaXIxKXtjYXNlIFwiZG93blwiOmY9XCJ0b3BcIjticmVhaztjYXNlIFwidXBcIjpmPVwiYm90dG9tXCI7YnJlYWs7Y2FzZSBcImxlZnRcIjpmPVwicmlnaHRcIjticmVhaztjYXNlIFwicmlnaHRcIjpmPVwibGVmdFwifWM9cGFyc2VJbnQoYi5jc3MoZikucmVwbGFjZSgvKD86XFwuLip8W14wLTkuXSkvZyxcblwiXCIpKTtpc05hTihjKSYmKGM9MCk7XCJ1bmRlZmluZWRcIiE9PXR5cGVvZiBhLmZpcnN0cG9zMXx8ZHx8KGEuZmlyc3Rwb3MxPWMsYS5uZXh0cG9zMT1hLmZpcnN0cG9zMSk7dmFyIGU7c3dpdGNoKGEuZGlyMil7Y2FzZSBcImRvd25cIjplPVwidG9wXCI7YnJlYWs7Y2FzZSBcInVwXCI6ZT1cImJvdHRvbVwiO2JyZWFrO2Nhc2UgXCJsZWZ0XCI6ZT1cInJpZ2h0XCI7YnJlYWs7Y2FzZSBcInJpZ2h0XCI6ZT1cImxlZnRcIn1jPXBhcnNlSW50KGIuY3NzKGUpLnJlcGxhY2UoLyg/OlxcLi4qfFteMC05Ll0pL2csXCJcIikpO2lzTmFOKGMpJiYoYz0wKTtcInVuZGVmaW5lZFwiIT09dHlwZW9mIGEuZmlyc3Rwb3MyfHxkfHwoYS5maXJzdHBvczI9YyxhLm5leHRwb3MyPWEuZmlyc3Rwb3MyKTtpZihcImRvd25cIj09PWEuZGlyMSYmYS5uZXh0cG9zMStiLmhlaWdodCgpPihhLmNvbnRleHQuaXMoaCk/bi5oZWlnaHQoKTphLmNvbnRleHQucHJvcChcInNjcm9sbEhlaWdodFwiKSl8fFwidXBcIj09PWEuZGlyMSYmYS5uZXh0cG9zMStiLmhlaWdodCgpPlxuKGEuY29udGV4dC5pcyhoKT9uLmhlaWdodCgpOmEuY29udGV4dC5wcm9wKFwic2Nyb2xsSGVpZ2h0XCIpKXx8XCJsZWZ0XCI9PT1hLmRpcjEmJmEubmV4dHBvczErYi53aWR0aCgpPihhLmNvbnRleHQuaXMoaCk/bi53aWR0aCgpOmEuY29udGV4dC5wcm9wKFwic2Nyb2xsV2lkdGhcIikpfHxcInJpZ2h0XCI9PT1hLmRpcjEmJmEubmV4dHBvczErYi53aWR0aCgpPihhLmNvbnRleHQuaXMoaCk/bi53aWR0aCgpOmEuY29udGV4dC5wcm9wKFwic2Nyb2xsV2lkdGhcIikpKWEubmV4dHBvczE9YS5maXJzdHBvczEsYS5uZXh0cG9zMis9YS5hZGRwb3MyKyhcInVuZGVmaW5lZFwiPT09dHlwZW9mIGEuc3BhY2luZzI/MjU6YS5zcGFjaW5nMiksYS5hZGRwb3MyPTA7XCJudW1iZXJcIj09PXR5cGVvZiBhLm5leHRwb3MyJiYoYS5hbmltYXRpb24/Yi5jc3MoZSxhLm5leHRwb3MyK1wicHhcIik6KGIucmVtb3ZlQ2xhc3MoXCJ1aS1wbm90aWZ5LW1vdmVcIiksYi5jc3MoZSxhLm5leHRwb3MyK1wicHhcIiksYi5jc3MoZSksYi5hZGRDbGFzcyhcInVpLXBub3RpZnktbW92ZVwiKSkpO1xuc3dpdGNoKGEuZGlyMil7Y2FzZSBcImRvd25cIjpjYXNlIFwidXBcIjpiLm91dGVySGVpZ2h0KCEwKT5hLmFkZHBvczImJihhLmFkZHBvczI9Yi5oZWlnaHQoKSk7YnJlYWs7Y2FzZSBcImxlZnRcIjpjYXNlIFwicmlnaHRcIjpiLm91dGVyV2lkdGgoITApPmEuYWRkcG9zMiYmKGEuYWRkcG9zMj1iLndpZHRoKCkpfVwibnVtYmVyXCI9PT10eXBlb2YgYS5uZXh0cG9zMSYmKGEuYW5pbWF0aW9uP2IuY3NzKGYsYS5uZXh0cG9zMStcInB4XCIpOihiLnJlbW92ZUNsYXNzKFwidWktcG5vdGlmeS1tb3ZlXCIpLGIuY3NzKGYsYS5uZXh0cG9zMStcInB4XCIpLGIuY3NzKGYpLGIuYWRkQ2xhc3MoXCJ1aS1wbm90aWZ5LW1vdmVcIikpKTtzd2l0Y2goYS5kaXIxKXtjYXNlIFwiZG93blwiOmNhc2UgXCJ1cFwiOmEubmV4dHBvczErPWIuaGVpZ2h0KCkrKFwidW5kZWZpbmVkXCI9PT10eXBlb2YgYS5zcGFjaW5nMT8yNTphLnNwYWNpbmcxKTticmVhaztjYXNlIFwibGVmdFwiOmNhc2UgXCJyaWdodFwiOmEubmV4dHBvczErPWIud2lkdGgoKSsoXCJ1bmRlZmluZWRcIj09PVxudHlwZW9mIGEuc3BhY2luZzE/MjU6YS5zcGFjaW5nMSl9fXJldHVybiB0aGlzfX0scXVldWVQb3NpdGlvbjpmdW5jdGlvbihiLGEpe2cmJmNsZWFyVGltZW91dChnKTthfHwoYT0xMCk7Zz1zZXRUaW1lb3V0KGZ1bmN0aW9uKCl7ZC5wb3NpdGlvbkFsbChiKX0sYSk7cmV0dXJuIHRoaXN9LGNhbmNlbFJlbW92ZTpmdW5jdGlvbigpe3RoaXMudGltZXImJmwuY2xlYXJUaW1lb3V0KHRoaXMudGltZXIpO3RoaXMuYW5pbVRpbWVyJiZsLmNsZWFyVGltZW91dCh0aGlzLmFuaW1UaW1lcik7XCJjbG9zaW5nXCI9PT10aGlzLnN0YXRlJiYodGhpcy5zdGF0ZT1cIm9wZW5cIix0aGlzLmFuaW1hdGluZz0hMSx0aGlzLmVsZW0uYWRkQ2xhc3MoXCJ1aS1wbm90aWZ5LWluXCIpLFwiZmFkZVwiPT09dGhpcy5vcHRpb25zLmFuaW1hdGlvbiYmdGhpcy5lbGVtLmFkZENsYXNzKFwidWktcG5vdGlmeS1mYWRlLWluXCIpKTtyZXR1cm4gdGhpc30scXVldWVSZW1vdmU6ZnVuY3Rpb24oKXt2YXIgYj10aGlzO3RoaXMuY2FuY2VsUmVtb3ZlKCk7XG50aGlzLnRpbWVyPWwuc2V0VGltZW91dChmdW5jdGlvbigpe2IucmVtb3ZlKCEwKX0saXNOYU4odGhpcy5vcHRpb25zLmRlbGF5KT8wOnRoaXMub3B0aW9ucy5kZWxheSk7cmV0dXJuIHRoaXN9fSk7Yi5leHRlbmQoZCx7bm90aWNlczpbXSxyZWxvYWQ6cSxyZW1vdmVBbGw6ZnVuY3Rpb24oKXtiLmVhY2goZC5ub3RpY2VzLGZ1bmN0aW9uKCl7dGhpcy5yZW1vdmUmJnRoaXMucmVtb3ZlKCExKX0pfSxyZW1vdmVTdGFjazpmdW5jdGlvbihjKXtiLmVhY2goZC5ub3RpY2VzLGZ1bmN0aW9uKCl7dGhpcy5yZW1vdmUmJnRoaXMub3B0aW9ucy5zdGFjaz09PWMmJnRoaXMucmVtb3ZlKCExKX0pfSxwb3NpdGlvbkFsbDpmdW5jdGlvbihjKXtnJiZjbGVhclRpbWVvdXQoZyk7Zz1udWxsO2lmKGQubm90aWNlcyYmZC5ub3RpY2VzLmxlbmd0aCliLmVhY2goZC5ub3RpY2VzLGZ1bmN0aW9uKCl7dmFyIGE9dGhpcy5vcHRpb25zLnN0YWNrO2EmJihhLm92ZXJsYXkmJmEub3ZlcmxheS5oaWRlKCksYS5uZXh0cG9zMT1cbmEuZmlyc3Rwb3MxLGEubmV4dHBvczI9YS5maXJzdHBvczIsYS5hZGRwb3MyPTAsYS5hbmltYXRpb249Yyl9KSxiLmVhY2goZC5ub3RpY2VzLGZ1bmN0aW9uKCl7dGhpcy5wb3NpdGlvbigpfSk7ZWxzZXt2YXIgYT1kLnByb3RvdHlwZS5vcHRpb25zLnN0YWNrO2EmJihkZWxldGUgYS5uZXh0cG9zMSxkZWxldGUgYS5uZXh0cG9zMil9fSxzdHlsaW5nOnticmlnaHR0aGVtZTp7Y29udGFpbmVyOlwiYnJpZ2h0dGhlbWVcIixub3RpY2U6XCJicmlnaHR0aGVtZS1ub3RpY2VcIixub3RpY2VfaWNvbjpcImJyaWdodHRoZW1lLWljb24tbm90aWNlXCIsaW5mbzpcImJyaWdodHRoZW1lLWluZm9cIixpbmZvX2ljb246XCJicmlnaHR0aGVtZS1pY29uLWluZm9cIixzdWNjZXNzOlwiYnJpZ2h0dGhlbWUtc3VjY2Vzc1wiLHN1Y2Nlc3NfaWNvbjpcImJyaWdodHRoZW1lLWljb24tc3VjY2Vzc1wiLGVycm9yOlwiYnJpZ2h0dGhlbWUtZXJyb3JcIixlcnJvcl9pY29uOlwiYnJpZ2h0dGhlbWUtaWNvbi1lcnJvclwifSxqcXVlcnl1aTp7Y29udGFpbmVyOlwidWktd2lkZ2V0IHVpLXdpZGdldC1jb250ZW50IHVpLWNvcm5lci1hbGxcIixcbm5vdGljZTpcInVpLXN0YXRlLWhpZ2hsaWdodFwiLG5vdGljZV9pY29uOlwidWktaWNvbiB1aS1pY29uLWluZm9cIixpbmZvOlwiXCIsaW5mb19pY29uOlwidWktaWNvbiB1aS1pY29uLWluZm9cIixzdWNjZXNzOlwidWktc3RhdGUtZGVmYXVsdFwiLHN1Y2Nlc3NfaWNvbjpcInVpLWljb24gdWktaWNvbi1jaXJjbGUtY2hlY2tcIixlcnJvcjpcInVpLXN0YXRlLWVycm9yXCIsZXJyb3JfaWNvbjpcInVpLWljb24gdWktaWNvbi1hbGVydFwifSxib290c3RyYXAzOntjb250YWluZXI6XCJhbGVydFwiLG5vdGljZTpcImFsZXJ0LXdhcm5pbmdcIixub3RpY2VfaWNvbjpcImdseXBoaWNvbiBnbHlwaGljb24tZXhjbGFtYXRpb24tc2lnblwiLGluZm86XCJhbGVydC1pbmZvXCIsaW5mb19pY29uOlwiZ2x5cGhpY29uIGdseXBoaWNvbi1pbmZvLXNpZ25cIixzdWNjZXNzOlwiYWxlcnQtc3VjY2Vzc1wiLHN1Y2Nlc3NfaWNvbjpcImdseXBoaWNvbiBnbHlwaGljb24tb2stc2lnblwiLGVycm9yOlwiYWxlcnQtZGFuZ2VyXCIsZXJyb3JfaWNvbjpcImdseXBoaWNvbiBnbHlwaGljb24td2FybmluZy1zaWduXCJ9fX0pO1xuZC5zdHlsaW5nLmZvbnRhd2Vzb21lPWIuZXh0ZW5kKHt9LGQuc3R5bGluZy5ib290c3RyYXAzKTtiLmV4dGVuZChkLnN0eWxpbmcuZm9udGF3ZXNvbWUse25vdGljZV9pY29uOlwiZmEgZmEtZXhjbGFtYXRpb24tY2lyY2xlXCIsaW5mb19pY29uOlwiZmEgZmEtaW5mb1wiLHN1Y2Nlc3NfaWNvbjpcImZhIGZhLWNoZWNrXCIsZXJyb3JfaWNvbjpcImZhIGZhLXdhcm5pbmdcIn0pO2wuZG9jdW1lbnQuYm9keT9yKCk6YihyKTtyZXR1cm4gZH07cmV0dXJuIHEoayl9KTtcbiIsInZhciBrZXkgPSByZXF1aXJlKFwia2V5bWFzdGVyXCIpO1xyXG52YXIgbWVzc2FnZSA9IHJlcXVpcmUoXCJtb2R1bGVzL21lc3NhZ2VcIik7XHJcblxyXG4vLyBBZGQgc2hvcnRjdXQgY29lXHJcbnZhciBfYWRkU2hvcnRjdXQgPSBmdW5jdGlvbihzZWxlY3RlZEtleSxjYikge1xyXG4gIGtleShzZWxlY3RlZEtleSwgY2IpO1xyXG59XHJcblxyXG52YXIgX2NsaWNrQnV0dG9uID0gZnVuY3Rpb24oc2VsZWN0b3IpIHtcclxuICAkKHNlbGVjdG9yKS50cmlnZ2VyKCdjbGljaycpO1xyXG4gIHJldHVybiBmYWxzZTtcclxufVxyXG5cclxuLy8gQWRkIHNob3J0Y3V0cyBmb3Igc2VsZWN0b3JzXHJcbnZhciBkZWZhdWx0U2hvcnRjdXRzID0gZnVuY3Rpb24oKSB7XHJcbiAgX2FkZFNob3J0Y3V0KCdjdHJsK3MnLCBmdW5jdGlvbihldmVudCxoYW5kbGVyKXtcclxuICAgIF9jbGlja0J1dHRvbignLnQtQnV0dG9uW29uY2xpY2sqPVwiXFwnQ1JFQVRFXFwnXCJdLC50LUJ1dHRvbiNDUkVBVEUnKTtcclxuICB9KTtcclxuICBfYWRkU2hvcnRjdXQoJ2N0cmwrcycsIGZ1bmN0aW9uKGV2ZW50LGhhbmRsZXIpe1xyXG4gICAgX2NsaWNrQnV0dG9uKCcudC1CdXR0b25bb25jbGljayo9XCJcXCdTQVZFXFwnXCJdLC50LUJ1dHRvbiNTQVZFJyk7XHJcbiAgfSk7XHJcbiAgX2FkZFNob3J0Y3V0KCdjdHJsK2QnLCBmdW5jdGlvbihldmVudCxoYW5kbGVyKXtcclxuICAgIF9jbGlja0J1dHRvbignLnQtQnV0dG9uW29uY2xpY2sqPVwiXFwnREVMRVRFXFwnXCJdLC50LUJ1dHRvbiNERUxFVEUnKTtcclxuICB9KTtcclxuICBfYWRkU2hvcnRjdXQoJ2N0cmwrbScsIGZ1bmN0aW9uKGV2ZW50LGhhbmRsZXIpe1xyXG4gICAgbWVzc2FnZS5pbmZvKHtcclxuICAgICAgdGl0bGU6IFwiQSBrZXkgd2FzIHByZXNzZWRcIixcclxuICAgICAgdGV4dDogXCJjdHJsK21cIlxyXG4gICAgfSk7XHJcbiAgfSk7XHJcbn07XHJcblxyXG5tb2R1bGUuZXhwb3J0cyA9IHtcclxuICBkZWZhdWx0U2hvcnRjdXRzOiBkZWZhdWx0U2hvcnRjdXRzXHJcbn1cclxuIiwidmFyICQgPSAod2luZG93LiQpOyAvLyBiZWluZyBzaGltbWVkXHJcbnZhciBQTm90aWZ5ID0gcmVxdWlyZShcInBub3RpZnlcIik7XHJcblxyXG5cclxuLy8gQnV0dG9uIGRlZmF1bHRzXHJcbnZhciBkZWZhdWx0cyA9IHtcclxuICBoaWRlOiBmYWxzZSxcclxuICBjbG9zZXI6IHRydWUsXHJcbiAgYnV0dG9uczoge1xyXG4gICAgY2xvc2VyX2hvdmVyOiBmYWxzZSxcclxuICAgIHN0aWNrZXI6IGZhbHNlLFxyXG4gICAgbGFiZWxzOiB7XHJcbiAgICAgIGNsb3NlOiAnU2x1aXQgbWVsZGluZydcclxuICAgIH1cclxuICB9XHJcbn07XHJcblxyXG5cclxudmFyIGdldFNldHRpbmdzID0gZnVuY3Rpb24ob3B0aW9ucykge1xyXG5cclxuICBzd2l0Y2ggKHR5cGVvZiBvcHRpb25zKSB7XHJcbiAgICBjYXNlIFwic3RyaW5nXCI6XHJcbiAgICAgIHJldHVybiAkLmV4dGVuZCh7XHJcbiAgICAgICAgdGl0bGU6IG9wdGlvbnNcclxuICAgICAgfSwgZGVmYXVsdHMpO1xyXG4gICAgY2FzZSBcIm9iamVjdFwiOlxyXG4gICAgICByZXR1cm4gJC5leHRlbmQob3B0aW9ucywgZGVmYXVsdHMpO1xyXG4gICAgZGVmYXVsdDpcclxuICAgICAgcmV0dXJuIGRlZmF1bHRzO1xyXG4gIH07XHJcblxyXG59O1xyXG5cclxudmFyIGluZm8gPSBmdW5jdGlvbihvcHRpb25zKSB7XHJcbiAgcmV0dXJuIG5ldyBQTm90aWZ5KCQuZXh0ZW5kKGdldFNldHRpbmdzKG9wdGlvbnMpLCB7XHJcbiAgICB0eXBlOiAnaW5mbycsXHJcbiAgICBoaWRlOiB0cnVlXHJcbiAgfSkpO1xyXG59O1xyXG5cclxudmFyIHN1Y2Nlc3MgPSBmdW5jdGlvbihvcHRpb25zKSB7XHJcbiAgcmV0dXJuIG5ldyBQTm90aWZ5KCQuZXh0ZW5kKGdldFNldHRpbmdzKG9wdGlvbnMpLCB7XHJcbiAgICB0eXBlOiAnc3VjY2VzcycsXHJcbiAgICBoaWRlOiB0cnVlXHJcbiAgfSkpO1xyXG59O1xyXG5cclxudmFyIHdhcm5pbmcgPSBmdW5jdGlvbihvcHRpb25zKSB7XHJcbiAgcmV0dXJuIG5ldyBQTm90aWZ5KCQuZXh0ZW5kKGdldFNldHRpbmdzKG9wdGlvbnMpLCB7XHJcbiAgICB0eXBlOiAnd2FybmluZydcclxuICB9KSk7XHJcbn07XHJcblxyXG52YXIgZXJyb3IgPSBmdW5jdGlvbihvcHRpb25zKSB7XHJcbiAgcmV0dXJuIG5ldyBQTm90aWZ5KCQuZXh0ZW5kKGdldFNldHRpbmdzKG9wdGlvbnMpLCB7XHJcbiAgICB0eXBlOiAnZXJyb3InXHJcbiAgfSkpO1xyXG59O1xyXG5cclxubW9kdWxlLmV4cG9ydHMgPSB7XHJcbiAgaW5mbzogaW5mbyxcclxuICBzdWNjZXNzOiBzdWNjZXNzLFxyXG4gIHdhcm5pbmc6IHdhcm5pbmcsXHJcbiAgZXJyb3I6IGVycm9yXHJcbn07XHJcbiIsIiQud2lkZ2V0KFwiY3VzdG9tLmN1c3RvbVJlcG9ydFwiLCB7XHJcbiAgb3B0aW9uczoge1xyXG4gICAgZXhjZXB0Q2xhc3M6ICduby1yb3ctbGluaycsXHJcbiAgICBhY3RpdmVDbGFzczogJ2FjdGl2ZScsXHJcbiAgICBjb2x1bW5zOiBbXSxcclxuICAgIHJvd2NsaWNrOiBmdW5jdGlvbihlLCBkYXRhKSB7XHJcbiAgICAgICQodGhpcykuY3VzdG9tUmVwb3J0KCdvcGVuTGluaycsIGUpO1xyXG4gICAgfVxyXG4gIH0sXHJcblxyXG4gIF9jcmVhdGU6IGZ1bmN0aW9uKCkge1xyXG4gICAgdmFyIHNlbGYgPSB0aGlzO1xyXG4gICAgLy8gQ2hlY2sgaWYgcmVwb3J0IG1heSBoYXZlIGEgcm93IGxpbmtcclxuICAgIGlmICh0aGlzLl9yb3dMaW5rQWxsb3dlZCkge1xyXG4gICAgICB0aGlzLl9pbml0Um93Q2xpY2soKTtcclxuICAgIH1cclxuXHJcbiAgICAkKHRoaXMuZWxlbWVudCkub24oJ2FwZXhhZnRlcnJlZnJlc2gnLGZ1bmN0aW9uKGUpe1xyXG4gICAgICBzZWxmLl9hcGV4YWZ0ZXJyZWZyZXNoKCk7XHJcbiAgICB9KTtcclxuXHJcbiAgfSxcclxuXHJcbiAgX2FwZXhhZnRlcnJlZnJlc2g6IGZ1bmN0aW9uKCkge1xyXG4gICAgLy8gQ2hlY2sgaWYgcmVwb3J0IG1heSBoYXZlIGEgcm93IGxpbmtcclxuICAgIGlmICh0aGlzLl9yb3dMaW5rQWxsb3dlZCkge1xyXG4gICAgICB0aGlzLl9pbml0Um93Q2xpY2soKTtcclxuICAgIH1cclxuICB9LFxyXG5cclxuICBfaW5pdFJvd0NsaWNrOiBmdW5jdGlvbihjYikge1xyXG5cclxuICAgIHZhciBzZWxmID0gdGhpcztcclxuICAgIHZhciBkYXRhO1xyXG4gICAgY2IgPSAkLnByb3h5KGNiLCBzZWxmKTtcclxuXHJcbiAgICAvLyBSZW1vdmUgcHJldmlvdXMgaGFuZGxlcnNcclxuICAgIHRoaXMuX29mZih0aGlzLmVsZW1lbnQsICdjbGljayB0ciB0ZDpub3QoOmhhcyhhKSknKTtcclxuICAgIHRoaXMuX29mZih0aGlzLmVsZW1lbnQsICdob3ZlciB0ciB0ZDpub3QoOmhhcyhhKSknKTtcclxuXHJcbiAgICAvLyBBZGQgbmV3IGhhbmRsZXJcclxuICAgIHRoaXMuX29uKHRoaXMuZWxlbWVudCwge1xyXG4gICAgICAnbW91c2VlbnRlciB0ciB0ZDpub3QoOmhhcyhhKSknOiBmdW5jdGlvbihlKSB7XHJcbiAgICAgICAgJChlLnRhcmdldCkuY3NzKCdjdXJzb3InLCAncG9pbnRlcicpO1xyXG4gICAgICB9XHJcbiAgICB9KTtcclxuXHJcbiAgICAvLyBBZGQgbmV3IGhhbmRsZXJcclxuICAgIHRoaXMuX29uKHRoaXMuZWxlbWVudCwge1xyXG4gICAgICAnY2xpY2sgdHIgdGQ6bm90KDpoYXMoYSkpJzogZnVuY3Rpb24oZSkge1xyXG4gICAgICAgIHNlbGYuX3RyaWdnZXIoJ3Jvd2NsaWNrJywgZSwgZGF0YSk7XHJcbiAgICAgICAgZS5zdG9wSW1tZWRpYXRlUHJvcGFnYXRpb24oKTtcclxuICAgICAgfVxyXG4gICAgfSk7XHJcblxyXG4gIH0sXHJcblxyXG4gIC8vIFVzZSBhbiBhIGhyZWYgdmFsdWUgdG8gcmVkaXJlY3Qgb24gcm93IGNsaWNrIGluIHJlcG9ydFxyXG4gIG9wZW5MaW5rOiBmdW5jdGlvbihldmVudCwgb3B0aW9ucykge1xyXG5cclxuICAgIHZhciBzZWxmID0gdGhpcztcclxuXHJcbiAgICB2YXIgZGVmYXVsdHMgPSB7XHJcbiAgICAgIGFQb3M6IDAgLy8gd2hpY2ggXCJhXCIgZWxlbWVudCBjb250YWlucyB0aGUgbGlua1xyXG4gICAgfVxyXG5cclxuICAgIHZhciBzZXR0aW5ncyA9ICQuZXh0ZW5kKG9wdGlvbnMsIGRlZmF1bHRzKTtcclxuXHJcbiAgICAvLyBHZXQgbGlua1xyXG4gICAgdmFyICRsaW5rRWxlbSA9IHNlbGYuX2dldExpbmtFbGVtZW50KHNldHRpbmdzLCBldmVudC5jdXJyZW50VGFyZ2V0KTtcclxuICAgIHZhciBocmVmID0gJGxpbmtFbGVtLmF0dHIoJ2hyZWYnKTtcclxuXHJcbiAgICBpZiAoaHJlZiAhPSB1bmRlZmluZWQpIHtcclxuICAgICAgd2luZG93LmxvY2F0aW9uLmhyZWYgPSBocmVmO1xyXG4gICAgfVxyXG4gIH0sXHJcblxyXG4gIF9nZXRMaW5rRWxlbWVudDogZnVuY3Rpb24ob3B0aW9ucywgdGFyZ2V0KSB7XHJcblxyXG4gICAgdmFyIGxpbmtzID0gJCh0YXJnZXQpLmNsb3Nlc3QoJ3RyJykuZmluZCgndGQ6aGFzKGEpJyk7XHJcblxyXG4gICAgLy8gUmFpc2UgZXhjZXB0aW9uIGlmIHBvc2l0aW9uIG9mIGEgZWxlbWVudCBpcyBpbnZhbGlkXHJcbiAgICBpZiAobGlua3MubGVuZ3RoIDwgb3B0aW9ucy5hUG9zKSB7XHJcbiAgICAgIGFwZXguZGVidWcuZXJyb3IoJ0V4Y2VwdGlvbjogJywgb3B0aW9ucy5hUG9zICsgJ3RoIFwiYVwiIGVsZW1lbnQgaXMgbm90IGZvdW5kIGluIHJlcG9ydCByb3cuJyk7XHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICByZXR1cm4gJChsaW5rc1tvcHRpb25zLmFQb3NdKS5maW5kKCdhJyk7XHJcbiAgICB9XHJcblxyXG4gIH0sXHJcblxyXG4gIC8vIF9yb3dMaW5rQWxsb3dlZCByZXR1cm5zIGJvb2xlYW5cclxuICBfcm93TGlua0FsbG93ZWQ6IGZ1bmN0aW9uKCkge1xyXG4gICAgcmV0dXJuICEkKHRoaXMuZWxlbWVudCkuaGFzQ2xhc3ModGhpcy5vcHRpb25zLmV4Y2VwdENsYXNzKTtcclxuICB9LFxyXG5cclxuICBfc2V0QWN0aXZlUm93OiBmdW5jdGlvbigkcm93KSB7XHJcbiAgICAkKCRyb3cpLmNsb3Nlc3QoJ3RhYmxlJykuZmluZCgndGQuJyt0aGlzLm9wdGlvbnMuYWN0aXZlQ2xhc3MpLnJlbW92ZUNsYXNzKHRoaXMub3B0aW9ucy5hY3RpdmVDbGFzcyk7XHJcbiAgICAkKCRyb3cpLmZpbmQoJ3RkJykuYWRkQ2xhc3ModGhpcy5vcHRpb25zLmFjdGl2ZUNsYXNzKTtcclxuICB9LFxyXG5cclxuICBhY3RpdmVSb3c6IGZ1bmN0aW9uKCkge1xyXG4gICAgcmV0dXJuICQodGhpcy5lbGVtZW50KS5maW5kKCd0ZC4nK3RoaXMub3B0aW9ucy5hY3RpdmVDbGFzcykuY2xvc2VzdCgndHInKTtcclxuICB9XHJcblxyXG59KTtcclxuIl19
550 |
--------------------------------------------------------------------------------