├── .editorconfig ├── .eslintrc ├── .gitignore ├── .prettierrc ├── Sketch Resize.sketchplugin └── Contents │ ├── Resources │ ├── _webpack_resources │ │ ├── 43aa738f582ce37c61cb81dcdf421b63.html │ │ └── 9a9109f28172ef2d57fd3a1b7a72d03b.css │ ├── resize.png │ ├── script.js │ ├── script.js.map │ └── sketch-icon.png │ └── Sketch │ ├── main.js │ ├── main.js.map │ └── manifest.json ├── appcast.xml ├── assets ├── resize.png └── sketch-icon.png ├── images ├── img-header.jpg ├── img-sketch-resize.jpg ├── img-sketch-reverse.jpg ├── img-sketch-runner.jpg ├── img-sketch-styles-generator.jpg └── img-usage.gif ├── license ├── package.json ├── readme.md ├── resources └── webview │ ├── script.js │ ├── styles.css │ └── webview.html ├── src ├── actions │ └── resize.js ├── main.js └── manifest.json ├── webpack.skpm.config.js └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_size = 2 7 | indent_style = space 8 | insert_final_newline = true 9 | max_line_length = null 10 | trim_trailing_whitespace = true 11 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "amd": true, 5 | "node": true, 6 | "es6": true 7 | }, 8 | "parser": "babel-eslint", 9 | "parserOptions": { 10 | "ecmaVersion": 2017, 11 | "sourceType": "module", 12 | "ecmaFeatures": { 13 | "modules": true 14 | } 15 | }, 16 | "extends": ["eslint:recommended"], 17 | "rules": { 18 | "no-console": "off" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build 3 | .DS_Store 4 | .DS_Store? 5 | ._* 6 | .env.local 7 | .env.development.local 8 | .env.test.local 9 | .env.production.local 10 | yarn-debug.log* 11 | yarn-error.log* 12 | npm-debug.log* 13 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "es5", 3 | "tabWidth": 2, 4 | "semi": true, 5 | "singleQuote": true 6 | } 7 | -------------------------------------------------------------------------------- /Sketch Resize.sketchplugin/Contents/Resources/_webpack_resources/43aa738f582ce37c61cb81dcdf421b63.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Sketch Resize 7 | 8 | 9 | 10 | 11 |
12 | Resize Element(s) 13 |
14 | 15 |
16 |

Enter the new width/height of every selected element.

17 |
18 | 19 | 20 |
21 |

22 | Use %w, and %h as wildcards for math operations (i.e. %w + 24). The resizing is unconstrained, 23 | and doesn't affect any other property like corner radius, border thickness, shadow size, etc. 24 |

25 |
26 | 27 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /Sketch Resize.sketchplugin/Contents/Resources/_webpack_resources/9a9109f28172ef2d57fd3a1b7a72d03b.css: -------------------------------------------------------------------------------- 1 | html { 2 | box-sizing: border-box; 3 | overflow: hidden; 4 | padding: 1px; 5 | color: #9c9c9c; 6 | background: #171717; 7 | font-family: -apple-system; 8 | font-size: 13px; 9 | line-height: 20px; 10 | cursor: default; 11 | } 12 | 13 | *, 14 | *:before, 15 | *:after { 16 | position: relative; 17 | box-sizing: inherit; 18 | margin: 0; 19 | padding: 0; 20 | -webkit-user-select: none; 21 | user-select: none; 22 | } 23 | 24 | input, 25 | textarea { 26 | -webkit-user-select: auto; 27 | user-select: auto; 28 | } 29 | 30 | /* -------------------------------- */ 31 | /* header/menubar */ 32 | /* -------------------------------- */ 33 | 34 | header { 35 | border-bottom: 1px solid #000000; 36 | height: 37px; 37 | color: rgba(255, 255, 255, 0.7); 38 | background-color: #242424; 39 | font-weight: 600; 40 | line-height: 37px; 41 | text-align: center; 42 | } 43 | 44 | /* -------------------------------- */ 45 | /* section/content */ 46 | /* -------------------------------- */ 47 | 48 | section { 49 | padding: 24px; 50 | } 51 | 52 | section > p.info { 53 | margin-bottom: 16px; 54 | font-weight: 400; 55 | color: rgba(255, 255, 255, 0.7); 56 | } 57 | 58 | section > p.info > strong { 59 | color: #ffffff; 60 | } 61 | 62 | section > div { 63 | font-size: 0; 64 | } 65 | 66 | section > div > input { 67 | display: inline-block; 68 | margin-bottom: 16px; 69 | border: 2px solid #242424; 70 | border-radius: 4px; 71 | padding: 12px; 72 | width: calc(50% - 4px); 73 | background-color: #242424; 74 | color: #ffffff; 75 | font-size: 13px; 76 | } 77 | 78 | section > div > input:focus { 79 | outline: 0; 80 | border: 2px solid #1c7ed5; 81 | } 82 | 83 | section > div > input::placeholder { 84 | color: rgba(255, 255, 255, 0.4); 85 | } 86 | 87 | section > div > input#width { 88 | margin-right: 8px; 89 | } 90 | 91 | section > p.tip { 92 | font-size: 12px; 93 | font-weight: 400; 94 | color: rgba(255, 255, 255, 0.4); 95 | } 96 | 97 | /* -------------------------------- */ 98 | /* footer/actions */ 99 | /* -------------------------------- */ 100 | 101 | footer { 102 | border-top: 1px solid rgba(255, 255, 255, 0.1); 103 | padding: 16px 24px; 104 | background-color: #242424; 105 | font-size: 0; 106 | text-align: right; 107 | } 108 | 109 | footer > button { 110 | display: inline-block; 111 | height: 40px; 112 | border: 0; 113 | border-radius: 4px; 114 | padding: 0 12px; 115 | color: #ffffff; 116 | font-size: 13px; 117 | line-height: 16px; 118 | font-weight: 600; 119 | } 120 | 121 | footer > button:focus { 122 | outline: 0; 123 | } 124 | 125 | footer > button:focus:after { 126 | content: ''; 127 | display: block; 128 | position: absolute; 129 | top: -5px; 130 | left: -5px; 131 | width: calc(100% + 10px); 132 | height: calc(100% + 10px); 133 | border: 2px solid #1c7ed5; 134 | border-radius: 6px; 135 | } 136 | 137 | footer > button#cancel { 138 | margin-right: 8px; 139 | border: 1px solid rgba(255, 255, 255, 0.4); 140 | background-color: transparent; 141 | } 142 | 143 | footer > button#cancel:hover { 144 | background-color: rgba(255, 255, 255, 0.1); 145 | } 146 | 147 | footer > button#submit { 148 | border: 1px solid #1c7ed5; 149 | background-color: #1c7ed5; 150 | } 151 | 152 | footer > button#submit:hover { 153 | background-color: #186bb5; 154 | } 155 | -------------------------------------------------------------------------------- /Sketch Resize.sketchplugin/Contents/Resources/resize.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucaorio/sketch-resize/b2babc188b7e6e362588c971807f75bf48b4dcd0/Sketch Resize.sketchplugin/Contents/Resources/resize.png -------------------------------------------------------------------------------- /Sketch Resize.sketchplugin/Contents/Resources/script.js: -------------------------------------------------------------------------------- 1 | /******/ (function(modules) { // webpackBootstrap 2 | /******/ // The module cache 3 | /******/ var installedModules = {}; 4 | /******/ 5 | /******/ // The require function 6 | /******/ function __webpack_require__(moduleId) { 7 | /******/ 8 | /******/ // Check if module is in cache 9 | /******/ if(installedModules[moduleId]) { 10 | /******/ return installedModules[moduleId].exports; 11 | /******/ } 12 | /******/ // Create a new module (and put it into the cache) 13 | /******/ var module = installedModules[moduleId] = { 14 | /******/ i: moduleId, 15 | /******/ l: false, 16 | /******/ exports: {} 17 | /******/ }; 18 | /******/ 19 | /******/ // Execute the module function 20 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 21 | /******/ 22 | /******/ // Flag the module as loaded 23 | /******/ module.l = true; 24 | /******/ 25 | /******/ // Return the exports of the module 26 | /******/ return module.exports; 27 | /******/ } 28 | /******/ 29 | /******/ 30 | /******/ // expose the modules object (__webpack_modules__) 31 | /******/ __webpack_require__.m = modules; 32 | /******/ 33 | /******/ // expose the module cache 34 | /******/ __webpack_require__.c = installedModules; 35 | /******/ 36 | /******/ // define getter function for harmony exports 37 | /******/ __webpack_require__.d = function(exports, name, getter) { 38 | /******/ if(!__webpack_require__.o(exports, name)) { 39 | /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); 40 | /******/ } 41 | /******/ }; 42 | /******/ 43 | /******/ // define __esModule on exports 44 | /******/ __webpack_require__.r = function(exports) { 45 | /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { 46 | /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); 47 | /******/ } 48 | /******/ Object.defineProperty(exports, '__esModule', { value: true }); 49 | /******/ }; 50 | /******/ 51 | /******/ // create a fake namespace object 52 | /******/ // mode & 1: value is a module id, require it 53 | /******/ // mode & 2: merge all properties of value into the ns 54 | /******/ // mode & 4: return value when already ns object 55 | /******/ // mode & 8|1: behave like require 56 | /******/ __webpack_require__.t = function(value, mode) { 57 | /******/ if(mode & 1) value = __webpack_require__(value); 58 | /******/ if(mode & 8) return value; 59 | /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; 60 | /******/ var ns = Object.create(null); 61 | /******/ __webpack_require__.r(ns); 62 | /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); 63 | /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); 64 | /******/ return ns; 65 | /******/ }; 66 | /******/ 67 | /******/ // getDefaultExport function for compatibility with non-harmony modules 68 | /******/ __webpack_require__.n = function(module) { 69 | /******/ var getter = module && module.__esModule ? 70 | /******/ function getDefault() { return module['default']; } : 71 | /******/ function getModuleExports() { return module; }; 72 | /******/ __webpack_require__.d(getter, 'a', getter); 73 | /******/ return getter; 74 | /******/ }; 75 | /******/ 76 | /******/ // Object.prototype.hasOwnProperty.call 77 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; 78 | /******/ 79 | /******/ // __webpack_public_path__ 80 | /******/ __webpack_require__.p = ""; 81 | /******/ 82 | /******/ 83 | /******/ // Load entry module and return exports 84 | /******/ return __webpack_require__(__webpack_require__.s = "./resources/webview/script.js"); 85 | /******/ }) 86 | /************************************************************************/ 87 | /******/ ({ 88 | 89 | /***/ "./resources/webview/script.js": 90 | /*!*************************************!*\ 91 | !*** ./resources/webview/script.js ***! 92 | \*************************************/ 93 | /*! no static exports found */ 94 | /***/ (function(module, exports) { 95 | 96 | // disable context menu 97 | document.addEventListener('contextmenu', function (event) { 98 | event.preventDefault(); 99 | }); // cancel button 100 | 101 | document.getElementById('cancel').addEventListener('click', function (event) { 102 | event.preventDefault(); 103 | window.postMessage('cancel'); 104 | }); // submit on enter 105 | 106 | document.addEventListener('keypress', function (event) { 107 | if (event.keyCode !== 13) return; 108 | event.preventDefault(); 109 | window.postMessage('submit', getValues()); 110 | }); // submit button 111 | 112 | document.getElementById('submit').addEventListener('click', function (event) { 113 | event.preventDefault(); 114 | window.postMessage('submit', getValues()); 115 | }); // get inputs value 116 | 117 | var getValues = function getValues() { 118 | return { 119 | width: document.getElementById('width').value, 120 | height: document.getElementById('height').value 121 | }; 122 | }; 123 | 124 | /***/ }) 125 | 126 | /******/ }); 127 | //# sourceMappingURL=script.js.map -------------------------------------------------------------------------------- /Sketch Resize.sketchplugin/Contents/Resources/script.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["webpack:///webpack/bootstrap","webpack:///./resources/webview/script.js"],"names":["document","addEventListener","event","preventDefault","getElementById","window","postMessage","keyCode","getValues","width","value","height"],"mappings":";AAAA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;;AAGA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA,kDAA0C,gCAAgC;AAC1E;AACA;;AAEA;AACA;AACA;AACA,gEAAwD,kBAAkB;AAC1E;AACA,yDAAiD,cAAc;AAC/D;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iDAAyC,iCAAiC;AAC1E,wHAAgH,mBAAmB,EAAE;AACrI;AACA;;AAEA;AACA;AACA;AACA,mCAA2B,0BAA0B,EAAE;AACvD,yCAAiC,eAAe;AAChD;AACA;AACA;;AAEA;AACA,8DAAsD,+DAA+D;;AAErH;AACA;;;AAGA;AACA;;;;;;;;;;;;AClFA;AACAA,QAAQ,CAACC,gBAAT,CAA0B,aAA1B,EAAyC,UAAAC,KAAK,EAAI;AAChDA,OAAK,CAACC,cAAN;AACD,CAFD,E,CAIA;;AACAH,QAAQ,CAACI,cAAT,CAAwB,QAAxB,EAAkCH,gBAAlC,CAAmD,OAAnD,EAA4D,UAAAC,KAAK,EAAI;AACnEA,OAAK,CAACC,cAAN;AACAE,QAAM,CAACC,WAAP,CAAmB,QAAnB;AACD,CAHD,E,CAKA;;AACAN,QAAQ,CAACC,gBAAT,CAA0B,UAA1B,EAAsC,UAAAC,KAAK,EAAI;AAC7C,MAAIA,KAAK,CAACK,OAAN,KAAkB,EAAtB,EAA0B;AAC1BL,OAAK,CAACC,cAAN;AACAE,QAAM,CAACC,WAAP,CAAmB,QAAnB,EAA6BE,SAAS,EAAtC;AACD,CAJD,E,CAMA;;AACAR,QAAQ,CAACI,cAAT,CAAwB,QAAxB,EAAkCH,gBAAlC,CAAmD,OAAnD,EAA4D,UAAAC,KAAK,EAAI;AACnEA,OAAK,CAACC,cAAN;AACAE,QAAM,CAACC,WAAP,CAAmB,QAAnB,EAA6BE,SAAS,EAAtC;AACD,CAHD,E,CAKA;;AACA,IAAMA,SAAS,GAAG,SAAZA,SAAY;AAAA,SAAO;AACvBC,SAAK,EAAET,QAAQ,CAACI,cAAT,CAAwB,OAAxB,EAAiCM,KADjB;AAEvBC,UAAM,EAAEX,QAAQ,CAACI,cAAT,CAAwB,QAAxB,EAAkCM;AAFnB,GAAP;AAAA,CAAlB,C","file":"script.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = \"./resources/webview/script.js\");\n","// disable context menu\ndocument.addEventListener('contextmenu', event => {\n event.preventDefault();\n});\n\n// cancel button\ndocument.getElementById('cancel').addEventListener('click', event => {\n event.preventDefault();\n window.postMessage('cancel');\n});\n\n// submit on enter\ndocument.addEventListener('keypress', event => {\n if (event.keyCode !== 13) return;\n event.preventDefault();\n window.postMessage('submit', getValues());\n});\n\n// submit button\ndocument.getElementById('submit').addEventListener('click', event => {\n event.preventDefault();\n window.postMessage('submit', getValues());\n});\n\n// get inputs value\nconst getValues = () => ({\n width: document.getElementById('width').value,\n height: document.getElementById('height').value\n});\n"],"sourceRoot":""} -------------------------------------------------------------------------------- /Sketch Resize.sketchplugin/Contents/Resources/sketch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucaorio/sketch-resize/b2babc188b7e6e362588c971807f75bf48b4dcd0/Sketch Resize.sketchplugin/Contents/Resources/sketch-icon.png -------------------------------------------------------------------------------- /Sketch Resize.sketchplugin/Contents/Sketch/main.js: -------------------------------------------------------------------------------- 1 | var that = this; 2 | function __skpm_run (key, context) { 3 | that.context = context; 4 | 5 | var exports = 6 | /******/ (function(modules) { // webpackBootstrap 7 | /******/ // The module cache 8 | /******/ var installedModules = {}; 9 | /******/ 10 | /******/ // The require function 11 | /******/ function __webpack_require__(moduleId) { 12 | /******/ 13 | /******/ // Check if module is in cache 14 | /******/ if(installedModules[moduleId]) { 15 | /******/ return installedModules[moduleId].exports; 16 | /******/ } 17 | /******/ // Create a new module (and put it into the cache) 18 | /******/ var module = installedModules[moduleId] = { 19 | /******/ i: moduleId, 20 | /******/ l: false, 21 | /******/ exports: {} 22 | /******/ }; 23 | /******/ 24 | /******/ // Execute the module function 25 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 26 | /******/ 27 | /******/ // Flag the module as loaded 28 | /******/ module.l = true; 29 | /******/ 30 | /******/ // Return the exports of the module 31 | /******/ return module.exports; 32 | /******/ } 33 | /******/ 34 | /******/ 35 | /******/ // expose the modules object (__webpack_modules__) 36 | /******/ __webpack_require__.m = modules; 37 | /******/ 38 | /******/ // expose the module cache 39 | /******/ __webpack_require__.c = installedModules; 40 | /******/ 41 | /******/ // define getter function for harmony exports 42 | /******/ __webpack_require__.d = function(exports, name, getter) { 43 | /******/ if(!__webpack_require__.o(exports, name)) { 44 | /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); 45 | /******/ } 46 | /******/ }; 47 | /******/ 48 | /******/ // define __esModule on exports 49 | /******/ __webpack_require__.r = function(exports) { 50 | /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { 51 | /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); 52 | /******/ } 53 | /******/ Object.defineProperty(exports, '__esModule', { value: true }); 54 | /******/ }; 55 | /******/ 56 | /******/ // create a fake namespace object 57 | /******/ // mode & 1: value is a module id, require it 58 | /******/ // mode & 2: merge all properties of value into the ns 59 | /******/ // mode & 4: return value when already ns object 60 | /******/ // mode & 8|1: behave like require 61 | /******/ __webpack_require__.t = function(value, mode) { 62 | /******/ if(mode & 1) value = __webpack_require__(value); 63 | /******/ if(mode & 8) return value; 64 | /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; 65 | /******/ var ns = Object.create(null); 66 | /******/ __webpack_require__.r(ns); 67 | /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); 68 | /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); 69 | /******/ return ns; 70 | /******/ }; 71 | /******/ 72 | /******/ // getDefaultExport function for compatibility with non-harmony modules 73 | /******/ __webpack_require__.n = function(module) { 74 | /******/ var getter = module && module.__esModule ? 75 | /******/ function getDefault() { return module['default']; } : 76 | /******/ function getModuleExports() { return module; }; 77 | /******/ __webpack_require__.d(getter, 'a', getter); 78 | /******/ return getter; 79 | /******/ }; 80 | /******/ 81 | /******/ // Object.prototype.hasOwnProperty.call 82 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; 83 | /******/ 84 | /******/ // __webpack_public_path__ 85 | /******/ __webpack_require__.p = ""; 86 | /******/ 87 | /******/ 88 | /******/ // Load entry module and return exports 89 | /******/ return __webpack_require__(__webpack_require__.s = "./src/main.js"); 90 | /******/ }) 91 | /************************************************************************/ 92 | /******/ ({ 93 | 94 | /***/ "./node_modules/@skpm/timers/immediate.js": 95 | /*!************************************************!*\ 96 | !*** ./node_modules/@skpm/timers/immediate.js ***! 97 | \************************************************/ 98 | /*! no static exports found */ 99 | /***/ (function(module, exports, __webpack_require__) { 100 | 101 | /* globals coscript, sketch */ 102 | var timeout = __webpack_require__(/*! ./timeout */ "./node_modules/@skpm/timers/timeout.js") 103 | 104 | function setImmediate(func, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10) { 105 | return timeout.setTimeout(func, 0, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10) 106 | } 107 | 108 | function clearImmediate(id) { 109 | return timeout.clearTimeout(id) 110 | } 111 | 112 | module.exports = { 113 | setImmediate: setImmediate, 114 | clearImmediate: clearImmediate 115 | } 116 | 117 | 118 | /***/ }), 119 | 120 | /***/ "./node_modules/@skpm/timers/test-if-fiber.js": 121 | /*!****************************************************!*\ 122 | !*** ./node_modules/@skpm/timers/test-if-fiber.js ***! 123 | \****************************************************/ 124 | /*! no static exports found */ 125 | /***/ (function(module, exports) { 126 | 127 | module.exports = function () { 128 | return typeof coscript !== 'undefined' && coscript.createFiber 129 | } 130 | 131 | 132 | /***/ }), 133 | 134 | /***/ "./node_modules/@skpm/timers/timeout.js": 135 | /*!**********************************************!*\ 136 | !*** ./node_modules/@skpm/timers/timeout.js ***! 137 | \**********************************************/ 138 | /*! no static exports found */ 139 | /***/ (function(module, exports, __webpack_require__) { 140 | 141 | /* globals coscript, sketch */ 142 | var fiberAvailable = __webpack_require__(/*! ./test-if-fiber */ "./node_modules/@skpm/timers/test-if-fiber.js") 143 | 144 | var setTimeout 145 | var clearTimeout 146 | 147 | var fibers = [] 148 | 149 | if (fiberAvailable()) { 150 | var fibers = [] 151 | 152 | setTimeout = function (func, delay, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10) { 153 | // fibers takes care of keeping coscript around 154 | var id = fibers.length 155 | fibers.push(coscript.scheduleWithInterval_jsFunction( 156 | (delay || 0) / 1000, 157 | function () { 158 | func(param1, param2, param3, param4, param5, param6, param7, param8, param9, param10) 159 | } 160 | )) 161 | return id 162 | } 163 | 164 | clearTimeout = function (id) { 165 | var timeout = fibers[id] 166 | if (timeout) { 167 | timeout.cancel() // fibers takes care of keeping coscript around 168 | fibers[id] = undefined // garbage collect the fiber 169 | } 170 | } 171 | } else { 172 | setTimeout = function (func, delay, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10) { 173 | coscript.shouldKeepAround = true 174 | var id = fibers.length 175 | fibers.push(true) 176 | coscript.scheduleWithInterval_jsFunction( 177 | (delay || 0) / 1000, 178 | function () { 179 | if (fibers[id]) { // if not cleared 180 | func(param1, param2, param3, param4, param5, param6, param7, param8, param9, param10) 181 | } 182 | clearTimeout(id) 183 | if (fibers.every(function (_id) { return !_id })) { // if everything is cleared 184 | coscript.shouldKeepAround = false 185 | } 186 | } 187 | ) 188 | return id 189 | } 190 | 191 | clearTimeout = function (id) { 192 | fibers[id] = false 193 | } 194 | } 195 | 196 | module.exports = { 197 | setTimeout: setTimeout, 198 | clearTimeout: clearTimeout 199 | } 200 | 201 | 202 | /***/ }), 203 | 204 | /***/ "./node_modules/cocoascript-class/lib/index.js": 205 | /*!*****************************************************!*\ 206 | !*** ./node_modules/cocoascript-class/lib/index.js ***! 207 | \*****************************************************/ 208 | /*! no static exports found */ 209 | /***/ (function(module, exports, __webpack_require__) { 210 | 211 | "use strict"; 212 | 213 | 214 | Object.defineProperty(exports, "__esModule", { 215 | value: true 216 | }); 217 | exports.SuperCall = undefined; 218 | exports.default = ObjCClass; 219 | 220 | var _runtime = __webpack_require__(/*! ./runtime.js */ "./node_modules/cocoascript-class/lib/runtime.js"); 221 | 222 | exports.SuperCall = _runtime.SuperCall; 223 | 224 | // super when returnType is id and args are void 225 | // id objc_msgSendSuper(struct objc_super *super, SEL op, void) 226 | 227 | const SuperInit = (0, _runtime.SuperCall)(NSStringFromSelector("init"), [], { type: "@" }); 228 | 229 | // Returns a real ObjC class. No need to use new. 230 | function ObjCClass(defn) { 231 | const superclass = defn.superclass || NSObject; 232 | const className = (defn.className || defn.classname || "ObjCClass") + NSUUID.UUID().UUIDString(); 233 | const reserved = new Set(['className', 'classname', 'superclass']); 234 | var cls = MOClassDescription.allocateDescriptionForClassWithName_superclass_(className, superclass); 235 | // Add each handler to the class description 236 | const ivars = []; 237 | for (var key in defn) { 238 | const v = defn[key]; 239 | if (typeof v == 'function' && key !== 'init') { 240 | var selector = NSSelectorFromString(key); 241 | cls.addInstanceMethodWithSelector_function_(selector, v); 242 | } else if (!reserved.has(key)) { 243 | ivars.push(key); 244 | cls.addInstanceVariableWithName_typeEncoding(key, "@"); 245 | } 246 | } 247 | 248 | cls.addInstanceMethodWithSelector_function_(NSSelectorFromString('init'), function () { 249 | const self = SuperInit.call(this); 250 | ivars.map(name => { 251 | Object.defineProperty(self, name, { 252 | get() { 253 | return getIvar(self, name); 254 | }, 255 | set(v) { 256 | (0, _runtime.object_setInstanceVariable)(self, name, v); 257 | } 258 | }); 259 | self[name] = defn[name]; 260 | }); 261 | // If there is a passsed-in init funciton, call it now. 262 | if (typeof defn.init == 'function') defn.init.call(this); 263 | return self; 264 | }); 265 | 266 | return cls.registerClass(); 267 | }; 268 | 269 | function getIvar(obj, name) { 270 | const retPtr = MOPointer.new(); 271 | (0, _runtime.object_getInstanceVariable)(obj, name, retPtr); 272 | return retPtr.value().retain().autorelease(); 273 | } 274 | 275 | /***/ }), 276 | 277 | /***/ "./node_modules/cocoascript-class/lib/runtime.js": 278 | /*!*******************************************************!*\ 279 | !*** ./node_modules/cocoascript-class/lib/runtime.js ***! 280 | \*******************************************************/ 281 | /*! no static exports found */ 282 | /***/ (function(module, exports, __webpack_require__) { 283 | 284 | "use strict"; 285 | 286 | 287 | Object.defineProperty(exports, "__esModule", { 288 | value: true 289 | }); 290 | exports.SuperCall = SuperCall; 291 | exports.CFunc = CFunc; 292 | const objc_super_typeEncoding = '{objc_super="receiver"@"super_class"#}'; 293 | 294 | // You can store this to call your function. this must be bound to the current instance. 295 | function SuperCall(selector, argTypes, returnType) { 296 | const func = CFunc("objc_msgSendSuper", [{ type: '^' + objc_super_typeEncoding }, { type: ":" }, ...argTypes], returnType); 297 | return function (...args) { 298 | const struct = make_objc_super(this, this.superclass()); 299 | const structPtr = MOPointer.alloc().initWithValue_(struct); 300 | return func(structPtr, selector, ...args); 301 | }; 302 | } 303 | 304 | // Recursively create a MOStruct 305 | function makeStruct(def) { 306 | if (typeof def !== 'object' || Object.keys(def).length == 0) { 307 | return def; 308 | } 309 | const name = Object.keys(def)[0]; 310 | const values = def[name]; 311 | 312 | const structure = MOStruct.structureWithName_memberNames_runtime(name, Object.keys(values), Mocha.sharedRuntime()); 313 | 314 | Object.keys(values).map(member => { 315 | structure[member] = makeStruct(values[member]); 316 | }); 317 | 318 | return structure; 319 | } 320 | 321 | function make_objc_super(self, cls) { 322 | return makeStruct({ 323 | objc_super: { 324 | receiver: self, 325 | super_class: cls 326 | } 327 | }); 328 | } 329 | 330 | // Due to particularities of the JS bridge, we can't call into MOBridgeSupport objects directly 331 | // But, we can ask key value coding to do the dirty work for us ;) 332 | function setKeys(o, d) { 333 | const funcDict = NSMutableDictionary.dictionary(); 334 | funcDict.o = o; 335 | Object.keys(d).map(k => funcDict.setValue_forKeyPath(d[k], "o." + k)); 336 | } 337 | 338 | // Use any C function, not just ones with BridgeSupport 339 | function CFunc(name, args, retVal) { 340 | function makeArgument(a) { 341 | if (!a) return null; 342 | const arg = MOBridgeSupportArgument.alloc().init(); 343 | setKeys(arg, { 344 | type64: a.type 345 | }); 346 | return arg; 347 | } 348 | const func = MOBridgeSupportFunction.alloc().init(); 349 | setKeys(func, { 350 | name: name, 351 | arguments: args.map(makeArgument), 352 | returnValue: makeArgument(retVal) 353 | }); 354 | return func; 355 | } 356 | 357 | /* 358 | @encode(char*) = "*" 359 | @encode(id) = "@" 360 | @encode(Class) = "#" 361 | @encode(void*) = "^v" 362 | @encode(CGRect) = "{CGRect={CGPoint=dd}{CGSize=dd}}" 363 | @encode(SEL) = ":" 364 | */ 365 | 366 | function addStructToBridgeSupport(key, structDef) { 367 | // OK, so this is probably the nastiest hack in this file. 368 | // We go modify MOBridgeSupportController behind its back and use kvc to add our own definition 369 | // There isn't another API for this though. So the only other way would be to make a real bridgesupport file. 370 | const symbols = MOBridgeSupportController.sharedController().valueForKey('symbols'); 371 | if (!symbols) throw Error("Something has changed within bridge support so we can't add our definitions"); 372 | // If someone already added this definition, don't re-register it. 373 | if (symbols[key] !== null) return; 374 | const def = MOBridgeSupportStruct.alloc().init(); 375 | setKeys(def, { 376 | name: key, 377 | type: structDef.type 378 | }); 379 | symbols[key] = def; 380 | }; 381 | 382 | // This assumes the ivar is an object type. Return value is pretty useless. 383 | const object_getInstanceVariable = exports.object_getInstanceVariable = CFunc("object_getInstanceVariable", [{ type: "@" }, { type: '*' }, { type: "^@" }], { type: "^{objc_ivar=}" }); 384 | // Again, ivar is of object type 385 | const object_setInstanceVariable = exports.object_setInstanceVariable = CFunc("object_setInstanceVariable", [{ type: "@" }, { type: '*' }, { type: "@" }], { type: "^{objc_ivar=}" }); 386 | 387 | // We need Mocha to understand what an objc_super is so we can use it as a function argument 388 | addStructToBridgeSupport('objc_super', { type: objc_super_typeEncoding }); 389 | 390 | /***/ }), 391 | 392 | /***/ "./node_modules/promise-polyfill/lib/index.js": 393 | /*!****************************************************!*\ 394 | !*** ./node_modules/promise-polyfill/lib/index.js ***! 395 | \****************************************************/ 396 | /*! no static exports found */ 397 | /***/ (function(module, exports, __webpack_require__) { 398 | 399 | "use strict"; 400 | /* WEBPACK VAR INJECTION */(function(setTimeout, setImmediate) { 401 | 402 | /** 403 | * @this {Promise} 404 | */ 405 | function finallyConstructor(callback) { 406 | var constructor = this.constructor; 407 | return this.then( 408 | function(value) { 409 | return constructor.resolve(callback()).then(function() { 410 | return value; 411 | }); 412 | }, 413 | function(reason) { 414 | return constructor.resolve(callback()).then(function() { 415 | return constructor.reject(reason); 416 | }); 417 | } 418 | ); 419 | } 420 | 421 | // Store setTimeout reference so promise-polyfill will be unaffected by 422 | // other code modifying setTimeout (like sinon.useFakeTimers()) 423 | var setTimeoutFunc = setTimeout; 424 | 425 | function noop() {} 426 | 427 | // Polyfill for Function.prototype.bind 428 | function bind(fn, thisArg) { 429 | return function() { 430 | fn.apply(thisArg, arguments); 431 | }; 432 | } 433 | 434 | /** 435 | * @constructor 436 | * @param {Function} fn 437 | */ 438 | function Promise(fn) { 439 | if (!(this instanceof Promise)) 440 | throw new TypeError('Promises must be constructed via new'); 441 | if (typeof fn !== 'function') throw new TypeError('not a function'); 442 | /** @type {!number} */ 443 | this._state = 0; 444 | /** @type {!boolean} */ 445 | this._handled = false; 446 | /** @type {Promise|undefined} */ 447 | this._value = undefined; 448 | /** @type {!Array} */ 449 | this._deferreds = []; 450 | 451 | doResolve(fn, this); 452 | } 453 | 454 | function handle(self, deferred) { 455 | while (self._state === 3) { 456 | self = self._value; 457 | } 458 | if (self._state === 0) { 459 | self._deferreds.push(deferred); 460 | return; 461 | } 462 | self._handled = true; 463 | Promise._immediateFn(function() { 464 | var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected; 465 | if (cb === null) { 466 | (self._state === 1 ? resolve : reject)(deferred.promise, self._value); 467 | return; 468 | } 469 | var ret; 470 | try { 471 | ret = cb(self._value); 472 | } catch (e) { 473 | reject(deferred.promise, e); 474 | return; 475 | } 476 | resolve(deferred.promise, ret); 477 | }); 478 | } 479 | 480 | function resolve(self, newValue) { 481 | try { 482 | // Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure 483 | if (newValue === self) 484 | throw new TypeError('A promise cannot be resolved with itself.'); 485 | if ( 486 | newValue && 487 | (typeof newValue === 'object' || typeof newValue === 'function') 488 | ) { 489 | var then = newValue.then; 490 | if (newValue instanceof Promise) { 491 | self._state = 3; 492 | self._value = newValue; 493 | finale(self); 494 | return; 495 | } else if (typeof then === 'function') { 496 | doResolve(bind(then, newValue), self); 497 | return; 498 | } 499 | } 500 | self._state = 1; 501 | self._value = newValue; 502 | finale(self); 503 | } catch (e) { 504 | reject(self, e); 505 | } 506 | } 507 | 508 | function reject(self, newValue) { 509 | self._state = 2; 510 | self._value = newValue; 511 | finale(self); 512 | } 513 | 514 | function finale(self) { 515 | if (self._state === 2 && self._deferreds.length === 0) { 516 | Promise._immediateFn(function() { 517 | if (!self._handled) { 518 | Promise._unhandledRejectionFn(self._value); 519 | } 520 | }); 521 | } 522 | 523 | for (var i = 0, len = self._deferreds.length; i < len; i++) { 524 | handle(self, self._deferreds[i]); 525 | } 526 | self._deferreds = null; 527 | } 528 | 529 | /** 530 | * @constructor 531 | */ 532 | function Handler(onFulfilled, onRejected, promise) { 533 | this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null; 534 | this.onRejected = typeof onRejected === 'function' ? onRejected : null; 535 | this.promise = promise; 536 | } 537 | 538 | /** 539 | * Take a potentially misbehaving resolver function and make sure 540 | * onFulfilled and onRejected are only called once. 541 | * 542 | * Makes no guarantees about asynchrony. 543 | */ 544 | function doResolve(fn, self) { 545 | var done = false; 546 | try { 547 | fn( 548 | function(value) { 549 | if (done) return; 550 | done = true; 551 | resolve(self, value); 552 | }, 553 | function(reason) { 554 | if (done) return; 555 | done = true; 556 | reject(self, reason); 557 | } 558 | ); 559 | } catch (ex) { 560 | if (done) return; 561 | done = true; 562 | reject(self, ex); 563 | } 564 | } 565 | 566 | Promise.prototype['catch'] = function(onRejected) { 567 | return this.then(null, onRejected); 568 | }; 569 | 570 | Promise.prototype.then = function(onFulfilled, onRejected) { 571 | // @ts-ignore 572 | var prom = new this.constructor(noop); 573 | 574 | handle(this, new Handler(onFulfilled, onRejected, prom)); 575 | return prom; 576 | }; 577 | 578 | Promise.prototype['finally'] = finallyConstructor; 579 | 580 | Promise.all = function(arr) { 581 | return new Promise(function(resolve, reject) { 582 | if (!arr || typeof arr.length === 'undefined') 583 | throw new TypeError('Promise.all accepts an array'); 584 | var args = Array.prototype.slice.call(arr); 585 | if (args.length === 0) return resolve([]); 586 | var remaining = args.length; 587 | 588 | function res(i, val) { 589 | try { 590 | if (val && (typeof val === 'object' || typeof val === 'function')) { 591 | var then = val.then; 592 | if (typeof then === 'function') { 593 | then.call( 594 | val, 595 | function(val) { 596 | res(i, val); 597 | }, 598 | reject 599 | ); 600 | return; 601 | } 602 | } 603 | args[i] = val; 604 | if (--remaining === 0) { 605 | resolve(args); 606 | } 607 | } catch (ex) { 608 | reject(ex); 609 | } 610 | } 611 | 612 | for (var i = 0; i < args.length; i++) { 613 | res(i, args[i]); 614 | } 615 | }); 616 | }; 617 | 618 | Promise.resolve = function(value) { 619 | if (value && typeof value === 'object' && value.constructor === Promise) { 620 | return value; 621 | } 622 | 623 | return new Promise(function(resolve) { 624 | resolve(value); 625 | }); 626 | }; 627 | 628 | Promise.reject = function(value) { 629 | return new Promise(function(resolve, reject) { 630 | reject(value); 631 | }); 632 | }; 633 | 634 | Promise.race = function(values) { 635 | return new Promise(function(resolve, reject) { 636 | for (var i = 0, len = values.length; i < len; i++) { 637 | values[i].then(resolve, reject); 638 | } 639 | }); 640 | }; 641 | 642 | // Use polyfill for setImmediate for performance gains 643 | Promise._immediateFn = 644 | (typeof setImmediate === 'function' && 645 | function(fn) { 646 | setImmediate(fn); 647 | }) || 648 | function(fn) { 649 | setTimeoutFunc(fn, 0); 650 | }; 651 | 652 | Promise._unhandledRejectionFn = function _unhandledRejectionFn(err) { 653 | if (typeof console !== 'undefined' && console) { 654 | console.warn('Possible Unhandled Promise Rejection:', err); // eslint-disable-line no-console 655 | } 656 | }; 657 | 658 | module.exports = Promise; 659 | 660 | /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./node_modules/@skpm/timers/timeout.js */ "./node_modules/@skpm/timers/timeout.js")["setTimeout"], __webpack_require__(/*! ./node_modules/@skpm/timers/immediate.js */ "./node_modules/@skpm/timers/immediate.js")["setImmediate"])) 661 | 662 | /***/ }), 663 | 664 | /***/ "./node_modules/sketch-module-web-view/lib/browser-api.js": 665 | /*!****************************************************************!*\ 666 | !*** ./node_modules/sketch-module-web-view/lib/browser-api.js ***! 667 | \****************************************************************/ 668 | /*! no static exports found */ 669 | /***/ (function(module, exports) { 670 | 671 | function parseHexColor(color) { 672 | // Check the string for incorrect formatting. 673 | if (!color || color[0] !== '#') { 674 | if ( 675 | color && 676 | typeof color.isKindOfClass === 'function' && 677 | color.isKindOfClass(NSColor) 678 | ) { 679 | return color 680 | } 681 | throw new Error( 682 | 'Incorrect color formating. It should be an hex color: #RRGGBBAA' 683 | ) 684 | } 685 | 686 | // append FF if alpha channel is not specified. 687 | var source = color.substr(1) 688 | if (source.length === 3) { 689 | source += 'F' 690 | } else if (source.length === 6) { 691 | source += 'FF' 692 | } 693 | // Convert the string from #FFF format to #FFFFFF format. 694 | var hex 695 | if (source.length === 4) { 696 | for (var i = 0; i < 4; i += 1) { 697 | hex += source[i] 698 | hex += source[i] 699 | } 700 | } else if (source.length === 8) { 701 | hex = source 702 | } else { 703 | return NSColor.whiteColor() 704 | } 705 | 706 | var r = parseInt(hex.slice(0, 2), 16) 707 | var g = parseInt(hex.slice(2, 4), 16) 708 | var b = parseInt(hex.slice(4, 6), 16) 709 | var a = parseInt(hex.slice(6, 8), 16) 710 | 711 | return NSColor.colorWithSRGBRed_green_blue_alpha(r, g, b, a) 712 | } 713 | 714 | module.exports = function(browserWindow, panel, webview) { 715 | // keep reference to the subviews 716 | browserWindow._panel = panel 717 | browserWindow._webview = webview 718 | browserWindow._destroyed = false 719 | 720 | browserWindow.destroy = function() { 721 | return panel.close() 722 | } 723 | 724 | browserWindow.close = function() { 725 | if (panel.delegate().utils.parentWindow) { 726 | var shouldClose = true 727 | browserWindow.emit('close', { 728 | get defaultPrevented() { 729 | return !shouldClose 730 | }, 731 | preventDefault: function() { 732 | shouldClose = false 733 | }, 734 | }) 735 | if (shouldClose) { 736 | panel.delegate().utils.parentWindow.endSheet(panel) 737 | } 738 | return 739 | } 740 | 741 | if (!browserWindow.isClosable()) { 742 | return 743 | } 744 | 745 | panel.performClose(null) 746 | } 747 | 748 | function focus(focused) { 749 | if (browserWindow.isVisible()) { 750 | return 751 | } 752 | if (focused) { 753 | NSApplication.sharedApplication().activateIgnoringOtherApps(true) 754 | panel.makeKeyAndOrderFront(null) 755 | } else { 756 | panel.orderBack(null) 757 | } 758 | } 759 | 760 | browserWindow.focus = focus.bind(this, true) 761 | browserWindow.blur = focus.bind(this, false) 762 | 763 | browserWindow.isFocused = function() { 764 | return panel.isKeyWindow() 765 | } 766 | 767 | browserWindow.isDestroyed = function() { 768 | return browserWindow._destroyed 769 | } 770 | 771 | browserWindow.show = function() { 772 | // This method is supposed to put focus on window, however if the app does not 773 | // have focus then "makeKeyAndOrderFront" will only show the window. 774 | NSApp.activateIgnoringOtherApps(true) 775 | 776 | if (panel.delegate().utils.parentWindow) { 777 | return panel.delegate().utils.parentWindow.beginSheet_completionHandler( 778 | panel, 779 | __mocha__.createBlock_function('v16@?0q8', function() { 780 | browserWindow.emit('closed') 781 | }) 782 | ) 783 | } 784 | 785 | return panel.makeKeyAndOrderFront(null) 786 | } 787 | 788 | browserWindow.showInactive = function() { 789 | return panel.orderFrontRegardless() 790 | } 791 | 792 | browserWindow.hide = function() { 793 | return panel.orderOut(null) 794 | } 795 | 796 | browserWindow.isVisible = function() { 797 | return panel.isVisible() 798 | } 799 | 800 | browserWindow.isModal = function() { 801 | return false 802 | } 803 | 804 | browserWindow.maximize = function() { 805 | if (!browserWindow.isMaximized()) { 806 | panel.zoom(null) 807 | } 808 | } 809 | browserWindow.unmaximize = function() { 810 | if (browserWindow.isMaximized()) { 811 | panel.zoom(null) 812 | } 813 | } 814 | 815 | browserWindow.isMaximized = function() { 816 | if ((panel.styleMask() & NSResizableWindowMask) !== 0) { 817 | return panel.isZoomed() 818 | } 819 | var rectScreen = NSScreen.mainScreen().visibleFrame() 820 | var rectWindow = panel.frame() 821 | return ( 822 | rectScreen.origin.x == rectWindow.origin.x && 823 | rectScreen.origin.y == rectWindow.origin.y && 824 | rectScreen.size.width == rectWindow.size.width && 825 | rectScreen.size.height == rectWindow.size.height 826 | ) 827 | } 828 | 829 | browserWindow.minimize = function() { 830 | return panel.miniaturize(null) 831 | } 832 | 833 | browserWindow.restore = function() { 834 | return panel.deminiaturize(null) 835 | } 836 | 837 | browserWindow.isMinimized = function() { 838 | return panel.isMiniaturized() 839 | } 840 | 841 | browserWindow.setFullScreen = function(fullscreen) { 842 | if (fullscreen !== browserWindow.isFullscreen()) { 843 | panel.toggleFullScreen(null) 844 | } 845 | } 846 | 847 | browserWindow.isFullscreen = function() { 848 | return panel.styleMask() & NSFullScreenWindowMask 849 | } 850 | 851 | browserWindow.setAspectRatio = function(aspectRatio /* , extraSize */) { 852 | // Reset the behaviour to default if aspect_ratio is set to 0 or less. 853 | if (aspectRatio > 0.0) { 854 | panel.setAspectRatio(NSMakeSize(aspectRatio, 1.0)) 855 | } else { 856 | panel.setResizeIncrements(NSMakeSize(1.0, 1.0)) 857 | } 858 | } 859 | 860 | browserWindow.setBounds = function(bounds, animate) { 861 | if (!bounds) { 862 | return 863 | } 864 | 865 | // Do nothing if in fullscreen mode. 866 | if (browserWindow.isFullscreen()) { 867 | return 868 | } 869 | 870 | const newBounds = Object.assign(browserWindow.getBounds(), bounds) 871 | 872 | // TODO: Check size constraints since setFrame does not check it. 873 | // var size = bounds.size 874 | // size.SetToMax(GetMinimumSize()); 875 | // gfx::Size max_size = GetMaximumSize(); 876 | // if (!max_size.IsEmpty()) 877 | // size.SetToMin(max_size); 878 | 879 | var cocoaBounds = NSMakeRect( 880 | newBounds.x, 881 | 0, 882 | newBounds.width, 883 | newBounds.height 884 | ) 885 | // Flip Y coordinates based on the primary screen 886 | var screen = NSScreen.screens().firstObject() 887 | cocoaBounds.origin.y = NSHeight(screen.frame()) - newBounds.y 888 | 889 | panel.setFrame_display_animate(cocoaBounds, true, animate) 890 | } 891 | 892 | browserWindow.getBounds = function() { 893 | const cocoaBounds = panel.frame() 894 | var mainScreenRect = NSScreen.screens() 895 | .firstObject() 896 | .frame() 897 | return { 898 | x: cocoaBounds.origin.x, 899 | y: Math.round(NSHeight(mainScreenRect) - cocoaBounds.origin.y), 900 | width: cocoaBounds.size.width, 901 | height: cocoaBounds.size.height, 902 | } 903 | } 904 | 905 | browserWindow.setContentBounds = function(bounds, animate) { 906 | // TODO: 907 | browserWindow.setBounds(bounds, animate) 908 | } 909 | 910 | browserWindow.getContentBounds = function() { 911 | // TODO: 912 | return browserWindow.getBounds() 913 | } 914 | 915 | browserWindow.setSize = function(width, height, animate) { 916 | // TODO: handle resizing around center 917 | return browserWindow.setBounds({ width: width, height: height }, animate) 918 | } 919 | 920 | browserWindow.getSize = function() { 921 | var bounds = browserWindow.getBounds() 922 | return [bounds.width, bounds.height] 923 | } 924 | 925 | browserWindow.setContentSize = function(width, height, animate) { 926 | // TODO: handle resizing around center 927 | return browserWindow.setContentBounds( 928 | { width: width, height: height }, 929 | animate 930 | ) 931 | } 932 | 933 | browserWindow.getContentSize = function() { 934 | var bounds = browserWindow.getContentBounds() 935 | return [bounds.width, bounds.height] 936 | } 937 | 938 | browserWindow.setMinimumSize = function(width, height) { 939 | const minSize = CGSizeMake(width, height) 940 | panel.setContentMinSize(minSize) 941 | } 942 | 943 | browserWindow.getMinimumSize = function() { 944 | const size = panel.contentMinSize() 945 | return [size.width, size.height] 946 | } 947 | 948 | browserWindow.setMaximumSize = function(width, height) { 949 | const maxSize = CGSizeMake(width, height) 950 | panel.setContentMaxSize(maxSize) 951 | } 952 | 953 | browserWindow.getMaximumSize = function() { 954 | const size = panel.contentMaxSize() 955 | return [size.width, size.height] 956 | } 957 | 958 | browserWindow.setResizable = function(resizable) { 959 | return browserWindow._setStyleMask(resizable, NSResizableWindowMask) 960 | } 961 | 962 | browserWindow.isResizable = function() { 963 | return panel.styleMask() & NSResizableWindowMask 964 | } 965 | 966 | browserWindow.setMovable = function(movable) { 967 | return panel.setMovable(movable) 968 | } 969 | browserWindow.isMovable = function() { 970 | return panel.isMovable() 971 | } 972 | 973 | browserWindow.setMinimizable = function(minimizable) { 974 | return browserWindow._setStyleMask(minimizable, NSMiniaturizableWindowMask) 975 | } 976 | 977 | browserWindow.isMinimizable = function() { 978 | return panel.styleMask() & NSMiniaturizableWindowMask 979 | } 980 | 981 | browserWindow.setMaximizable = function(maximizable) { 982 | if (panel.standardWindowButton(NSWindowZoomButton)) { 983 | panel.standardWindowButton(NSWindowZoomButton).setEnabled(maximizable) 984 | } 985 | } 986 | 987 | browserWindow.isMaximizable = function() { 988 | return ( 989 | panel.standardWindowButton(NSWindowZoomButton) && 990 | panel.standardWindowButton(NSWindowZoomButton).isEnabled() 991 | ) 992 | } 993 | 994 | browserWindow.setFullScreenable = function(fullscreenable) { 995 | browserWindow._setCollectionBehavior( 996 | fullscreenable, 997 | NSWindowCollectionBehaviorFullScreenPrimary 998 | ) 999 | // On EL Capitan this flag is required to hide fullscreen button. 1000 | browserWindow._setCollectionBehavior( 1001 | !fullscreenable, 1002 | NSWindowCollectionBehaviorFullScreenAuxiliary 1003 | ) 1004 | } 1005 | 1006 | browserWindow.isFullScreenable = function() { 1007 | var collectionBehavior = panel.collectionBehavior() 1008 | return collectionBehavior & NSWindowCollectionBehaviorFullScreenPrimary 1009 | } 1010 | 1011 | browserWindow.setClosable = function(closable) { 1012 | browserWindow._setStyleMask(closable, NSClosableWindowMask) 1013 | } 1014 | 1015 | browserWindow.isClosable = function() { 1016 | return panel.styleMask() & NSClosableWindowMask 1017 | } 1018 | 1019 | browserWindow.setAlwaysOnTop = function(top, level, relativeLevel) { 1020 | var windowLevel = NSNormalWindowLevel 1021 | var maxWindowLevel = CGWindowLevelForKey(kCGMaximumWindowLevelKey) 1022 | var minWindowLevel = CGWindowLevelForKey(kCGMinimumWindowLevelKey) 1023 | 1024 | if (top) { 1025 | if (level === 'normal') { 1026 | windowLevel = NSNormalWindowLevel 1027 | } else if (level === 'torn-off-menu') { 1028 | windowLevel = NSTornOffMenuWindowLevel 1029 | } else if (level === 'modal-panel') { 1030 | windowLevel = NSModalPanelWindowLevel 1031 | } else if (level === 'main-menu') { 1032 | windowLevel = NSMainMenuWindowLevel 1033 | } else if (level === 'status') { 1034 | windowLevel = NSStatusWindowLevel 1035 | } else if (level === 'pop-up-menu') { 1036 | windowLevel = NSPopUpMenuWindowLevel 1037 | } else if (level === 'screen-saver') { 1038 | windowLevel = NSScreenSaverWindowLevel 1039 | } else if (level === 'dock') { 1040 | // Deprecated by macOS, but kept for backwards compatibility 1041 | windowLevel = NSDockWindowLevel 1042 | } else { 1043 | windowLevel = NSFloatingWindowLevel 1044 | } 1045 | } 1046 | 1047 | var newLevel = windowLevel + (relativeLevel || 0) 1048 | if (newLevel >= minWindowLevel && newLevel <= maxWindowLevel) { 1049 | panel.setLevel(newLevel) 1050 | } else { 1051 | throw new Error( 1052 | 'relativeLevel must be between ' + 1053 | minWindowLevel + 1054 | ' and ' + 1055 | maxWindowLevel 1056 | ) 1057 | } 1058 | } 1059 | 1060 | browserWindow.isAlwaysOnTop = function() { 1061 | return panel.level() !== NSNormalWindowLevel 1062 | } 1063 | 1064 | browserWindow.moveTop = function() { 1065 | return panel.orderFrontRegardless() 1066 | } 1067 | 1068 | browserWindow.center = function() { 1069 | panel.center() 1070 | } 1071 | 1072 | browserWindow.setPosition = function(x, y, animate) { 1073 | return browserWindow.setBounds({ x: x, y: y }, animate) 1074 | } 1075 | 1076 | browserWindow.getPosition = function() { 1077 | var bounds = browserWindow.getBounds() 1078 | return [bounds.x, bounds.y] 1079 | } 1080 | 1081 | browserWindow.setTitle = function(title) { 1082 | panel.setTitle(title) 1083 | } 1084 | 1085 | browserWindow.getTitle = function() { 1086 | return String(panel.title()) 1087 | } 1088 | 1089 | var attentionRequestId = 0 1090 | browserWindow.flashFrame = function(flash) { 1091 | if (flash) { 1092 | attentionRequestId = NSApp.requestUserAttention(NSInformationalRequest) 1093 | } else { 1094 | NSApp.cancelUserAttentionRequest(attentionRequestId) 1095 | attentionRequestId = 0 1096 | } 1097 | } 1098 | 1099 | browserWindow.getNativeWindowHandle = function() { 1100 | return panel 1101 | } 1102 | 1103 | browserWindow.getNativeWebViewHandle = function() { 1104 | return webview 1105 | } 1106 | 1107 | browserWindow.loadURL = function(url) { 1108 | // When frameLocation is a file, prefix it with the Sketch Resources path 1109 | if (/^(?!https?|file).*\.html?$/.test(url)) { 1110 | if (typeof __command !== 'undefined' && __command.pluginBundle()) { 1111 | url = 1112 | 'file://' + 1113 | __command 1114 | .pluginBundle() 1115 | .urlForResourceNamed(url) 1116 | .path() 1117 | } 1118 | } 1119 | 1120 | if (/^file:\/\/.*\.html?$/.test(url)) { 1121 | webview.loadFileURL_allowingReadAccessToURL( 1122 | NSURL.fileURLWithPath(url), 1123 | NSURL.fileURLWithPath('file:///') 1124 | ) 1125 | return 1126 | } 1127 | 1128 | const properURL = NSURL.URLWithString(url) 1129 | const urlRequest = NSURLRequest.requestWithURL(properURL) 1130 | 1131 | webview.loadRequest(urlRequest) 1132 | } 1133 | 1134 | browserWindow.reload = function() { 1135 | webview.reload() 1136 | } 1137 | 1138 | browserWindow.setHasShadow = function(hasShadow) { 1139 | return panel.setHasShadow(hasShadow) 1140 | } 1141 | 1142 | browserWindow.hasShadow = function() { 1143 | return panel.hasShadow() 1144 | } 1145 | 1146 | browserWindow.setOpacity = function(opacity) { 1147 | return panel.setAlphaValue(opacity) 1148 | } 1149 | 1150 | browserWindow.getOpacity = function() { 1151 | return panel.alphaValue() 1152 | } 1153 | 1154 | browserWindow.setVisibleOnAllWorkspaces = function(visible) { 1155 | return browserWindow._setCollectionBehavior( 1156 | visible, 1157 | NSWindowCollectionBehaviorCanJoinAllSpaces 1158 | ) 1159 | } 1160 | 1161 | browserWindow.isVisibleOnAllWorkspaces = function() { 1162 | var collectionBehavior = panel.collectionBehavior() 1163 | return collectionBehavior & NSWindowCollectionBehaviorCanJoinAllSpaces 1164 | } 1165 | 1166 | browserWindow.setIgnoreMouseEvents = function(ignore) { 1167 | return panel.setIgnoresMouseEvents(ignore) 1168 | } 1169 | 1170 | browserWindow.setContentProtection = function(enable) { 1171 | panel.setSharingType(enable ? NSWindowSharingNone : NSWindowSharingReadOnly) 1172 | } 1173 | 1174 | browserWindow.setAutoHideCursor = function(autoHide) { 1175 | panel.setDisableAutoHideCursor(autoHide) 1176 | } 1177 | 1178 | browserWindow.setVibrancy = function(type) { 1179 | var effectView = browserWindow._vibrantView 1180 | 1181 | if (!type) { 1182 | if (effectView == null) { 1183 | return 1184 | } 1185 | 1186 | effectView.removeFromSuperview() 1187 | panel.setVibrantView(null) 1188 | return 1189 | } 1190 | 1191 | if (effectView == null) { 1192 | var contentView = panel.contentView() 1193 | effectView = NSVisualEffectView.alloc().initWithFrame( 1194 | contentView.bounds() 1195 | ) 1196 | browserWindow._vibrantView = effectView 1197 | 1198 | effectView.setAutoresizingMask(NSViewWidthSizable | NSViewHeightSizable) 1199 | effectView.setBlendingMode(NSVisualEffectBlendingModeBehindWindow) 1200 | effectView.setState(NSVisualEffectStateActive) 1201 | effectView.setFrame(contentView.bounds()) 1202 | contentView.addSubview_positioned_relativeTo( 1203 | effectView, 1204 | NSWindowBelow, 1205 | null 1206 | ) 1207 | } 1208 | 1209 | var vibrancyType = NSVisualEffectMaterialLight 1210 | 1211 | if (type === 'appearance-based') { 1212 | vibrancyType = NSVisualEffectMaterialAppearanceBased 1213 | } else if (type === 'light') { 1214 | vibrancyType = NSVisualEffectMaterialLight 1215 | } else if (type === 'dark') { 1216 | vibrancyType = NSVisualEffectMaterialDark 1217 | } else if (type === 'titlebar') { 1218 | vibrancyType = NSVisualEffectMaterialTitlebar 1219 | } else if (type === 'selection') { 1220 | vibrancyType = NSVisualEffectMaterialSelection 1221 | } else if (type === 'menu') { 1222 | vibrancyType = NSVisualEffectMaterialMenu 1223 | } else if (type === 'popover') { 1224 | vibrancyType = NSVisualEffectMaterialPopover 1225 | } else if (type === 'sidebar') { 1226 | vibrancyType = NSVisualEffectMaterialSidebar 1227 | } else if (type === 'medium-light') { 1228 | vibrancyType = NSVisualEffectMaterialMediumLight 1229 | } else if (type === 'ultra-dark') { 1230 | vibrancyType = NSVisualEffectMaterialUltraDark 1231 | } 1232 | 1233 | effectView.setMaterial(vibrancyType) 1234 | } 1235 | 1236 | browserWindow._setBackgroundColor = function(colorName) { 1237 | var color = parseHexColor(colorName) 1238 | webview.setValue_forKey(false, 'drawsBackground') 1239 | panel.backgroundColor = color 1240 | } 1241 | 1242 | browserWindow._invalidate = function() { 1243 | panel.flushWindow() 1244 | panel.contentView().setNeedsDisplay(true) 1245 | } 1246 | 1247 | browserWindow._setStyleMask = function(on, flag) { 1248 | var wasMaximizable = browserWindow.isMaximizable() 1249 | if (on) { 1250 | panel.setStyleMask(panel.styleMask() | flag) 1251 | } else { 1252 | panel.setStyleMask(panel.styleMask() & ~flag) 1253 | } 1254 | // Change style mask will make the zoom button revert to default, probably 1255 | // a bug of Cocoa or macOS. 1256 | browserWindow.setMaximizable(wasMaximizable) 1257 | } 1258 | 1259 | browserWindow._setCollectionBehavior = function(on, flag) { 1260 | var wasMaximizable = browserWindow.isMaximizable() 1261 | if (on) { 1262 | panel.setCollectionBehavior(panel.collectionBehavior() | flag) 1263 | } else { 1264 | panel.setCollectionBehavior(panel.collectionBehavior() & ~flag) 1265 | } 1266 | // Change collectionBehavior will make the zoom button revert to default, 1267 | // probably a bug of Cocoa or macOS. 1268 | browserWindow.setMaximizable(wasMaximizable) 1269 | } 1270 | 1271 | browserWindow._showWindowButton = function(button) { 1272 | var view = panel.standardWindowButton(button) 1273 | view.superview().addSubview_positioned_relative(view, NSWindowAbove, null) 1274 | } 1275 | } 1276 | 1277 | 1278 | /***/ }), 1279 | 1280 | /***/ "./node_modules/sketch-module-web-view/lib/constants.js": 1281 | /*!**************************************************************!*\ 1282 | !*** ./node_modules/sketch-module-web-view/lib/constants.js ***! 1283 | \**************************************************************/ 1284 | /*! no static exports found */ 1285 | /***/ (function(module, exports) { 1286 | 1287 | module.exports = { 1288 | JS_BRIDGE: '__skpm_sketchBridge', 1289 | START_MOVING_WINDOW: '__skpm_startMovingWindow', 1290 | STOP_MOVING_WINDOW: '__skpm_stopMovingWindow', 1291 | MOVE_WINDOW: '__skpm_moveWindow', 1292 | EXECUTE_JAVASCRIPT: '__skpm_executeJS', 1293 | EXECUTE_JAVASCRIPT_SUCCESS: '__skpm_executeJS_success_', 1294 | EXECUTE_JAVASCRIPT_ERROR: '__skpm_executeJS_error_', 1295 | } 1296 | 1297 | 1298 | /***/ }), 1299 | 1300 | /***/ "./node_modules/sketch-module-web-view/lib/dispatch-first-click.js": 1301 | /*!*************************************************************************!*\ 1302 | !*** ./node_modules/sketch-module-web-view/lib/dispatch-first-click.js ***! 1303 | \*************************************************************************/ 1304 | /*! no static exports found */ 1305 | /***/ (function(module, exports) { 1306 | 1307 | var tagsToFocus = 1308 | '["text", "textarea", "date", "datetime-local", "email", "number", "month", "password", "search", "tel", "time", "url", "week" ]' 1309 | 1310 | module.exports = function(webView, event) { 1311 | var point = webView.convertPoint_fromView(event.locationInWindow(), null) 1312 | var x = point.x 1313 | var y = webView.frame().size.height - point.y // the coord start from the bottom instead of the top 1314 | return ( 1315 | 'var el = document.elementFromPoint(' + // get the DOM element that match the event 1316 | x + 1317 | ', ' + 1318 | y + 1319 | '); ' + 1320 | 'if (el && ' + // some tags need to be focused instead of clicked 1321 | tagsToFocus + 1322 | '.indexOf(el.type) >= 0 && ' + 1323 | 'el.focus' + 1324 | ') {' + 1325 | 'el.focus();' + // so focus them 1326 | '} else if (el) {' + 1327 | 'el.dispatchEvent(new Event("click", {bubbles: true}))' + // click the others 1328 | '}' 1329 | ) 1330 | } 1331 | 1332 | 1333 | /***/ }), 1334 | 1335 | /***/ "./node_modules/sketch-module-web-view/lib/execute-javascript.js": 1336 | /*!***********************************************************************!*\ 1337 | !*** ./node_modules/sketch-module-web-view/lib/execute-javascript.js ***! 1338 | \***********************************************************************/ 1339 | /*! no static exports found */ 1340 | /***/ (function(module, exports, __webpack_require__) { 1341 | 1342 | /* WEBPACK VAR INJECTION */(function(Promise) {var CONSTANTS = __webpack_require__(/*! ./constants */ "./node_modules/sketch-module-web-view/lib/constants.js") 1343 | 1344 | module.exports = function(webview, browserWindow) { 1345 | function executeJavaScript(script, userGesture, callback) { 1346 | if (typeof userGesture === 'function') { 1347 | callback = userGesture 1348 | userGesture = false 1349 | } 1350 | var fiber = coscript.createFiber() 1351 | 1352 | // if the webview is not ready yet, defer the execution until it is 1353 | if (webview.navigationDelegate().valueForKey('state').wasReady == 0) { 1354 | return new Promise(function(resolve, reject) { 1355 | browserWindow.once('ready-to-show', function() { 1356 | executeJavaScript(script, userGesture, callback) 1357 | .then(resolve) 1358 | .catch(reject) 1359 | fiber.cleanup() 1360 | }) 1361 | }) 1362 | } 1363 | 1364 | return new Promise(function(resolve, reject) { 1365 | var requestId = Math.random() 1366 | 1367 | browserWindow.webContents.on( 1368 | CONSTANTS.EXECUTE_JAVASCRIPT_SUCCESS + requestId, 1369 | function(res) { 1370 | try { 1371 | if (callback) { 1372 | callback(null, res) 1373 | } 1374 | resolve(res) 1375 | } catch (_) { 1376 | // /shrug 1377 | } 1378 | fiber.cleanup() 1379 | } 1380 | ) 1381 | browserWindow.webContents.on( 1382 | CONSTANTS.EXECUTE_JAVASCRIPT_ERROR + requestId, 1383 | function(err) { 1384 | try { 1385 | if (callback) { 1386 | callback(err) 1387 | resolve() 1388 | } else { 1389 | reject(err) 1390 | } 1391 | } catch (_) { 1392 | // /shrug 1393 | } 1394 | fiber.cleanup() 1395 | } 1396 | ) 1397 | 1398 | webview.evaluateJavaScript_completionHandler( 1399 | 'window.' + 1400 | CONSTANTS.EXECUTE_JAVASCRIPT + 1401 | '(' + 1402 | requestId + 1403 | ', "' + 1404 | script.replace(/"/g, '\\"') + 1405 | '")', 1406 | null 1407 | ) 1408 | }) 1409 | } 1410 | 1411 | return executeJavaScript 1412 | } 1413 | 1414 | module.exports.injectScript = function(webView) { 1415 | var source = 1416 | 'window.' + 1417 | CONSTANTS.EXECUTE_JAVASCRIPT + 1418 | ' = function(id, script) {' + 1419 | ' try {' + 1420 | ' var res = eval(script);' + 1421 | ' if (res && typeof res.then === "function" && typeof res.catch === "function") {' + 1422 | ' res.then(function (res2) {' + 1423 | ' window.postMessage("' + 1424 | CONSTANTS.EXECUTE_JAVASCRIPT_SUCCESS + 1425 | '" + id, res2);' + 1426 | ' })' + 1427 | ' .catch(function (err) {' + 1428 | ' window.postMessage("' + 1429 | CONSTANTS.EXECUTE_JAVASCRIPT_ERROR + 1430 | '" + id, err);' + 1431 | ' })' + 1432 | ' } else {' + 1433 | ' window.postMessage("' + 1434 | CONSTANTS.EXECUTE_JAVASCRIPT_SUCCESS + 1435 | '" + id, res);' + 1436 | ' }' + 1437 | ' } catch (err) {' + 1438 | ' window.postMessage("' + 1439 | CONSTANTS.EXECUTE_JAVASCRIPT_ERROR + 1440 | '" + id, err);' + 1441 | ' }' + 1442 | '}' 1443 | var script = WKUserScript.alloc().initWithSource_injectionTime_forMainFrameOnly( 1444 | source, 1445 | 0, 1446 | true 1447 | ) 1448 | webView 1449 | .configuration() 1450 | .userContentController() 1451 | .addUserScript(script) 1452 | } 1453 | 1454 | /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./node_modules/promise-polyfill/lib/index.js */ "./node_modules/promise-polyfill/lib/index.js"))) 1455 | 1456 | /***/ }), 1457 | 1458 | /***/ "./node_modules/sketch-module-web-view/lib/fitSubview.js": 1459 | /*!***************************************************************!*\ 1460 | !*** ./node_modules/sketch-module-web-view/lib/fitSubview.js ***! 1461 | \***************************************************************/ 1462 | /*! no static exports found */ 1463 | /***/ (function(module, exports) { 1464 | 1465 | function addEdgeConstraint(edge, subview, view, constant) { 1466 | view.addConstraint( 1467 | NSLayoutConstraint.constraintWithItem_attribute_relatedBy_toItem_attribute_multiplier_constant( 1468 | subview, 1469 | edge, 1470 | NSLayoutRelationEqual, 1471 | view, 1472 | edge, 1473 | 1, 1474 | constant 1475 | ) 1476 | ) 1477 | } 1478 | module.exports = function fitSubviewToView(subview, view, constants) { 1479 | constants = constants || [] 1480 | subview.setTranslatesAutoresizingMaskIntoConstraints(false) 1481 | 1482 | addEdgeConstraint(NSLayoutAttributeLeft, subview, view, constants[0] || 0) 1483 | addEdgeConstraint(NSLayoutAttributeTop, subview, view, constants[1] || 0) 1484 | addEdgeConstraint(NSLayoutAttributeRight, subview, view, constants[2] || 0) 1485 | addEdgeConstraint(NSLayoutAttributeBottom, subview, view, constants[3] || 0) 1486 | } 1487 | 1488 | 1489 | /***/ }), 1490 | 1491 | /***/ "./node_modules/sketch-module-web-view/lib/index.js": 1492 | /*!**********************************************************!*\ 1493 | !*** ./node_modules/sketch-module-web-view/lib/index.js ***! 1494 | \**********************************************************/ 1495 | /*! no static exports found */ 1496 | /***/ (function(module, exports, __webpack_require__) { 1497 | 1498 | /* let's try to match the API from Electron's Browser window 1499 | (https://github.com/electron/electron/blob/master/docs/api/browser-window.md) */ 1500 | var EventEmitter = __webpack_require__(/*! events */ "events") 1501 | var buildBrowserAPI = __webpack_require__(/*! ./browser-api */ "./node_modules/sketch-module-web-view/lib/browser-api.js") 1502 | var buildWebAPI = __webpack_require__(/*! ./webview-api */ "./node_modules/sketch-module-web-view/lib/webview-api.js") 1503 | var fitSubviewToView = __webpack_require__(/*! ./fitSubview */ "./node_modules/sketch-module-web-view/lib/fitSubview.js") 1504 | var dispatchFirstClick = __webpack_require__(/*! ./dispatch-first-click */ "./node_modules/sketch-module-web-view/lib/dispatch-first-click.js") 1505 | var injectClientMessaging = __webpack_require__(/*! ./inject-client-messaging */ "./node_modules/sketch-module-web-view/lib/inject-client-messaging.js") 1506 | var movableArea = __webpack_require__(/*! ./movable-area */ "./node_modules/sketch-module-web-view/lib/movable-area.js") 1507 | var executeJavaScript = __webpack_require__(/*! ./execute-javascript */ "./node_modules/sketch-module-web-view/lib/execute-javascript.js") 1508 | var setDelegates = __webpack_require__(/*! ./set-delegates */ "./node_modules/sketch-module-web-view/lib/set-delegates.js") 1509 | 1510 | function BrowserWindow(options) { 1511 | options = options || {} 1512 | 1513 | var identifier = options.identifier || NSUUID.UUID().UUIDString() 1514 | var threadDictionary = NSThread.mainThread().threadDictionary() 1515 | 1516 | var existingBrowserWindow = BrowserWindow.fromId(identifier) 1517 | 1518 | // if we already have a window opened, reuse it 1519 | if (existingBrowserWindow) { 1520 | return existingBrowserWindow 1521 | } 1522 | 1523 | var browserWindow = new EventEmitter() 1524 | browserWindow.id = identifier 1525 | 1526 | if (options.modal && !options.parent) { 1527 | throw new Error('A modal needs to have a parent.') 1528 | } 1529 | 1530 | // Long-running script 1531 | var fiber = coscript.createFiber() 1532 | 1533 | // Window size 1534 | var width = options.width || 800 1535 | var height = options.height || 600 1536 | var mainScreenRect = NSScreen.screens() 1537 | .firstObject() 1538 | .frame() 1539 | var cocoaBounds = NSMakeRect( 1540 | typeof options.x !== 'undefined' 1541 | ? options.x 1542 | : Math.round((NSWidth(mainScreenRect) - width) / 2), 1543 | typeof options.y !== 'undefined' 1544 | ? NSHeight(mainScreenRect) - options.y 1545 | : Math.round((NSHeight(mainScreenRect) - height) / 2), 1546 | width, 1547 | height 1548 | ) 1549 | 1550 | if (options.titleBarStyle && options.titleBarStyle !== 'default') { 1551 | options.frame = false 1552 | } 1553 | 1554 | var useStandardWindow = options.windowType !== 'textured' 1555 | var styleMask = NSTitledWindowMask 1556 | 1557 | // this is commented out because the toolbar doesn't appear otherwise :thinking-face: 1558 | // if (!useStandardWindow || options.frame === false) { 1559 | // styleMask = NSFullSizeContentViewWindowMask 1560 | // } 1561 | if (options.minimizable !== false) { 1562 | styleMask |= NSMiniaturizableWindowMask 1563 | } 1564 | if (options.closable !== false) { 1565 | styleMask |= NSClosableWindowMask 1566 | } 1567 | if (options.resizable !== false) { 1568 | styleMask |= NSResizableWindowMask 1569 | } 1570 | if (!useStandardWindow || options.transparent || options.frame === false) { 1571 | styleMask |= NSTexturedBackgroundWindowMask 1572 | } 1573 | 1574 | var panel = NSPanel.alloc().initWithContentRect_styleMask_backing_defer( 1575 | cocoaBounds, 1576 | styleMask, 1577 | NSBackingStoreBuffered, 1578 | true 1579 | ) 1580 | 1581 | var wkwebviewConfig = WKWebViewConfiguration.alloc().init() 1582 | var webView = WKWebView.alloc().initWithFrame_configuration( 1583 | CGRectMake(0, 0, options.width || 800, options.height || 600), 1584 | wkwebviewConfig 1585 | ) 1586 | injectClientMessaging(webView) 1587 | webView.setAutoresizingMask(NSViewWidthSizable | NSViewHeightSizable) 1588 | 1589 | buildBrowserAPI(browserWindow, panel, webView) 1590 | buildWebAPI(browserWindow, panel, webView) 1591 | setDelegates(browserWindow, panel, webView, options) 1592 | 1593 | if (options.windowType === 'desktop') { 1594 | panel.setLevel(kCGDesktopWindowLevel - 1) 1595 | // panel.setCanBecomeKeyWindow(false) 1596 | panel.setCollectionBehavior( 1597 | NSWindowCollectionBehaviorCanJoinAllSpaces | 1598 | NSWindowCollectionBehaviorStationary | 1599 | NSWindowCollectionBehaviorIgnoresCycle 1600 | ) 1601 | } 1602 | 1603 | if ( 1604 | typeof options.minWidth !== 'undefined' || 1605 | typeof options.minHeight !== 'undefined' 1606 | ) { 1607 | browserWindow.setMinimumSize(options.minWidth || 0, options.minHeight || 0) 1608 | } 1609 | 1610 | if ( 1611 | typeof options.maxWidth !== 'undefined' || 1612 | typeof options.maxHeight !== 'undefined' 1613 | ) { 1614 | browserWindow.setMaximumSize( 1615 | options.maxWidth || 10000, 1616 | options.maxHeight || 10000 1617 | ) 1618 | } 1619 | 1620 | // if (options.focusable === false) { 1621 | // panel.setCanBecomeKeyWindow(false) 1622 | // } 1623 | 1624 | if (options.transparent || options.frame === false) { 1625 | panel.titlebarAppearsTransparent = true 1626 | panel.titleVisibility = NSWindowTitleHidden 1627 | panel.setOpaque(0) 1628 | panel.isMovableByWindowBackground = true 1629 | var toolbar2 = NSToolbar.alloc().initWithIdentifier( 1630 | 'titlebarStylingToolbar' 1631 | ) 1632 | toolbar2.setShowsBaselineSeparator(false) 1633 | panel.setToolbar(toolbar2) 1634 | } 1635 | 1636 | if (options.titleBarStyle === 'hiddenInset') { 1637 | var toolbar = NSToolbar.alloc().initWithIdentifier('titlebarStylingToolbar') 1638 | toolbar.setShowsBaselineSeparator(false) 1639 | panel.setToolbar(toolbar) 1640 | } 1641 | 1642 | if (options.frame === false || !options.useContentSize) { 1643 | browserWindow.setSize(width, height) 1644 | } 1645 | 1646 | if (options.center) { 1647 | browserWindow.center() 1648 | } 1649 | 1650 | if (options.alwaysOnTop) { 1651 | browserWindow.setAlwaysOnTop(true) 1652 | } 1653 | 1654 | if (options.fullscreen) { 1655 | browserWindow.setFullScreen(true) 1656 | } 1657 | browserWindow.setFullScreenable(!!options.fullscreenable) 1658 | 1659 | const title = 1660 | options.title || 1661 | (typeof __command !== 'undefined' && __command.pluginBundle() 1662 | ? __command.pluginBundle().name() 1663 | : undefined) 1664 | if (title) { 1665 | browserWindow.setTitle(title) 1666 | } 1667 | 1668 | var backgroundColor = options.backgroundColor 1669 | if (options.transparent) { 1670 | backgroundColor = NSColor.clearColor() 1671 | } 1672 | if (!backgroundColor && options.frame === false && options.vibrancy) { 1673 | backgroundColor = NSColor.clearColor() 1674 | } 1675 | 1676 | browserWindow._setBackgroundColor( 1677 | backgroundColor || NSColor.windowBackgroundColor() 1678 | ) 1679 | 1680 | if (options.hasShadow === false) { 1681 | browserWindow.setHasShadow(false) 1682 | } 1683 | 1684 | if (typeof options.opacity !== 'undefined') { 1685 | browserWindow.setOpacity(options.opacity) 1686 | } 1687 | 1688 | options.webPreferences = options.webPreferences || {} 1689 | 1690 | webView 1691 | .configuration() 1692 | .preferences() 1693 | .setValue_forKey( 1694 | options.webPreferences.devTools !== false, 1695 | 'developerExtrasEnabled' 1696 | ) 1697 | webView 1698 | .configuration() 1699 | .preferences() 1700 | .setValue_forKey( 1701 | options.webPreferences.javascript !== false, 1702 | 'javaScriptEnabled' 1703 | ) 1704 | webView 1705 | .configuration() 1706 | .preferences() 1707 | .setValue_forKey(!!options.webPreferences.plugins, 'plugInsEnabled') 1708 | webView 1709 | .configuration() 1710 | .preferences() 1711 | .setValue_forKey( 1712 | options.webPreferences.minimumFontSize || 0, 1713 | 'minimumFontSize' 1714 | ) 1715 | 1716 | if (options.webPreferences.zoomFactor) { 1717 | webView.setMagnification(options.webPreferences.zoomFactor) 1718 | } 1719 | 1720 | var contentView = panel.contentView() 1721 | 1722 | if (options.frame !== false) { 1723 | webView.setFrame(contentView.bounds()) 1724 | contentView.addSubview(webView) 1725 | } else { 1726 | // In OSX 10.10, adding subviews to the root view for the NSView hierarchy 1727 | // produces warnings. To eliminate the warnings, we resize the contentView 1728 | // to fill the window, and add subviews to that. 1729 | // http://crbug.com/380412 1730 | contentView.setAutoresizingMask(NSViewWidthSizable | NSViewHeightSizable) 1731 | fitSubviewToView(contentView, contentView.superview()) 1732 | 1733 | webView.setFrame(contentView.bounds()) 1734 | contentView.addSubview(webView) 1735 | 1736 | // The fullscreen button should always be hidden for frameless window. 1737 | if (panel.standardWindowButton(NSWindowFullScreenButton)) { 1738 | panel.standardWindowButton(NSWindowFullScreenButton).setHidden(true) 1739 | } 1740 | 1741 | if (!options.titleBarStyle || options.titleBarStyle === 'default') { 1742 | // Hide the window buttons. 1743 | panel.standardWindowButton(NSWindowZoomButton).setHidden(true) 1744 | panel.standardWindowButton(NSWindowMiniaturizeButton).setHidden(true) 1745 | panel.standardWindowButton(NSWindowCloseButton).setHidden(true) 1746 | 1747 | // Some third-party macOS utilities check the zoom button's enabled state to 1748 | // determine whether to show custom UI on hover, so we disable it here to 1749 | // prevent them from doing so in a frameless app window. 1750 | panel.standardWindowButton(NSWindowZoomButton).setEnabled(false) 1751 | } 1752 | } 1753 | 1754 | if (options.vibrancy) { 1755 | browserWindow.setVibrancy(options.vibrancy) 1756 | } 1757 | 1758 | // Set maximizable state last to ensure zoom button does not get reset 1759 | // by calls to other APIs. 1760 | browserWindow.setMaximizable(options.maximizable !== false) 1761 | 1762 | if (options.acceptsFirstMouse) { 1763 | browserWindow.on('focus', function(event) { 1764 | if (event.type() === NSEventTypeLeftMouseDown) { 1765 | browserWindow.webContents 1766 | .executeJavaScript(dispatchFirstClick(webView, event)) 1767 | .catch(() => {}) 1768 | } 1769 | }) 1770 | } 1771 | 1772 | executeJavaScript.injectScript(webView) 1773 | movableArea.injectScript(webView) 1774 | movableArea.setupHandler(browserWindow) 1775 | 1776 | if (options.show !== false) { 1777 | browserWindow.show() 1778 | } 1779 | 1780 | browserWindow.on('closed', function() { 1781 | browserWindow._destroyed = true 1782 | threadDictionary.removeObjectForKey(identifier) 1783 | fiber.cleanup() 1784 | }) 1785 | 1786 | threadDictionary[identifier] = panel 1787 | 1788 | fiber.onCleanup(function() { 1789 | if (!browserWindow._destroyed) { 1790 | browserWindow.destroy() 1791 | } 1792 | }) 1793 | 1794 | return browserWindow 1795 | } 1796 | 1797 | BrowserWindow.fromId = function(identifier) { 1798 | var threadDictionary = NSThread.mainThread().threadDictionary() 1799 | 1800 | if (threadDictionary[identifier]) { 1801 | return BrowserWindow.fromPanel(threadDictionary[identifier], identifier) 1802 | } 1803 | 1804 | return undefined 1805 | } 1806 | 1807 | BrowserWindow.fromPanel = function(panel, identifier) { 1808 | var browserWindow = new EventEmitter() 1809 | browserWindow.id = identifier 1810 | 1811 | if (!panel || !panel.contentView) { 1812 | throw new Error('needs to pass an NSPanel') 1813 | } 1814 | 1815 | var webView = panel.contentView().subviews()[0] 1816 | 1817 | if (!webView) { 1818 | throw new Error('The NSPanel needs to have a webview') 1819 | } 1820 | 1821 | buildBrowserAPI(browserWindow, panel, webView) 1822 | buildWebAPI(browserWindow, panel, webView) 1823 | 1824 | return browserWindow 1825 | } 1826 | 1827 | module.exports = BrowserWindow 1828 | 1829 | 1830 | /***/ }), 1831 | 1832 | /***/ "./node_modules/sketch-module-web-view/lib/inject-client-messaging.js": 1833 | /*!****************************************************************************!*\ 1834 | !*** ./node_modules/sketch-module-web-view/lib/inject-client-messaging.js ***! 1835 | \****************************************************************************/ 1836 | /*! no static exports found */ 1837 | /***/ (function(module, exports, __webpack_require__) { 1838 | 1839 | var CONSTANTS = __webpack_require__(/*! ./constants */ "./node_modules/sketch-module-web-view/lib/constants.js") 1840 | 1841 | module.exports = function(webView) { 1842 | var source = 1843 | 'window.originalPostMessage = window.postMessage;' + 1844 | 'window.postMessage = function(actionName) {' + 1845 | 'if (!actionName) {' + 1846 | "throw new Error('missing action name')" + 1847 | '}' + 1848 | 'window.webkit.messageHandlers.' + 1849 | CONSTANTS.JS_BRIDGE + 1850 | '.postMessage(' + 1851 | 'JSON.stringify([].slice.call(arguments))' + 1852 | ');' + 1853 | '}' 1854 | var script = WKUserScript.alloc().initWithSource_injectionTime_forMainFrameOnly( 1855 | source, 1856 | 0, 1857 | true 1858 | ) 1859 | webView 1860 | .configuration() 1861 | .userContentController() 1862 | .addUserScript(script) 1863 | } 1864 | 1865 | 1866 | /***/ }), 1867 | 1868 | /***/ "./node_modules/sketch-module-web-view/lib/movable-area.js": 1869 | /*!*****************************************************************!*\ 1870 | !*** ./node_modules/sketch-module-web-view/lib/movable-area.js ***! 1871 | \*****************************************************************/ 1872 | /*! no static exports found */ 1873 | /***/ (function(module, exports, __webpack_require__) { 1874 | 1875 | var CONSTANTS = __webpack_require__(/*! ./constants */ "./node_modules/sketch-module-web-view/lib/constants.js") 1876 | 1877 | module.exports.injectScript = function(webView) { 1878 | var source = 1879 | '(function () {' + 1880 | 'var animationId = null;' + 1881 | "document.addEventListener('mousedown', onMouseDown);" + 1882 | '' + 1883 | 'function shouldDrag(target) {' + 1884 | ' if (!target || (target.dataset || {}).appRegion === "no-drag") { return false }' + 1885 | ' if ((target.dataset || {}).appRegion === "drag") { return true }' + 1886 | ' return shouldDrag(target.parentElement)' + 1887 | '};' + 1888 | '' + 1889 | 'function onMouseDown(e) {' + 1890 | ' if (e.button !== 0 || !shouldDrag(e.target)) { return }' + 1891 | ' window.postMessage("' + 1892 | CONSTANTS.START_MOVING_WINDOW + 1893 | '");' + 1894 | " document.addEventListener('mouseup', onMouseUp);" + 1895 | ' animationId = requestAnimationFrame(moveWindow);' + 1896 | '};' + 1897 | '' + 1898 | 'function onMouseUp(e) {' + 1899 | ' window.postMessage("' + 1900 | CONSTANTS.STOP_MOVING_WINDOW + 1901 | '");' + 1902 | " document.removeEventListener('mouseup', onMouseUp);" + 1903 | ' cancelAnimationFrame(animationId);' + 1904 | '};' + 1905 | '' + 1906 | 'function moveWindow(e) {' + 1907 | ' window.postMessage("' + 1908 | CONSTANTS.MOVE_WINDOW + 1909 | '");' + 1910 | ' animationId = requestAnimationFrame(moveWindow);' + 1911 | '}' + 1912 | '})()' 1913 | var script = WKUserScript.alloc().initWithSource_injectionTime_forMainFrameOnly( 1914 | source, 1915 | 0, 1916 | true 1917 | ) 1918 | webView 1919 | .configuration() 1920 | .userContentController() 1921 | .addUserScript(script) 1922 | } 1923 | 1924 | module.exports.setupHandler = function(browserWindow) { 1925 | var initialMouseLocation = null 1926 | var initialWindowPosition = null 1927 | 1928 | browserWindow.webContents.on(CONSTANTS.START_MOVING_WINDOW, function() { 1929 | initialMouseLocation = NSEvent.mouseLocation() 1930 | var position = browserWindow.getPosition() 1931 | initialWindowPosition = { 1932 | x: position[0], 1933 | y: position[1], 1934 | } 1935 | }) 1936 | 1937 | browserWindow.webContents.on(CONSTANTS.STOP_MOVING_WINDOW, function() { 1938 | initialMouseLocation = null 1939 | initialWindowPosition = null 1940 | }) 1941 | 1942 | browserWindow.webContents.on(CONSTANTS.MOVE_WINDOW, function() { 1943 | if (!initialWindowPosition) { 1944 | return 1945 | } 1946 | const mouse = NSEvent.mouseLocation() 1947 | browserWindow.setPosition( 1948 | initialWindowPosition.x + (mouse.x - initialMouseLocation.x), 1949 | initialWindowPosition.y + (initialMouseLocation.y - mouse.y), // y is inverted 1950 | false 1951 | ) 1952 | }) 1953 | } 1954 | 1955 | 1956 | /***/ }), 1957 | 1958 | /***/ "./node_modules/sketch-module-web-view/lib/parseWebArguments.js": 1959 | /*!**********************************************************************!*\ 1960 | !*** ./node_modules/sketch-module-web-view/lib/parseWebArguments.js ***! 1961 | \**********************************************************************/ 1962 | /*! no static exports found */ 1963 | /***/ (function(module, exports) { 1964 | 1965 | module.exports = function(webArguments) { 1966 | var args = null 1967 | try { 1968 | args = JSON.parse(webArguments) 1969 | } catch (e) { 1970 | // malformed arguments 1971 | } 1972 | 1973 | if ( 1974 | !args || 1975 | !args.constructor || 1976 | args.constructor !== Array || 1977 | args.length == 0 1978 | ) { 1979 | return null 1980 | } 1981 | 1982 | return args 1983 | } 1984 | 1985 | 1986 | /***/ }), 1987 | 1988 | /***/ "./node_modules/sketch-module-web-view/lib/set-delegates.js": 1989 | /*!******************************************************************!*\ 1990 | !*** ./node_modules/sketch-module-web-view/lib/set-delegates.js ***! 1991 | \******************************************************************/ 1992 | /*! no static exports found */ 1993 | /***/ (function(module, exports, __webpack_require__) { 1994 | 1995 | var ObjCClass = __webpack_require__(/*! cocoascript-class */ "./node_modules/cocoascript-class/lib/index.js").default 1996 | var parseWebArguments = __webpack_require__(/*! ./parseWebArguments */ "./node_modules/sketch-module-web-view/lib/parseWebArguments.js") 1997 | var CONSTANTS = __webpack_require__(/*! ./constants */ "./node_modules/sketch-module-web-view/lib/constants.js") 1998 | 1999 | // We create one ObjC class for ourselves here 2000 | var WindowDelegateClass 2001 | var NavigationDelegateClass 2002 | var WebScriptHandlerClass 2003 | 2004 | // TODO: events 2005 | // - 'page-favicon-updated' 2006 | // - 'new-window' 2007 | // - 'did-navigate-in-page' 2008 | // - 'will-prevent-unload' 2009 | // - 'crashed' 2010 | // - 'unresponsive' 2011 | // - 'responsive' 2012 | // - 'destroyed' 2013 | // - 'before-input-event' 2014 | // - 'certificate-error' 2015 | // - 'found-in-page' 2016 | // - 'media-started-playing' 2017 | // - 'media-paused' 2018 | // - 'did-change-theme-color' 2019 | // - 'update-target-url' 2020 | // - 'cursor-changed' 2021 | // - 'context-menu' 2022 | // - 'select-bluetooth-device' 2023 | // - 'paint' 2024 | // - 'console-message' 2025 | 2026 | module.exports = function(browserWindow, panel, webview, options) { 2027 | if (!WindowDelegateClass) { 2028 | WindowDelegateClass = ObjCClass({ 2029 | classname: 'WindowDelegateClass', 2030 | utils: null, 2031 | panel: null, 2032 | 2033 | 'windowDidResize:': function() { 2034 | this.utils.emit('resize') 2035 | }, 2036 | 2037 | 'windowDidMiniaturize:': function() { 2038 | this.utils.emit('minimize') 2039 | }, 2040 | 2041 | 'windowDidDeminiaturize:': function() { 2042 | this.utils.emit('restore') 2043 | }, 2044 | 2045 | 'windowDidEnterFullScreen:': function() { 2046 | this.utils.emit('enter-full-screen') 2047 | }, 2048 | 2049 | 'windowDidExitFullScreen:': function() { 2050 | this.utils.emit('leave-full-screen') 2051 | }, 2052 | 2053 | 'windowDidMove:': function() { 2054 | this.utils.emit('move') 2055 | this.utils.emit('moved') 2056 | }, 2057 | 2058 | 'windowShouldClose:': function() { 2059 | var shouldClose = true 2060 | this.utils.emit('close', { 2061 | get defaultPrevented() { 2062 | return !shouldClose 2063 | }, 2064 | preventDefault: function() { 2065 | shouldClose = false 2066 | }, 2067 | }) 2068 | return shouldClose 2069 | }, 2070 | 2071 | 'windowWillClose:': function() { 2072 | this.utils.emit('closed') 2073 | }, 2074 | 2075 | 'windowDidBecomeKey:': function() { 2076 | this.utils.emit('focus', this.panel.currentEvent()) 2077 | }, 2078 | 2079 | 'windowDidResignKey:': function() { 2080 | this.utils.emit('blur') 2081 | }, 2082 | }) 2083 | } 2084 | 2085 | if (!NavigationDelegateClass) { 2086 | NavigationDelegateClass = ObjCClass({ 2087 | classname: 'NavigationDelegateClass', 2088 | state: NSMutableDictionary.dictionaryWithDictionary({ 2089 | wasReady: 0, 2090 | }), 2091 | utils: null, 2092 | 2093 | // // Called when the web view begins to receive web content. 2094 | 'webView:didCommitNavigation:': function(webView) { 2095 | this.utils.emit('will-navigate', {}, String(String(webView.URL()))) 2096 | }, 2097 | 2098 | // // Called when web content begins to load in a web view. 2099 | 'webView:didStartProvisionalNavigation:': function() { 2100 | this.utils.emit('did-start-navigation') 2101 | this.utils.emit('did-start-loading') 2102 | }, 2103 | 2104 | // Called when a web view receives a server redirect. 2105 | 'webView:didReceiveServerRedirectForProvisionalNavigation:': function() { 2106 | this.utils.emit('did-get-redirect-request') 2107 | }, 2108 | 2109 | // // Called when the web view needs to respond to an authentication challenge. 2110 | // 'webView:didReceiveAuthenticationChallenge:completionHandler:': function( 2111 | // webView, 2112 | // challenge, 2113 | // completionHandler 2114 | // ) { 2115 | // function callback(username, password) { 2116 | // completionHandler( 2117 | // 0, 2118 | // NSURLCredential.credentialWithUser_password_persistence( 2119 | // username, 2120 | // password, 2121 | // 1 2122 | // ) 2123 | // ) 2124 | // } 2125 | // var protectionSpace = challenge.protectionSpace() 2126 | // this.utils.emit( 2127 | // 'login', 2128 | // {}, 2129 | // { 2130 | // method: String(protectionSpace.authenticationMethod()), 2131 | // url: 'not implemented', // TODO: 2132 | // referrer: 'not implemented', // TODO: 2133 | // }, 2134 | // { 2135 | // isProxy: !!protectionSpace.isProxy(), 2136 | // scheme: String(protectionSpace.protocol()), 2137 | // host: String(protectionSpace.host()), 2138 | // port: Number(protectionSpace.port()), 2139 | // realm: String(protectionSpace.realm()), 2140 | // }, 2141 | // callback 2142 | // ) 2143 | // }, 2144 | 2145 | // Called when an error occurs during navigation. 2146 | // 'webView:didFailNavigation:withError:': function( 2147 | // webView, 2148 | // navigation, 2149 | // error 2150 | // ) {}, 2151 | 2152 | // Called when an error occurs while the web view is loading content. 2153 | 'webView:didFailProvisionalNavigation:withError:': function( 2154 | webView, 2155 | navigation, 2156 | error 2157 | ) { 2158 | this.utils.emit('did-fail-load', error) 2159 | }, 2160 | 2161 | // Called when the navigation is complete. 2162 | 'webView:didFinishNavigation:': function() { 2163 | if (this.state.wasReady == 0) { 2164 | this.state.setObject_forKey(1, 'wasReady') 2165 | this.utils.emitBrowserEvent('ready-to-show') 2166 | } 2167 | this.utils.emit('did-navigate') 2168 | this.utils.emit('did-frame-navigate') 2169 | this.utils.emit('did-stop-loading') 2170 | this.utils.emit('did-finish-load') 2171 | this.utils.emit('did-frame-finish-load') 2172 | }, 2173 | 2174 | // Called when the web view’s web content process is terminated. 2175 | 'webViewWebContentProcessDidTerminate:': function() { 2176 | this.utils.emit('dom-ready') 2177 | }, 2178 | 2179 | // Decides whether to allow or cancel a navigation. 2180 | // webView:decidePolicyForNavigationAction:decisionHandler: 2181 | 2182 | // Decides whether to allow or cancel a navigation after its response is known. 2183 | // webView:decidePolicyForNavigationResponse:decisionHandler: 2184 | }) 2185 | } 2186 | 2187 | if (!WebScriptHandlerClass) { 2188 | WebScriptHandlerClass = ObjCClass({ 2189 | classname: 'WebScriptHandlerClass', 2190 | utils: null, 2191 | 'userContentController:didReceiveScriptMessage:': function(_, message) { 2192 | var args = this.utils.parseWebArguments(String(message.body())) 2193 | if (!args) { 2194 | return 2195 | } 2196 | if (!args[0] || typeof args[0] !== 'string') { 2197 | return 2198 | } 2199 | args[0] = String(args[0]) 2200 | 2201 | this.utils.emit.apply(this, args) 2202 | }, 2203 | }) 2204 | } 2205 | 2206 | var navigationDelegate = NavigationDelegateClass.new() 2207 | navigationDelegate.utils = NSDictionary.dictionaryWithDictionary({ 2208 | setTitle: browserWindow.setTitle.bind(browserWindow), 2209 | emitBrowserEvent() { 2210 | try { 2211 | browserWindow.emit.apply(browserWindow, arguments) 2212 | } catch (err) { 2213 | console.error(err) 2214 | throw err 2215 | } 2216 | }, 2217 | emit() { 2218 | try { 2219 | browserWindow.webContents.emit.apply( 2220 | browserWindow.webContents, 2221 | arguments 2222 | ) 2223 | } catch (err) { 2224 | console.error(err) 2225 | throw err 2226 | } 2227 | }, 2228 | }) 2229 | // reset state as well 2230 | navigationDelegate.state = NSMutableDictionary.dictionaryWithDictionary({ 2231 | wasReady: 0, 2232 | }) 2233 | 2234 | webview.setNavigationDelegate(navigationDelegate) 2235 | 2236 | var webScriptHandler = WebScriptHandlerClass.new() 2237 | webScriptHandler.utils = NSDictionary.dictionaryWithDictionary({ 2238 | emit() { 2239 | try { 2240 | browserWindow.webContents.emit.apply( 2241 | browserWindow.webContents, 2242 | arguments 2243 | ) 2244 | } catch (err) { 2245 | console.error(err) 2246 | throw err 2247 | } 2248 | }, 2249 | parseWebArguments: parseWebArguments, 2250 | }) 2251 | 2252 | webview 2253 | .configuration() 2254 | .userContentController() 2255 | .addScriptMessageHandler_name(webScriptHandler, CONSTANTS.JS_BRIDGE) 2256 | 2257 | var windowDelegate = WindowDelegateClass.new() 2258 | var utils = { 2259 | emit() { 2260 | try { 2261 | browserWindow.emit.apply(browserWindow, arguments) 2262 | } catch (err) { 2263 | console.error(err) 2264 | throw err 2265 | } 2266 | }, 2267 | } 2268 | if (options.modal) { 2269 | // find the window of the document 2270 | var msdocument 2271 | if (options.parent.type === 'Document') { 2272 | msdocument = options.parent.sketchObject 2273 | } else { 2274 | msdocument = options.parent 2275 | } 2276 | if (msdocument && String(msdocument.class()) === 'MSDocumentData') { 2277 | // we only have an MSDocumentData instead of a MSDocument 2278 | // let's try to get back to the MSDocument 2279 | msdocument = msdocument.delegate() 2280 | } 2281 | utils.parentWindow = msdocument.windowForSheet() 2282 | } 2283 | 2284 | windowDelegate.utils = NSDictionary.dictionaryWithDictionary(utils) 2285 | windowDelegate.panel = panel 2286 | 2287 | panel.setDelegate(windowDelegate) 2288 | } 2289 | 2290 | 2291 | /***/ }), 2292 | 2293 | /***/ "./node_modules/sketch-module-web-view/lib/webview-api.js": 2294 | /*!****************************************************************!*\ 2295 | !*** ./node_modules/sketch-module-web-view/lib/webview-api.js ***! 2296 | \****************************************************************/ 2297 | /*! no static exports found */ 2298 | /***/ (function(module, exports, __webpack_require__) { 2299 | 2300 | var EventEmitter = __webpack_require__(/*! events */ "events") 2301 | var executeJavaScript = __webpack_require__(/*! ./execute-javascript */ "./node_modules/sketch-module-web-view/lib/execute-javascript.js") 2302 | 2303 | // let's try to match https://github.com/electron/electron/blob/master/docs/api/web-contents.md 2304 | module.exports = function buildAPI(browserWindow, panel, webview) { 2305 | var webContents = new EventEmitter() 2306 | 2307 | webContents.loadURL = browserWindow.loadURL 2308 | 2309 | webContents.loadFile = function(/* filePath */) { 2310 | // TODO: 2311 | console.warn( 2312 | 'Not implemented yet, please open a PR on https://github.com/skpm/sketch-module-web-view :)' 2313 | ) 2314 | } 2315 | 2316 | webContents.downloadURL = function(/* filePath */) { 2317 | // TODO: 2318 | console.warn( 2319 | 'Not implemented yet, please open a PR on https://github.com/skpm/sketch-module-web-view :)' 2320 | ) 2321 | } 2322 | 2323 | webContents.getURL = function() { 2324 | return String(webview.url()) 2325 | } 2326 | 2327 | webContents.getTitle = function() { 2328 | return String(webview.title()) 2329 | } 2330 | 2331 | webContents.isDestroyed = function() { 2332 | // TODO: 2333 | console.warn( 2334 | 'Not implemented yet, please open a PR on https://github.com/skpm/sketch-module-web-view :)' 2335 | ) 2336 | } 2337 | 2338 | webContents.focus = browserWindow.focus 2339 | webContents.isFocused = browserWindow.isFocused 2340 | 2341 | webContents.isLoading = function() { 2342 | return !!webview.loading() 2343 | } 2344 | 2345 | webContents.isLoadingMainFrame = function() { 2346 | // TODO: 2347 | return !!webview.loading() 2348 | } 2349 | 2350 | webContents.isWaitingForResponse = function() { 2351 | return !webview.loading() 2352 | } 2353 | 2354 | webContents.stop = function() { 2355 | webview.stopLoading() 2356 | } 2357 | webContents.reload = function() { 2358 | webview.reload() 2359 | } 2360 | webContents.reloadIgnoringCache = function() { 2361 | webview.reloadFromOrigin() 2362 | } 2363 | webContents.canGoBack = function() { 2364 | return !!webview.canGoBack() 2365 | } 2366 | webContents.canGoForward = function() { 2367 | return !!webview.canGoForward() 2368 | } 2369 | webContents.canGoToOffset = function(offset) { 2370 | return !!webview.backForwardList().itemAtIndex(offset) 2371 | } 2372 | webContents.clearHistory = function() { 2373 | // TODO: 2374 | console.warn( 2375 | 'Not implemented yet, please open a PR on https://github.com/skpm/sketch-module-web-view :)' 2376 | ) 2377 | } 2378 | webContents.goBack = function() { 2379 | webview.goBack() 2380 | } 2381 | webContents.goForward = function() { 2382 | webview.goForward() 2383 | } 2384 | webContents.goToIndex = function(index) { 2385 | var backForwardList = webview.backForwardList() 2386 | var backList = backForwardList.backList() 2387 | var backListLength = backList.count() 2388 | if (backListLength > index) { 2389 | webview.loadRequest(NSURLRequest.requestWithURL(backList[index])) 2390 | return 2391 | } 2392 | var forwardList = backForwardList.forwardList() 2393 | if (forwardList.count() > index - backListLength) { 2394 | webview.loadRequest( 2395 | NSURLRequest.requestWithURL(forwardList[index - backListLength]) 2396 | ) 2397 | return 2398 | } 2399 | throw new Error('Cannot go to index ' + index) 2400 | } 2401 | webContents.goToOffset = function(offset) { 2402 | if (!webContents.canGoToOffset(offset)) { 2403 | throw new Error('Cannot go to offset ' + offset) 2404 | } 2405 | webview.loadRequest( 2406 | NSURLRequest.requestWithURL(webview.backForwardList().itemAtIndex(offset)) 2407 | ) 2408 | } 2409 | webContents.isCrashed = function() { 2410 | // TODO: 2411 | console.warn( 2412 | 'Not implemented yet, please open a PR on https://github.com/skpm/sketch-module-web-view :)' 2413 | ) 2414 | } 2415 | webContents.setUserAgent = function(/* userAgent */) { 2416 | // TODO: 2417 | console.warn( 2418 | 'Not implemented yet, please open a PR on https://github.com/skpm/sketch-module-web-view :)' 2419 | ) 2420 | } 2421 | webContents.getUserAgent = function() { 2422 | const userAgent = webview.customUserAgent() 2423 | return userAgent ? String(userAgent) : undefined 2424 | } 2425 | webContents.insertCSS = function(css) { 2426 | var source = 2427 | "var style = document.createElement('style'); style.innerHTML = " + 2428 | css.replace(/"/, '\\"') + 2429 | '; document.head.appendChild(style);' 2430 | var script = WKUserScript.alloc().initWithSource_injectionTime_forMainFrameOnly( 2431 | source, 2432 | 0, 2433 | true 2434 | ) 2435 | webview 2436 | .configuration() 2437 | .userContentController() 2438 | .addUserScript(script) 2439 | } 2440 | webContents.insertJS = function(source) { 2441 | var script = WKUserScript.alloc().initWithSource_injectionTime_forMainFrameOnly( 2442 | source, 2443 | 0, 2444 | true 2445 | ) 2446 | webview 2447 | .configuration() 2448 | .userContentController() 2449 | .addUserScript(script) 2450 | } 2451 | webContents.executeJavaScript = executeJavaScript(webview, browserWindow) 2452 | webContents.setIgnoreMenuShortcuts = function() { 2453 | // TODO:?? 2454 | console.warn( 2455 | 'Not implemented yet, please open a PR on https://github.com/skpm/sketch-module-web-view :)' 2456 | ) 2457 | } 2458 | webContents.setAudioMuted = function(/* muted */) { 2459 | // TODO:?? 2460 | console.warn( 2461 | 'Not implemented yet, please open a PR on https://github.com/skpm/sketch-module-web-view :)' 2462 | ) 2463 | } 2464 | webContents.isAudioMuted = function() { 2465 | // TODO:?? 2466 | console.warn( 2467 | 'Not implemented yet, please open a PR on https://github.com/skpm/sketch-module-web-view :)' 2468 | ) 2469 | } 2470 | webContents.setZoomFactor = function(factor) { 2471 | webview.setMagnification_centeredAtPoint(factor, CGPointMake(0, 0)) 2472 | } 2473 | webContents.getZoomFactor = function(callback) { 2474 | callback(Number(webview.magnification())) 2475 | } 2476 | webContents.setZoomLevel = function(level) { 2477 | // eslint-disable-next-line no-restricted-properties 2478 | webContents.setZoomFactor(Math.pow(1.2, level)) 2479 | } 2480 | webContents.getZoomLevel = function(callback) { 2481 | // eslint-disable-next-line no-restricted-properties 2482 | callback(Math.log(Number(webview.magnification())) / Math.log(1.2)) 2483 | } 2484 | webContents.setVisualZoomLevelLimits = function(/* minimumLevel, maximumLevel */) { 2485 | // TODO:?? 2486 | console.warn( 2487 | 'Not implemented yet, please open a PR on https://github.com/skpm/sketch-module-web-view :)' 2488 | ) 2489 | } 2490 | webContents.setLayoutZoomLevelLimits = function(/* minimumLevel, maximumLevel */) { 2491 | // TODO:?? 2492 | console.warn( 2493 | 'Not implemented yet, please open a PR on https://github.com/skpm/sketch-module-web-view :)' 2494 | ) 2495 | } 2496 | 2497 | // TODO: 2498 | // webContents.undo = function() { 2499 | // webview.undoManager().undo() 2500 | // } 2501 | // webContents.redo = function() { 2502 | // webview.undoManager().redo() 2503 | // } 2504 | // webContents.cut = webview.cut 2505 | // webContents.copy = webview.copy 2506 | // webContents.paste = webview.paste 2507 | // webContents.pasteAndMatchStyle = webview.pasteAsRichText 2508 | // webContents.delete = webview.delete 2509 | // webContents.replace = webview.replaceSelectionWithText 2510 | 2511 | webContents.send = function() { 2512 | const script = 2513 | 'window.postMessage({' + 2514 | 'isSketchMessage: true,' + 2515 | "origin: '" + 2516 | String(__command.identifier()) + 2517 | "'," + 2518 | 'args: ' + 2519 | JSON.stringify([].slice.call(arguments)) + 2520 | '}, "*")' 2521 | webview.evaluateJavaScript_completionHandler(script, null) 2522 | } 2523 | 2524 | webContents.getNativeWebview = function() { 2525 | return webview 2526 | } 2527 | 2528 | browserWindow.webContents = webContents 2529 | } 2530 | 2531 | 2532 | /***/ }), 2533 | 2534 | /***/ "./resources/webview/webview.html": 2535 | /*!****************************************!*\ 2536 | !*** ./resources/webview/webview.html ***! 2537 | \****************************************/ 2538 | /*! no static exports found */ 2539 | /***/ (function(module, exports) { 2540 | 2541 | module.exports = "file://" + context.plugin.urlForResourceNamed("_webpack_resources/43aa738f582ce37c61cb81dcdf421b63.html").path(); 2542 | 2543 | /***/ }), 2544 | 2545 | /***/ "./src/actions/resize.js": 2546 | /*!*******************************!*\ 2547 | !*** ./src/actions/resize.js ***! 2548 | \*******************************/ 2549 | /*! exports provided: default */ 2550 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 2551 | 2552 | "use strict"; 2553 | __webpack_require__.r(__webpack_exports__); 2554 | /* harmony import */ var sketch__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! sketch */ "sketch"); 2555 | /* harmony import */ var sketch__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(sketch__WEBPACK_IMPORTED_MODULE_0__); 2556 | // resize logic 2557 | 2558 | var resize = function resize(selection, dimensions) { 2559 | var _iteratorNormalCompletion = true; 2560 | var _didIteratorError = false; 2561 | var _iteratorError = undefined; 2562 | 2563 | try { 2564 | for (var _iterator = selection.layers[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { 2565 | var layer = _step.value; 2566 | var frame = layer.frame; 2567 | var finalDimensions = process(dimensions, frame.width, frame.height); 2568 | frame.width = finalDimensions.width; 2569 | frame.height = finalDimensions.height; 2570 | } 2571 | } catch (err) { 2572 | _didIteratorError = true; 2573 | _iteratorError = err; 2574 | } finally { 2575 | try { 2576 | if (!_iteratorNormalCompletion && _iterator.return != null) { 2577 | _iterator.return(); 2578 | } 2579 | } finally { 2580 | if (_didIteratorError) { 2581 | throw _iteratorError; 2582 | } 2583 | } 2584 | } 2585 | 2586 | sketch__WEBPACK_IMPORTED_MODULE_0___default.a.UI.message("Resized ".concat(selection.length, " layer(s)")); 2587 | }; // process dimensions, and replace %w, and %h 2588 | 2589 | 2590 | var process = function process(dimensions, originalWidth, originalHeight) { 2591 | return { 2592 | width: eval(sanitize(dimensions.width, originalWidth, originalHeight)), 2593 | height: eval(sanitize(dimensions.height, originalWidth, originalHeight)) 2594 | }; 2595 | }; 2596 | 2597 | var sanitize = function sanitize(string, w, h) { 2598 | return string.replace(/%w/gi, w).replace(/%h/gi, h); 2599 | }; 2600 | 2601 | /* harmony default export */ __webpack_exports__["default"] = (resize); 2602 | 2603 | /***/ }), 2604 | 2605 | /***/ "./src/main.js": 2606 | /*!*********************!*\ 2607 | !*** ./src/main.js ***! 2608 | \*********************/ 2609 | /*! exports provided: default */ 2610 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 2611 | 2612 | "use strict"; 2613 | __webpack_require__.r(__webpack_exports__); 2614 | /* harmony import */ var sketch_module_web_view__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! sketch-module-web-view */ "./node_modules/sketch-module-web-view/lib/index.js"); 2615 | /* harmony import */ var sketch_module_web_view__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(sketch_module_web_view__WEBPACK_IMPORTED_MODULE_0__); 2616 | /* harmony import */ var sketch__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! sketch */ "sketch"); 2617 | /* harmony import */ var sketch__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(sketch__WEBPACK_IMPORTED_MODULE_1__); 2618 | /* harmony import */ var _actions_resize__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./actions/resize */ "./src/actions/resize.js"); 2619 | 2620 | 2621 | // main function 2622 | 2623 | var main = function main() { 2624 | var document = sketch__WEBPACK_IMPORTED_MODULE_1___default.a.getSelectedDocument(); 2625 | var selection = document.selectedLayers; // return error message if selection is empty 2626 | 2627 | if (selection.isEmpty) { 2628 | sketch__WEBPACK_IMPORTED_MODULE_1___default.a.UI.message('No layer(s) selected!'); 2629 | return; 2630 | } // define the webview, its options, and its content 2631 | 2632 | 2633 | var options = { 2634 | identifier: 'sketch.resize', 2635 | width: 480, 2636 | height: 316, 2637 | center: true, 2638 | show: false, 2639 | backgroundColor: '#181818', 2640 | titleBarStyle: 'hiddenInset', 2641 | alwaysOnTop: true, 2642 | resizable: false, 2643 | fullscreenable: false, 2644 | maximizable: false, 2645 | minimizable: false 2646 | }; 2647 | var webview = new sketch_module_web_view__WEBPACK_IMPORTED_MODULE_0___default.a(options); 2648 | var webcontent = webview.webContents; // when loaded, show the webview 2649 | 2650 | webview.once('ready-to-show', function () { 2651 | return webview.show(); 2652 | }); // cancel the overall process 2653 | 2654 | webcontent.on('cancel', function () { 2655 | return close(webview); 2656 | }); // process input, and trigger resize 2657 | 2658 | webcontent.on('submit', function (dimensions) { 2659 | Object(_actions_resize__WEBPACK_IMPORTED_MODULE_2__["default"])(selection, dimensions); 2660 | close(webview); 2661 | }); // show the webview 2662 | 2663 | webview.loadURL(__webpack_require__(/*! ../resources/webview/webview.html */ "./resources/webview/webview.html")); 2664 | }; // close the window 2665 | 2666 | 2667 | var close = function close(webview) { 2668 | webview.close(); 2669 | webview = null; 2670 | }; 2671 | 2672 | /* harmony default export */ __webpack_exports__["default"] = (main); 2673 | 2674 | /***/ }), 2675 | 2676 | /***/ "events": 2677 | /*!*************************!*\ 2678 | !*** external "events" ***! 2679 | \*************************/ 2680 | /*! no static exports found */ 2681 | /***/ (function(module, exports) { 2682 | 2683 | module.exports = require("events"); 2684 | 2685 | /***/ }), 2686 | 2687 | /***/ "sketch": 2688 | /*!*************************!*\ 2689 | !*** external "sketch" ***! 2690 | \*************************/ 2691 | /*! no static exports found */ 2692 | /***/ (function(module, exports) { 2693 | 2694 | module.exports = require("sketch"); 2695 | 2696 | /***/ }) 2697 | 2698 | /******/ }); 2699 | if (key === 'default' && typeof exports === 'function') { 2700 | exports(context); 2701 | } else { 2702 | exports[key](context); 2703 | } 2704 | } 2705 | that['onRun'] = __skpm_run.bind(this, 'default') 2706 | 2707 | //# sourceMappingURL=main.js.map -------------------------------------------------------------------------------- /Sketch Resize.sketchplugin/Contents/Sketch/main.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["webpack://exports/webpack/bootstrap","webpack://exports/./node_modules/@skpm/timers/immediate.js","webpack://exports/./node_modules/@skpm/timers/test-if-fiber.js","webpack://exports/./node_modules/@skpm/timers/timeout.js","webpack://exports/./node_modules/cocoascript-class/lib/index.js","webpack://exports/./node_modules/cocoascript-class/lib/runtime.js","webpack://exports/./node_modules/promise-polyfill/lib/index.js","webpack://exports/./node_modules/sketch-module-web-view/lib/browser-api.js","webpack://exports/./node_modules/sketch-module-web-view/lib/constants.js","webpack://exports/./node_modules/sketch-module-web-view/lib/dispatch-first-click.js","webpack://exports/./node_modules/sketch-module-web-view/lib/execute-javascript.js","webpack://exports/./node_modules/sketch-module-web-view/lib/fitSubview.js","webpack://exports/./node_modules/sketch-module-web-view/lib/index.js","webpack://exports/./node_modules/sketch-module-web-view/lib/inject-client-messaging.js","webpack://exports/./node_modules/sketch-module-web-view/lib/movable-area.js","webpack://exports/./node_modules/sketch-module-web-view/lib/parseWebArguments.js","webpack://exports/./node_modules/sketch-module-web-view/lib/set-delegates.js","webpack://exports/./node_modules/sketch-module-web-view/lib/webview-api.js","webpack://exports/./resources/webview/webview.html","webpack://exports/./src/actions/resize.js","webpack://exports/./src/main.js","webpack://exports/external \"events\"","webpack://exports/external \"sketch\""],"names":["resize","selection","dimensions","layers","layer","frame","finalDimensions","process","width","height","sketch","UI","message","length","originalWidth","originalHeight","eval","sanitize","string","w","h","replace","main","document","getSelectedDocument","selectedLayers","isEmpty","options","identifier","center","show","backgroundColor","titleBarStyle","alwaysOnTop","resizable","fullscreenable","maximizable","minimizable","webview","BrowserWindow","webcontent","webContents","once","on","close","loadURL","require"],"mappings":";;;;;;AAAA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;;AAGA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA,kDAA0C,gCAAgC;AAC1E;AACA;;AAEA;AACA;AACA;AACA,gEAAwD,kBAAkB;AAC1E;AACA,yDAAiD,cAAc;AAC/D;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iDAAyC,iCAAiC;AAC1E,wHAAgH,mBAAmB,EAAE;AACrI;AACA;;AAEA;AACA;AACA;AACA,mCAA2B,0BAA0B,EAAE;AACvD,yCAAiC,eAAe;AAChD;AACA;AACA;;AAEA;AACA,8DAAsD,+DAA+D;;AAErH;AACA;;;AAGA;AACA;;;;;;;;;;;;AClFA;AACA,cAAc,mBAAO,CAAC,yDAAW;;AAEjC;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;;;;;;;;;;;;ACdA;AACA;AACA;;;;;;;;;;;;ACFA;AACA,qBAAqB,mBAAO,CAAC,qEAAiB;;AAE9C;AACA;;AAEA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA,yBAAyB;AACzB;AACA;AACA;AACA,yCAAyC,cAAc,IAAI;AAC3D;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;;;;;;;;;;;;AC1Da;;AAEb;AACA;AACA,CAAC;AACD;AACA;;AAEA,eAAe,mBAAO,CAAC,qEAAc;;AAErC;;AAEA;AACA;;AAEA,6EAA6E,YAAY;;AAEzF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA,OAAO;AACP;AACA,KAAK;AACL;AACA;AACA;AACA,GAAG;;AAEH;AACA;;AAEA;AACA;AACA;AACA;AACA,C;;;;;;;;;;;;AC7Da;;AAEb;AACA;AACA,CAAC;AACD;AACA;AACA,kCAAkC,qCAAqC;;AAEvE;AACA;AACA,4CAA4C,sCAAsC,GAAG,YAAY;AACjG;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA,GAAG;;AAEH;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;;AAEA;AACA,iEAAiE;AACjE;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA,oBAAoB,QAAQ,YAAY,WAAW;AACnD;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;;AAEA;AACA,8GAA8G,YAAY,GAAG,YAAY,GAAG,aAAa,IAAI,UAAU,WAAW,GAAG;AACrL;AACA,8GAA8G,YAAY,GAAG,YAAY,GAAG,YAAY,IAAI,UAAU,WAAW,GAAG;;AAEpL;AACA,wCAAwC,gCAAgC,E;;;;;;;;;;;;ACvGxE,gEAAa;;AAEb;AACA,UAAU;AACV;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO;AACP,KAAK;AACL;AACA;AACA;AACA,OAAO;AACP;AACA;AACA;;AAEA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA,WAAW,SAAS;AACpB;AACA;AACA;AACA;AACA;AACA,aAAa,QAAQ;AACrB;AACA,aAAa,SAAS;AACtB;AACA,aAAa,kBAAkB;AAC/B;AACA,aAAa,kBAAkB;AAC/B;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA,GAAG;AACH;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;;AAEA,+CAA+C,SAAS;AACxD;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO;AACP;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,eAAe;AACf;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO;AACP;AACA;AACA;;AAEA,mBAAmB,iBAAiB;AACpC;AACA;AACA,GAAG;AACH;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA,GAAG;AACH;;AAEA;AACA;AACA;AACA,GAAG;AACH;;AAEA;AACA;AACA,wCAAwC,SAAS;AACjD;AACA;AACA,GAAG;AACH;;AAEA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;;AAEA;AACA;AACA,+DAA+D;AAC/D;AACA;;AAEA;;;;;;;;;;;;;AClQA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA,mBAAmB,OAAO;AAC1B;AACA;AACA;AACA,GAAG;AACH;AACA,GAAG;AACH;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA,SAAS;AACT,OAAO;AACP;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA,oCAAoC,+BAA+B;AACnE;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,OAAO,+BAA+B;AACtC;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,OAAO;AACP;AACA,OAAO;AACP;AACA,OAAO;AACP;AACA,OAAO;AACP;AACA,OAAO;AACP;AACA,OAAO;AACP;AACA,OAAO;AACP;AACA;AACA,OAAO;AACP;AACA;AACA;;AAEA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA,oCAAoC,aAAa;AACjD;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA,KAAK;AACL;AACA,KAAK;AACL;AACA,KAAK;AACL;AACA,KAAK;AACL;AACA,KAAK;AACL;AACA,KAAK;AACL;AACA,KAAK;AACL;AACA,KAAK;AACL;AACA,KAAK;AACL;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;;;;;;;;;;;AC5lBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;ACRA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO;AACP;AACA;AACA;AACA;AACA,QAAQ;AACR,gBAAgB;AAChB,MAAM,eAAe;AACrB,0CAA0C,cAAc;AACxD,MAAM;AACN;AACA;;;;;;;;;;;;ACvBA,+DAAgB,mBAAO,CAAC,2EAAa;;AAErC;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT,OAAO;AACP;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,WAAW;AACX;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa;AACb;AACA;AACA,WAAW;AACX;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA,8BAA8B;AAC9B,YAAY;AACZ,gCAAgC;AAChC,wFAAwF;AACxF,qCAAqC;AACrC;AACA;AACA,mBAAmB;AACnB,YAAY;AACZ,kCAAkC;AAClC;AACA;AACA,kBAAkB;AAClB,YAAY;AACZ,UAAU,OAAO;AACjB;AACA;AACA,kBAAkB;AAClB,UAAU;AACV,QAAQ,cAAc;AACtB;AACA;AACA,kBAAkB;AAClB,QAAQ;AACR,MAAM;AACN;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;AC9GA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;;;;;;;;;;;ACrBA;AACA;AACA,mBAAmB,mBAAO,CAAC,sBAAQ;AACnC,sBAAsB,mBAAO,CAAC,+EAAe;AAC7C,kBAAkB,mBAAO,CAAC,+EAAe;AACzC,uBAAuB,mBAAO,CAAC,6EAAc;AAC7C,yBAAyB,mBAAO,CAAC,iGAAwB;AACzD,4BAA4B,mBAAO,CAAC,uGAA2B;AAC/D,kBAAkB,mBAAO,CAAC,iFAAgB;AAC1C,wBAAwB,mBAAO,CAAC,6FAAsB;AACtD,mBAAmB,mBAAO,CAAC,mFAAiB;;AAE5C;AACA;;AAEA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA,yBAAyB;AACzB;AACA,KAAK;AACL;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA,GAAG;;AAEH;;AAEA;AACA;AACA;AACA;AACA,GAAG;;AAEH;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;;;;;;;;;;;;ACzUA,gBAAgB,mBAAO,CAAC,2EAAa;;AAErC;AACA;AACA,qDAAqD;AACrD,gDAAgD;AAChD,uBAAuB;AACvB;AACA,MAAM;AACN;AACA;AACA;AACA;AACA,OAAO;AACP,MAAM;AACN;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;ACxBA,gBAAgB,mBAAO,CAAC,2EAAa;;AAErC;AACA;AACA,mBAAmB;AACnB,4BAA4B;AAC5B,yDAAyD;AACzD;AACA,kCAAkC;AAClC,2CAA2C,4BAA4B,eAAe;AACtF,gCAAgC,yBAAyB,cAAc;AACvE;AACA,OAAO;AACP;AACA,8BAA8B;AAC9B,qDAAqD,SAAS;AAC9D;AACA;AACA,QAAQ;AACR,uDAAuD;AACvD,uDAAuD;AACvD,OAAO;AACP;AACA,4BAA4B;AAC5B;AACA;AACA,QAAQ;AACR,0DAA0D;AAC1D,yCAAyC;AACzC,OAAO;AACP;AACA,6BAA6B;AAC7B;AACA;AACA,QAAQ;AACR,uDAAuD;AACvD,MAAM;AACN,MAAM;AACN;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;;AAEH;AACA;AACA;AACA,GAAG;;AAEH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;;;;;;;;;;;;AC9EA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;;;;;;;;;;;AClBA,gBAAgB,mBAAO,CAAC,wEAAmB;AAC3C,wBAAwB,mBAAO,CAAC,2FAAqB;AACrD,gBAAgB,mBAAO,CAAC,2EAAa;;AAErC;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA,OAAO;;AAEP;AACA;AACA,OAAO;;AAEP;AACA;AACA,OAAO;;AAEP;AACA;AACA,OAAO;;AAEP;AACA;AACA,OAAO;;AAEP;AACA;AACA;AACA,OAAO;;AAEP;AACA;AACA;AACA;AACA;AACA,WAAW;AACX;AACA;AACA,WAAW;AACX,SAAS;AACT;AACA,OAAO;;AAEP;AACA;AACA,OAAO;;AAEP;AACA;AACA,OAAO;;AAEP;AACA;AACA,OAAO;AACP,KAAK;AACL;;AAEA;AACA;AACA;AACA;AACA;AACA,OAAO;AACP;;AAEA;AACA;AACA,2CAA2C;AAC3C,OAAO;;AAEP;AACA;AACA;AACA;AACA,OAAO;;AAEP;AACA;AACA;AACA,OAAO;;AAEP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,eAAe;AACf;AACA;AACA;AACA;AACA,cAAc;AACd;AACA;AACA;AACA;AACA;AACA;AACA,cAAc;AACd;AACA;AACA,UAAU;;AAEV;AACA;AACA;AACA;AACA;AACA,aAAa;;AAEb;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO;;AAEP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO;;AAEP;AACA;AACA;AACA,OAAO;;AAEP;AACA;;AAEA;AACA;AACA,KAAK;AACL;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA,OAAO;AACP,KAAK;AACL;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO;AACP;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA;AACA,OAAO;AACP;AACA;AACA;AACA,KAAK;AACL,GAAG;AACH;AACA;AACA;AACA,GAAG;;AAEH;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO;AACP;AACA;AACA;AACA,KAAK;AACL;AACA,GAAG;;AAEH;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA,OAAO;AACP;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;;;;;;;;;;;ACrSA,mBAAmB,mBAAO,CAAC,sBAAQ;AACnC,wBAAwB,mBAAO,CAAC,6FAAsB;;AAEtD;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,mDAAmD;AACnD;AACA,QAAQ,kCAAkC;AAC1C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA,2BAA2B;AAC3B;AACA;AACA;AACA;AACA;AACA;AACA,QAAQ;AACR;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;;;;;;;;;;;ACrOA,mI;;;;;;;;;;;;ACAA;AAAA;AAAA;CAEA;;AACA,IAAMA,MAAM,GAAG,SAATA,MAAS,CAACC,SAAD,EAAYC,UAAZ,EAA2B;AAAA;AAAA;AAAA;;AAAA;AACxC,yBAAkBD,SAAS,CAACE,MAA5B,8HAAoC;AAAA,UAA3BC,KAA2B;AAClC,UAAMC,KAAK,GAAGD,KAAK,CAACC,KAApB;AACA,UAAMC,eAAe,GAAGC,OAAO,CAACL,UAAD,EAAaG,KAAK,CAACG,KAAnB,EAA0BH,KAAK,CAACI,MAAhC,CAA/B;AACAJ,WAAK,CAACG,KAAN,GAAcF,eAAe,CAACE,KAA9B;AACAH,WAAK,CAACI,MAAN,GAAeH,eAAe,CAACG,MAA/B;AACD;AANuC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAQxCC,+CAAM,CAACC,EAAP,CAAUC,OAAV,mBAA6BX,SAAS,CAACY,MAAvC;AACD,CATD,C,CAWA;;;AACA,IAAMN,OAAO,GAAG,SAAVA,OAAU,CAACL,UAAD,EAAaY,aAAb,EAA4BC,cAA5B;AAAA,SAAgD;AAC9DP,SAAK,EAAEQ,IAAI,CAACC,QAAQ,CAACf,UAAU,CAACM,KAAZ,EAAmBM,aAAnB,EAAkCC,cAAlC,CAAT,CADmD;AAE9DN,UAAM,EAAEO,IAAI,CAACC,QAAQ,CAACf,UAAU,CAACO,MAAZ,EAAoBK,aAApB,EAAmCC,cAAnC,CAAT;AAFkD,GAAhD;AAAA,CAAhB;;AAKA,IAAME,QAAQ,GAAG,SAAXA,QAAW,CAACC,MAAD,EAASC,CAAT,EAAYC,CAAZ;AAAA,SAAkBF,MAAM,CAACG,OAAP,CAAe,MAAf,EAAuBF,CAAvB,EAA0BE,OAA1B,CAAkC,MAAlC,EAA0CD,CAA1C,CAAlB;AAAA,CAAjB;;AAEepB,qEAAf,E;;;;;;;;;;;;ACtBA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;CAGA;;AACA,IAAMsB,IAAI,GAAG,SAAPA,IAAO,GAAM;AACjB,MAAMC,QAAQ,GAAGb,6CAAM,CAACc,mBAAP,EAAjB;AACA,MAAMvB,SAAS,GAAGsB,QAAQ,CAACE,cAA3B,CAFiB,CAIjB;;AACA,MAAIxB,SAAS,CAACyB,OAAd,EAAuB;AACrBhB,iDAAM,CAACC,EAAP,CAAUC,OAAV,CAAkB,uBAAlB;AACA;AACD,GARgB,CAUjB;;;AACA,MAAMe,OAAO,GAAG;AACdC,cAAU,EAAE,eADE;AAEdpB,SAAK,EAAE,GAFO;AAGdC,UAAM,EAAE,GAHM;AAIdoB,UAAM,EAAE,IAJM;AAKdC,QAAI,EAAE,KALQ;AAMdC,mBAAe,EAAE,SANH;AAOdC,iBAAa,EAAE,aAPD;AAQdC,eAAW,EAAE,IARC;AASdC,aAAS,EAAE,KATG;AAUdC,kBAAc,EAAE,KAVF;AAWdC,eAAW,EAAE,KAXC;AAYdC,eAAW,EAAE;AAZC,GAAhB;AAeA,MAAIC,OAAO,GAAG,IAAIC,6DAAJ,CAAkBZ,OAAlB,CAAd;AACA,MAAMa,UAAU,GAAGF,OAAO,CAACG,WAA3B,CA3BiB,CA6BjB;;AACAH,SAAO,CAACI,IAAR,CAAa,eAAb,EAA8B;AAAA,WAAMJ,OAAO,CAACR,IAAR,EAAN;AAAA,GAA9B,EA9BiB,CAgCjB;;AACAU,YAAU,CAACG,EAAX,CAAc,QAAd,EAAwB;AAAA,WAAMC,KAAK,CAACN,OAAD,CAAX;AAAA,GAAxB,EAjCiB,CAmCjB;;AACAE,YAAU,CAACG,EAAX,CAAc,QAAd,EAAwB,UAAAzC,UAAU,EAAI;AACpCF,mEAAM,CAACC,SAAD,EAAYC,UAAZ,CAAN;AACA0C,SAAK,CAACN,OAAD,CAAL;AACD,GAHD,EApCiB,CAyCjB;;AACAA,SAAO,CAACO,OAAR,CAAgBC,mBAAO,CAAC,2EAAD,CAAvB;AACD,CA3CD,C,CA6CA;;;AACA,IAAMF,KAAK,GAAG,SAARA,KAAQ,CAAAN,OAAO,EAAI;AACvBA,SAAO,CAACM,KAAR;AACAN,SAAO,GAAG,IAAV;AACD,CAHD;;AAKehB,mEAAf,E;;;;;;;;;;;ACxDA,mC;;;;;;;;;;;ACAA,mC","file":"main.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = \"./src/main.js\");\n","/* globals coscript, sketch */\nvar timeout = require('./timeout')\n\nfunction setImmediate(func, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10) {\n return timeout.setTimeout(func, 0, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10)\n}\n\nfunction clearImmediate(id) {\n return timeout.clearTimeout(id)\n}\n\nmodule.exports = {\n setImmediate: setImmediate,\n clearImmediate: clearImmediate\n}\n","module.exports = function () {\n return typeof coscript !== 'undefined' && coscript.createFiber\n}\n","/* globals coscript, sketch */\nvar fiberAvailable = require('./test-if-fiber')\n\nvar setTimeout\nvar clearTimeout\n\nvar fibers = []\n\nif (fiberAvailable()) {\n var fibers = []\n\n setTimeout = function (func, delay, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10) {\n // fibers takes care of keeping coscript around\n var id = fibers.length\n fibers.push(coscript.scheduleWithInterval_jsFunction(\n (delay || 0) / 1000,\n function () {\n func(param1, param2, param3, param4, param5, param6, param7, param8, param9, param10)\n }\n ))\n return id\n }\n\n clearTimeout = function (id) {\n var timeout = fibers[id]\n if (timeout) {\n timeout.cancel() // fibers takes care of keeping coscript around\n fibers[id] = undefined // garbage collect the fiber\n }\n }\n} else {\n setTimeout = function (func, delay, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10) {\n coscript.shouldKeepAround = true\n var id = fibers.length\n fibers.push(true)\n coscript.scheduleWithInterval_jsFunction(\n (delay || 0) / 1000,\n function () {\n if (fibers[id]) { // if not cleared\n func(param1, param2, param3, param4, param5, param6, param7, param8, param9, param10)\n }\n clearTimeout(id)\n if (fibers.every(function (_id) { return !_id })) { // if everything is cleared\n coscript.shouldKeepAround = false\n }\n }\n )\n return id\n }\n\n clearTimeout = function (id) {\n fibers[id] = false\n }\n}\n\nmodule.exports = {\n setTimeout: setTimeout,\n clearTimeout: clearTimeout\n}\n","\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.SuperCall = undefined;\nexports.default = ObjCClass;\n\nvar _runtime = require(\"./runtime.js\");\n\nexports.SuperCall = _runtime.SuperCall;\n\n// super when returnType is id and args are void\n// id objc_msgSendSuper(struct objc_super *super, SEL op, void)\n\nconst SuperInit = (0, _runtime.SuperCall)(NSStringFromSelector(\"init\"), [], { type: \"@\" });\n\n// Returns a real ObjC class. No need to use new.\nfunction ObjCClass(defn) {\n const superclass = defn.superclass || NSObject;\n const className = (defn.className || defn.classname || \"ObjCClass\") + NSUUID.UUID().UUIDString();\n const reserved = new Set(['className', 'classname', 'superclass']);\n var cls = MOClassDescription.allocateDescriptionForClassWithName_superclass_(className, superclass);\n // Add each handler to the class description\n const ivars = [];\n for (var key in defn) {\n const v = defn[key];\n if (typeof v == 'function' && key !== 'init') {\n var selector = NSSelectorFromString(key);\n cls.addInstanceMethodWithSelector_function_(selector, v);\n } else if (!reserved.has(key)) {\n ivars.push(key);\n cls.addInstanceVariableWithName_typeEncoding(key, \"@\");\n }\n }\n\n cls.addInstanceMethodWithSelector_function_(NSSelectorFromString('init'), function () {\n const self = SuperInit.call(this);\n ivars.map(name => {\n Object.defineProperty(self, name, {\n get() {\n return getIvar(self, name);\n },\n set(v) {\n (0, _runtime.object_setInstanceVariable)(self, name, v);\n }\n });\n self[name] = defn[name];\n });\n // If there is a passsed-in init funciton, call it now.\n if (typeof defn.init == 'function') defn.init.call(this);\n return self;\n });\n\n return cls.registerClass();\n};\n\nfunction getIvar(obj, name) {\n const retPtr = MOPointer.new();\n (0, _runtime.object_getInstanceVariable)(obj, name, retPtr);\n return retPtr.value().retain().autorelease();\n}","'use strict';\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.SuperCall = SuperCall;\nexports.CFunc = CFunc;\nconst objc_super_typeEncoding = '{objc_super=\"receiver\"@\"super_class\"#}';\n\n// You can store this to call your function. this must be bound to the current instance.\nfunction SuperCall(selector, argTypes, returnType) {\n const func = CFunc(\"objc_msgSendSuper\", [{ type: '^' + objc_super_typeEncoding }, { type: \":\" }, ...argTypes], returnType);\n return function (...args) {\n const struct = make_objc_super(this, this.superclass());\n const structPtr = MOPointer.alloc().initWithValue_(struct);\n return func(structPtr, selector, ...args);\n };\n}\n\n// Recursively create a MOStruct\nfunction makeStruct(def) {\n if (typeof def !== 'object' || Object.keys(def).length == 0) {\n return def;\n }\n const name = Object.keys(def)[0];\n const values = def[name];\n\n const structure = MOStruct.structureWithName_memberNames_runtime(name, Object.keys(values), Mocha.sharedRuntime());\n\n Object.keys(values).map(member => {\n structure[member] = makeStruct(values[member]);\n });\n\n return structure;\n}\n\nfunction make_objc_super(self, cls) {\n return makeStruct({\n objc_super: {\n receiver: self,\n super_class: cls\n }\n });\n}\n\n// Due to particularities of the JS bridge, we can't call into MOBridgeSupport objects directly\n// But, we can ask key value coding to do the dirty work for us ;)\nfunction setKeys(o, d) {\n const funcDict = NSMutableDictionary.dictionary();\n funcDict.o = o;\n Object.keys(d).map(k => funcDict.setValue_forKeyPath(d[k], \"o.\" + k));\n}\n\n// Use any C function, not just ones with BridgeSupport\nfunction CFunc(name, args, retVal) {\n function makeArgument(a) {\n if (!a) return null;\n const arg = MOBridgeSupportArgument.alloc().init();\n setKeys(arg, {\n type64: a.type\n });\n return arg;\n }\n const func = MOBridgeSupportFunction.alloc().init();\n setKeys(func, {\n name: name,\n arguments: args.map(makeArgument),\n returnValue: makeArgument(retVal)\n });\n return func;\n}\n\n/*\n@encode(char*) = \"*\"\n@encode(id) = \"@\"\n@encode(Class) = \"#\"\n@encode(void*) = \"^v\"\n@encode(CGRect) = \"{CGRect={CGPoint=dd}{CGSize=dd}}\"\n@encode(SEL) = \":\"\n*/\n\nfunction addStructToBridgeSupport(key, structDef) {\n // OK, so this is probably the nastiest hack in this file.\n // We go modify MOBridgeSupportController behind its back and use kvc to add our own definition\n // There isn't another API for this though. So the only other way would be to make a real bridgesupport file.\n const symbols = MOBridgeSupportController.sharedController().valueForKey('symbols');\n if (!symbols) throw Error(\"Something has changed within bridge support so we can't add our definitions\");\n // If someone already added this definition, don't re-register it.\n if (symbols[key] !== null) return;\n const def = MOBridgeSupportStruct.alloc().init();\n setKeys(def, {\n name: key,\n type: structDef.type\n });\n symbols[key] = def;\n};\n\n// This assumes the ivar is an object type. Return value is pretty useless.\nconst object_getInstanceVariable = exports.object_getInstanceVariable = CFunc(\"object_getInstanceVariable\", [{ type: \"@\" }, { type: '*' }, { type: \"^@\" }], { type: \"^{objc_ivar=}\" });\n// Again, ivar is of object type\nconst object_setInstanceVariable = exports.object_setInstanceVariable = CFunc(\"object_setInstanceVariable\", [{ type: \"@\" }, { type: '*' }, { type: \"@\" }], { type: \"^{objc_ivar=}\" });\n\n// We need Mocha to understand what an objc_super is so we can use it as a function argument\naddStructToBridgeSupport('objc_super', { type: objc_super_typeEncoding });","'use strict';\n\n/**\n * @this {Promise}\n */\nfunction finallyConstructor(callback) {\n var constructor = this.constructor;\n return this.then(\n function(value) {\n return constructor.resolve(callback()).then(function() {\n return value;\n });\n },\n function(reason) {\n return constructor.resolve(callback()).then(function() {\n return constructor.reject(reason);\n });\n }\n );\n}\n\n// Store setTimeout reference so promise-polyfill will be unaffected by\n// other code modifying setTimeout (like sinon.useFakeTimers())\nvar setTimeoutFunc = setTimeout;\n\nfunction noop() {}\n\n// Polyfill for Function.prototype.bind\nfunction bind(fn, thisArg) {\n return function() {\n fn.apply(thisArg, arguments);\n };\n}\n\n/**\n * @constructor\n * @param {Function} fn\n */\nfunction Promise(fn) {\n if (!(this instanceof Promise))\n throw new TypeError('Promises must be constructed via new');\n if (typeof fn !== 'function') throw new TypeError('not a function');\n /** @type {!number} */\n this._state = 0;\n /** @type {!boolean} */\n this._handled = false;\n /** @type {Promise|undefined} */\n this._value = undefined;\n /** @type {!Array} */\n this._deferreds = [];\n\n doResolve(fn, this);\n}\n\nfunction handle(self, deferred) {\n while (self._state === 3) {\n self = self._value;\n }\n if (self._state === 0) {\n self._deferreds.push(deferred);\n return;\n }\n self._handled = true;\n Promise._immediateFn(function() {\n var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected;\n if (cb === null) {\n (self._state === 1 ? resolve : reject)(deferred.promise, self._value);\n return;\n }\n var ret;\n try {\n ret = cb(self._value);\n } catch (e) {\n reject(deferred.promise, e);\n return;\n }\n resolve(deferred.promise, ret);\n });\n}\n\nfunction resolve(self, newValue) {\n try {\n // Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure\n if (newValue === self)\n throw new TypeError('A promise cannot be resolved with itself.');\n if (\n newValue &&\n (typeof newValue === 'object' || typeof newValue === 'function')\n ) {\n var then = newValue.then;\n if (newValue instanceof Promise) {\n self._state = 3;\n self._value = newValue;\n finale(self);\n return;\n } else if (typeof then === 'function') {\n doResolve(bind(then, newValue), self);\n return;\n }\n }\n self._state = 1;\n self._value = newValue;\n finale(self);\n } catch (e) {\n reject(self, e);\n }\n}\n\nfunction reject(self, newValue) {\n self._state = 2;\n self._value = newValue;\n finale(self);\n}\n\nfunction finale(self) {\n if (self._state === 2 && self._deferreds.length === 0) {\n Promise._immediateFn(function() {\n if (!self._handled) {\n Promise._unhandledRejectionFn(self._value);\n }\n });\n }\n\n for (var i = 0, len = self._deferreds.length; i < len; i++) {\n handle(self, self._deferreds[i]);\n }\n self._deferreds = null;\n}\n\n/**\n * @constructor\n */\nfunction Handler(onFulfilled, onRejected, promise) {\n this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null;\n this.onRejected = typeof onRejected === 'function' ? onRejected : null;\n this.promise = promise;\n}\n\n/**\n * Take a potentially misbehaving resolver function and make sure\n * onFulfilled and onRejected are only called once.\n *\n * Makes no guarantees about asynchrony.\n */\nfunction doResolve(fn, self) {\n var done = false;\n try {\n fn(\n function(value) {\n if (done) return;\n done = true;\n resolve(self, value);\n },\n function(reason) {\n if (done) return;\n done = true;\n reject(self, reason);\n }\n );\n } catch (ex) {\n if (done) return;\n done = true;\n reject(self, ex);\n }\n}\n\nPromise.prototype['catch'] = function(onRejected) {\n return this.then(null, onRejected);\n};\n\nPromise.prototype.then = function(onFulfilled, onRejected) {\n // @ts-ignore\n var prom = new this.constructor(noop);\n\n handle(this, new Handler(onFulfilled, onRejected, prom));\n return prom;\n};\n\nPromise.prototype['finally'] = finallyConstructor;\n\nPromise.all = function(arr) {\n return new Promise(function(resolve, reject) {\n if (!arr || typeof arr.length === 'undefined')\n throw new TypeError('Promise.all accepts an array');\n var args = Array.prototype.slice.call(arr);\n if (args.length === 0) return resolve([]);\n var remaining = args.length;\n\n function res(i, val) {\n try {\n if (val && (typeof val === 'object' || typeof val === 'function')) {\n var then = val.then;\n if (typeof then === 'function') {\n then.call(\n val,\n function(val) {\n res(i, val);\n },\n reject\n );\n return;\n }\n }\n args[i] = val;\n if (--remaining === 0) {\n resolve(args);\n }\n } catch (ex) {\n reject(ex);\n }\n }\n\n for (var i = 0; i < args.length; i++) {\n res(i, args[i]);\n }\n });\n};\n\nPromise.resolve = function(value) {\n if (value && typeof value === 'object' && value.constructor === Promise) {\n return value;\n }\n\n return new Promise(function(resolve) {\n resolve(value);\n });\n};\n\nPromise.reject = function(value) {\n return new Promise(function(resolve, reject) {\n reject(value);\n });\n};\n\nPromise.race = function(values) {\n return new Promise(function(resolve, reject) {\n for (var i = 0, len = values.length; i < len; i++) {\n values[i].then(resolve, reject);\n }\n });\n};\n\n// Use polyfill for setImmediate for performance gains\nPromise._immediateFn =\n (typeof setImmediate === 'function' &&\n function(fn) {\n setImmediate(fn);\n }) ||\n function(fn) {\n setTimeoutFunc(fn, 0);\n };\n\nPromise._unhandledRejectionFn = function _unhandledRejectionFn(err) {\n if (typeof console !== 'undefined' && console) {\n console.warn('Possible Unhandled Promise Rejection:', err); // eslint-disable-line no-console\n }\n};\n\nmodule.exports = Promise;\n","function parseHexColor(color) {\n // Check the string for incorrect formatting.\n if (!color || color[0] !== '#') {\n if (\n color &&\n typeof color.isKindOfClass === 'function' &&\n color.isKindOfClass(NSColor)\n ) {\n return color\n }\n throw new Error(\n 'Incorrect color formating. It should be an hex color: #RRGGBBAA'\n )\n }\n\n // append FF if alpha channel is not specified.\n var source = color.substr(1)\n if (source.length === 3) {\n source += 'F'\n } else if (source.length === 6) {\n source += 'FF'\n }\n // Convert the string from #FFF format to #FFFFFF format.\n var hex\n if (source.length === 4) {\n for (var i = 0; i < 4; i += 1) {\n hex += source[i]\n hex += source[i]\n }\n } else if (source.length === 8) {\n hex = source\n } else {\n return NSColor.whiteColor()\n }\n\n var r = parseInt(hex.slice(0, 2), 16)\n var g = parseInt(hex.slice(2, 4), 16)\n var b = parseInt(hex.slice(4, 6), 16)\n var a = parseInt(hex.slice(6, 8), 16)\n\n return NSColor.colorWithSRGBRed_green_blue_alpha(r, g, b, a)\n}\n\nmodule.exports = function(browserWindow, panel, webview) {\n // keep reference to the subviews\n browserWindow._panel = panel\n browserWindow._webview = webview\n browserWindow._destroyed = false\n\n browserWindow.destroy = function() {\n return panel.close()\n }\n\n browserWindow.close = function() {\n if (panel.delegate().utils.parentWindow) {\n var shouldClose = true\n browserWindow.emit('close', {\n get defaultPrevented() {\n return !shouldClose\n },\n preventDefault: function() {\n shouldClose = false\n },\n })\n if (shouldClose) {\n panel.delegate().utils.parentWindow.endSheet(panel)\n }\n return\n }\n\n if (!browserWindow.isClosable()) {\n return\n }\n\n panel.performClose(null)\n }\n\n function focus(focused) {\n if (browserWindow.isVisible()) {\n return\n }\n if (focused) {\n NSApplication.sharedApplication().activateIgnoringOtherApps(true)\n panel.makeKeyAndOrderFront(null)\n } else {\n panel.orderBack(null)\n }\n }\n\n browserWindow.focus = focus.bind(this, true)\n browserWindow.blur = focus.bind(this, false)\n\n browserWindow.isFocused = function() {\n return panel.isKeyWindow()\n }\n\n browserWindow.isDestroyed = function() {\n return browserWindow._destroyed\n }\n\n browserWindow.show = function() {\n // This method is supposed to put focus on window, however if the app does not\n // have focus then \"makeKeyAndOrderFront\" will only show the window.\n NSApp.activateIgnoringOtherApps(true)\n\n if (panel.delegate().utils.parentWindow) {\n return panel.delegate().utils.parentWindow.beginSheet_completionHandler(\n panel,\n __mocha__.createBlock_function('v16@?0q8', function() {\n browserWindow.emit('closed')\n })\n )\n }\n\n return panel.makeKeyAndOrderFront(null)\n }\n\n browserWindow.showInactive = function() {\n return panel.orderFrontRegardless()\n }\n\n browserWindow.hide = function() {\n return panel.orderOut(null)\n }\n\n browserWindow.isVisible = function() {\n return panel.isVisible()\n }\n\n browserWindow.isModal = function() {\n return false\n }\n\n browserWindow.maximize = function() {\n if (!browserWindow.isMaximized()) {\n panel.zoom(null)\n }\n }\n browserWindow.unmaximize = function() {\n if (browserWindow.isMaximized()) {\n panel.zoom(null)\n }\n }\n\n browserWindow.isMaximized = function() {\n if ((panel.styleMask() & NSResizableWindowMask) !== 0) {\n return panel.isZoomed()\n }\n var rectScreen = NSScreen.mainScreen().visibleFrame()\n var rectWindow = panel.frame()\n return (\n rectScreen.origin.x == rectWindow.origin.x &&\n rectScreen.origin.y == rectWindow.origin.y &&\n rectScreen.size.width == rectWindow.size.width &&\n rectScreen.size.height == rectWindow.size.height\n )\n }\n\n browserWindow.minimize = function() {\n return panel.miniaturize(null)\n }\n\n browserWindow.restore = function() {\n return panel.deminiaturize(null)\n }\n\n browserWindow.isMinimized = function() {\n return panel.isMiniaturized()\n }\n\n browserWindow.setFullScreen = function(fullscreen) {\n if (fullscreen !== browserWindow.isFullscreen()) {\n panel.toggleFullScreen(null)\n }\n }\n\n browserWindow.isFullscreen = function() {\n return panel.styleMask() & NSFullScreenWindowMask\n }\n\n browserWindow.setAspectRatio = function(aspectRatio /* , extraSize */) {\n // Reset the behaviour to default if aspect_ratio is set to 0 or less.\n if (aspectRatio > 0.0) {\n panel.setAspectRatio(NSMakeSize(aspectRatio, 1.0))\n } else {\n panel.setResizeIncrements(NSMakeSize(1.0, 1.0))\n }\n }\n\n browserWindow.setBounds = function(bounds, animate) {\n if (!bounds) {\n return\n }\n\n // Do nothing if in fullscreen mode.\n if (browserWindow.isFullscreen()) {\n return\n }\n\n const newBounds = Object.assign(browserWindow.getBounds(), bounds)\n\n // TODO: Check size constraints since setFrame does not check it.\n // var size = bounds.size\n // size.SetToMax(GetMinimumSize());\n // gfx::Size max_size = GetMaximumSize();\n // if (!max_size.IsEmpty())\n // size.SetToMin(max_size);\n\n var cocoaBounds = NSMakeRect(\n newBounds.x,\n 0,\n newBounds.width,\n newBounds.height\n )\n // Flip Y coordinates based on the primary screen\n var screen = NSScreen.screens().firstObject()\n cocoaBounds.origin.y = NSHeight(screen.frame()) - newBounds.y\n\n panel.setFrame_display_animate(cocoaBounds, true, animate)\n }\n\n browserWindow.getBounds = function() {\n const cocoaBounds = panel.frame()\n var mainScreenRect = NSScreen.screens()\n .firstObject()\n .frame()\n return {\n x: cocoaBounds.origin.x,\n y: Math.round(NSHeight(mainScreenRect) - cocoaBounds.origin.y),\n width: cocoaBounds.size.width,\n height: cocoaBounds.size.height,\n }\n }\n\n browserWindow.setContentBounds = function(bounds, animate) {\n // TODO:\n browserWindow.setBounds(bounds, animate)\n }\n\n browserWindow.getContentBounds = function() {\n // TODO:\n return browserWindow.getBounds()\n }\n\n browserWindow.setSize = function(width, height, animate) {\n // TODO: handle resizing around center\n return browserWindow.setBounds({ width: width, height: height }, animate)\n }\n\n browserWindow.getSize = function() {\n var bounds = browserWindow.getBounds()\n return [bounds.width, bounds.height]\n }\n\n browserWindow.setContentSize = function(width, height, animate) {\n // TODO: handle resizing around center\n return browserWindow.setContentBounds(\n { width: width, height: height },\n animate\n )\n }\n\n browserWindow.getContentSize = function() {\n var bounds = browserWindow.getContentBounds()\n return [bounds.width, bounds.height]\n }\n\n browserWindow.setMinimumSize = function(width, height) {\n const minSize = CGSizeMake(width, height)\n panel.setContentMinSize(minSize)\n }\n\n browserWindow.getMinimumSize = function() {\n const size = panel.contentMinSize()\n return [size.width, size.height]\n }\n\n browserWindow.setMaximumSize = function(width, height) {\n const maxSize = CGSizeMake(width, height)\n panel.setContentMaxSize(maxSize)\n }\n\n browserWindow.getMaximumSize = function() {\n const size = panel.contentMaxSize()\n return [size.width, size.height]\n }\n\n browserWindow.setResizable = function(resizable) {\n return browserWindow._setStyleMask(resizable, NSResizableWindowMask)\n }\n\n browserWindow.isResizable = function() {\n return panel.styleMask() & NSResizableWindowMask\n }\n\n browserWindow.setMovable = function(movable) {\n return panel.setMovable(movable)\n }\n browserWindow.isMovable = function() {\n return panel.isMovable()\n }\n\n browserWindow.setMinimizable = function(minimizable) {\n return browserWindow._setStyleMask(minimizable, NSMiniaturizableWindowMask)\n }\n\n browserWindow.isMinimizable = function() {\n return panel.styleMask() & NSMiniaturizableWindowMask\n }\n\n browserWindow.setMaximizable = function(maximizable) {\n if (panel.standardWindowButton(NSWindowZoomButton)) {\n panel.standardWindowButton(NSWindowZoomButton).setEnabled(maximizable)\n }\n }\n\n browserWindow.isMaximizable = function() {\n return (\n panel.standardWindowButton(NSWindowZoomButton) &&\n panel.standardWindowButton(NSWindowZoomButton).isEnabled()\n )\n }\n\n browserWindow.setFullScreenable = function(fullscreenable) {\n browserWindow._setCollectionBehavior(\n fullscreenable,\n NSWindowCollectionBehaviorFullScreenPrimary\n )\n // On EL Capitan this flag is required to hide fullscreen button.\n browserWindow._setCollectionBehavior(\n !fullscreenable,\n NSWindowCollectionBehaviorFullScreenAuxiliary\n )\n }\n\n browserWindow.isFullScreenable = function() {\n var collectionBehavior = panel.collectionBehavior()\n return collectionBehavior & NSWindowCollectionBehaviorFullScreenPrimary\n }\n\n browserWindow.setClosable = function(closable) {\n browserWindow._setStyleMask(closable, NSClosableWindowMask)\n }\n\n browserWindow.isClosable = function() {\n return panel.styleMask() & NSClosableWindowMask\n }\n\n browserWindow.setAlwaysOnTop = function(top, level, relativeLevel) {\n var windowLevel = NSNormalWindowLevel\n var maxWindowLevel = CGWindowLevelForKey(kCGMaximumWindowLevelKey)\n var minWindowLevel = CGWindowLevelForKey(kCGMinimumWindowLevelKey)\n\n if (top) {\n if (level === 'normal') {\n windowLevel = NSNormalWindowLevel\n } else if (level === 'torn-off-menu') {\n windowLevel = NSTornOffMenuWindowLevel\n } else if (level === 'modal-panel') {\n windowLevel = NSModalPanelWindowLevel\n } else if (level === 'main-menu') {\n windowLevel = NSMainMenuWindowLevel\n } else if (level === 'status') {\n windowLevel = NSStatusWindowLevel\n } else if (level === 'pop-up-menu') {\n windowLevel = NSPopUpMenuWindowLevel\n } else if (level === 'screen-saver') {\n windowLevel = NSScreenSaverWindowLevel\n } else if (level === 'dock') {\n // Deprecated by macOS, but kept for backwards compatibility\n windowLevel = NSDockWindowLevel\n } else {\n windowLevel = NSFloatingWindowLevel\n }\n }\n\n var newLevel = windowLevel + (relativeLevel || 0)\n if (newLevel >= minWindowLevel && newLevel <= maxWindowLevel) {\n panel.setLevel(newLevel)\n } else {\n throw new Error(\n 'relativeLevel must be between ' +\n minWindowLevel +\n ' and ' +\n maxWindowLevel\n )\n }\n }\n\n browserWindow.isAlwaysOnTop = function() {\n return panel.level() !== NSNormalWindowLevel\n }\n\n browserWindow.moveTop = function() {\n return panel.orderFrontRegardless()\n }\n\n browserWindow.center = function() {\n panel.center()\n }\n\n browserWindow.setPosition = function(x, y, animate) {\n return browserWindow.setBounds({ x: x, y: y }, animate)\n }\n\n browserWindow.getPosition = function() {\n var bounds = browserWindow.getBounds()\n return [bounds.x, bounds.y]\n }\n\n browserWindow.setTitle = function(title) {\n panel.setTitle(title)\n }\n\n browserWindow.getTitle = function() {\n return String(panel.title())\n }\n\n var attentionRequestId = 0\n browserWindow.flashFrame = function(flash) {\n if (flash) {\n attentionRequestId = NSApp.requestUserAttention(NSInformationalRequest)\n } else {\n NSApp.cancelUserAttentionRequest(attentionRequestId)\n attentionRequestId = 0\n }\n }\n\n browserWindow.getNativeWindowHandle = function() {\n return panel\n }\n\n browserWindow.getNativeWebViewHandle = function() {\n return webview\n }\n\n browserWindow.loadURL = function(url) {\n // When frameLocation is a file, prefix it with the Sketch Resources path\n if (/^(?!https?|file).*\\.html?$/.test(url)) {\n if (typeof __command !== 'undefined' && __command.pluginBundle()) {\n url =\n 'file://' +\n __command\n .pluginBundle()\n .urlForResourceNamed(url)\n .path()\n }\n }\n\n if (/^file:\\/\\/.*\\.html?$/.test(url)) {\n webview.loadFileURL_allowingReadAccessToURL(\n NSURL.fileURLWithPath(url),\n NSURL.fileURLWithPath('file:///')\n )\n return\n }\n\n const properURL = NSURL.URLWithString(url)\n const urlRequest = NSURLRequest.requestWithURL(properURL)\n\n webview.loadRequest(urlRequest)\n }\n\n browserWindow.reload = function() {\n webview.reload()\n }\n\n browserWindow.setHasShadow = function(hasShadow) {\n return panel.setHasShadow(hasShadow)\n }\n\n browserWindow.hasShadow = function() {\n return panel.hasShadow()\n }\n\n browserWindow.setOpacity = function(opacity) {\n return panel.setAlphaValue(opacity)\n }\n\n browserWindow.getOpacity = function() {\n return panel.alphaValue()\n }\n\n browserWindow.setVisibleOnAllWorkspaces = function(visible) {\n return browserWindow._setCollectionBehavior(\n visible,\n NSWindowCollectionBehaviorCanJoinAllSpaces\n )\n }\n\n browserWindow.isVisibleOnAllWorkspaces = function() {\n var collectionBehavior = panel.collectionBehavior()\n return collectionBehavior & NSWindowCollectionBehaviorCanJoinAllSpaces\n }\n\n browserWindow.setIgnoreMouseEvents = function(ignore) {\n return panel.setIgnoresMouseEvents(ignore)\n }\n\n browserWindow.setContentProtection = function(enable) {\n panel.setSharingType(enable ? NSWindowSharingNone : NSWindowSharingReadOnly)\n }\n\n browserWindow.setAutoHideCursor = function(autoHide) {\n panel.setDisableAutoHideCursor(autoHide)\n }\n\n browserWindow.setVibrancy = function(type) {\n var effectView = browserWindow._vibrantView\n\n if (!type) {\n if (effectView == null) {\n return\n }\n\n effectView.removeFromSuperview()\n panel.setVibrantView(null)\n return\n }\n\n if (effectView == null) {\n var contentView = panel.contentView()\n effectView = NSVisualEffectView.alloc().initWithFrame(\n contentView.bounds()\n )\n browserWindow._vibrantView = effectView\n\n effectView.setAutoresizingMask(NSViewWidthSizable | NSViewHeightSizable)\n effectView.setBlendingMode(NSVisualEffectBlendingModeBehindWindow)\n effectView.setState(NSVisualEffectStateActive)\n effectView.setFrame(contentView.bounds())\n contentView.addSubview_positioned_relativeTo(\n effectView,\n NSWindowBelow,\n null\n )\n }\n\n var vibrancyType = NSVisualEffectMaterialLight\n\n if (type === 'appearance-based') {\n vibrancyType = NSVisualEffectMaterialAppearanceBased\n } else if (type === 'light') {\n vibrancyType = NSVisualEffectMaterialLight\n } else if (type === 'dark') {\n vibrancyType = NSVisualEffectMaterialDark\n } else if (type === 'titlebar') {\n vibrancyType = NSVisualEffectMaterialTitlebar\n } else if (type === 'selection') {\n vibrancyType = NSVisualEffectMaterialSelection\n } else if (type === 'menu') {\n vibrancyType = NSVisualEffectMaterialMenu\n } else if (type === 'popover') {\n vibrancyType = NSVisualEffectMaterialPopover\n } else if (type === 'sidebar') {\n vibrancyType = NSVisualEffectMaterialSidebar\n } else if (type === 'medium-light') {\n vibrancyType = NSVisualEffectMaterialMediumLight\n } else if (type === 'ultra-dark') {\n vibrancyType = NSVisualEffectMaterialUltraDark\n }\n\n effectView.setMaterial(vibrancyType)\n }\n\n browserWindow._setBackgroundColor = function(colorName) {\n var color = parseHexColor(colorName)\n webview.setValue_forKey(false, 'drawsBackground')\n panel.backgroundColor = color\n }\n\n browserWindow._invalidate = function() {\n panel.flushWindow()\n panel.contentView().setNeedsDisplay(true)\n }\n\n browserWindow._setStyleMask = function(on, flag) {\n var wasMaximizable = browserWindow.isMaximizable()\n if (on) {\n panel.setStyleMask(panel.styleMask() | flag)\n } else {\n panel.setStyleMask(panel.styleMask() & ~flag)\n }\n // Change style mask will make the zoom button revert to default, probably\n // a bug of Cocoa or macOS.\n browserWindow.setMaximizable(wasMaximizable)\n }\n\n browserWindow._setCollectionBehavior = function(on, flag) {\n var wasMaximizable = browserWindow.isMaximizable()\n if (on) {\n panel.setCollectionBehavior(panel.collectionBehavior() | flag)\n } else {\n panel.setCollectionBehavior(panel.collectionBehavior() & ~flag)\n }\n // Change collectionBehavior will make the zoom button revert to default,\n // probably a bug of Cocoa or macOS.\n browserWindow.setMaximizable(wasMaximizable)\n }\n\n browserWindow._showWindowButton = function(button) {\n var view = panel.standardWindowButton(button)\n view.superview().addSubview_positioned_relative(view, NSWindowAbove, null)\n }\n}\n","module.exports = {\n JS_BRIDGE: '__skpm_sketchBridge',\n START_MOVING_WINDOW: '__skpm_startMovingWindow',\n STOP_MOVING_WINDOW: '__skpm_stopMovingWindow',\n MOVE_WINDOW: '__skpm_moveWindow',\n EXECUTE_JAVASCRIPT: '__skpm_executeJS',\n EXECUTE_JAVASCRIPT_SUCCESS: '__skpm_executeJS_success_',\n EXECUTE_JAVASCRIPT_ERROR: '__skpm_executeJS_error_',\n}\n","var tagsToFocus =\n '[\"text\", \"textarea\", \"date\", \"datetime-local\", \"email\", \"number\", \"month\", \"password\", \"search\", \"tel\", \"time\", \"url\", \"week\" ]'\n\nmodule.exports = function(webView, event) {\n var point = webView.convertPoint_fromView(event.locationInWindow(), null)\n var x = point.x\n var y = webView.frame().size.height - point.y // the coord start from the bottom instead of the top\n return (\n 'var el = document.elementFromPoint(' + // get the DOM element that match the event\n x +\n ', ' +\n y +\n '); ' +\n 'if (el && ' + // some tags need to be focused instead of clicked\n tagsToFocus +\n '.indexOf(el.type) >= 0 && ' +\n 'el.focus' +\n ') {' +\n 'el.focus();' + // so focus them\n '} else if (el) {' +\n 'el.dispatchEvent(new Event(\"click\", {bubbles: true}))' + // click the others\n '}'\n )\n}\n","var CONSTANTS = require('./constants')\n\nmodule.exports = function(webview, browserWindow) {\n function executeJavaScript(script, userGesture, callback) {\n if (typeof userGesture === 'function') {\n callback = userGesture\n userGesture = false\n }\n var fiber = coscript.createFiber()\n\n // if the webview is not ready yet, defer the execution until it is\n if (webview.navigationDelegate().valueForKey('state').wasReady == 0) {\n return new Promise(function(resolve, reject) {\n browserWindow.once('ready-to-show', function() {\n executeJavaScript(script, userGesture, callback)\n .then(resolve)\n .catch(reject)\n fiber.cleanup()\n })\n })\n }\n\n return new Promise(function(resolve, reject) {\n var requestId = Math.random()\n\n browserWindow.webContents.on(\n CONSTANTS.EXECUTE_JAVASCRIPT_SUCCESS + requestId,\n function(res) {\n try {\n if (callback) {\n callback(null, res)\n }\n resolve(res)\n } catch (_) {\n // /shrug\n }\n fiber.cleanup()\n }\n )\n browserWindow.webContents.on(\n CONSTANTS.EXECUTE_JAVASCRIPT_ERROR + requestId,\n function(err) {\n try {\n if (callback) {\n callback(err)\n resolve()\n } else {\n reject(err)\n }\n } catch (_) {\n // /shrug\n }\n fiber.cleanup()\n }\n )\n\n webview.evaluateJavaScript_completionHandler(\n 'window.' +\n CONSTANTS.EXECUTE_JAVASCRIPT +\n '(' +\n requestId +\n ', \"' +\n script.replace(/\"/g, '\\\\\"') +\n '\")',\n null\n )\n })\n }\n\n return executeJavaScript\n}\n\nmodule.exports.injectScript = function(webView) {\n var source =\n 'window.' +\n CONSTANTS.EXECUTE_JAVASCRIPT +\n ' = function(id, script) {' +\n ' try {' +\n ' var res = eval(script);' +\n ' if (res && typeof res.then === \"function\" && typeof res.catch === \"function\") {' +\n ' res.then(function (res2) {' +\n ' window.postMessage(\"' +\n CONSTANTS.EXECUTE_JAVASCRIPT_SUCCESS +\n '\" + id, res2);' +\n ' })' +\n ' .catch(function (err) {' +\n ' window.postMessage(\"' +\n CONSTANTS.EXECUTE_JAVASCRIPT_ERROR +\n '\" + id, err);' +\n ' })' +\n ' } else {' +\n ' window.postMessage(\"' +\n CONSTANTS.EXECUTE_JAVASCRIPT_SUCCESS +\n '\" + id, res);' +\n ' }' +\n ' } catch (err) {' +\n ' window.postMessage(\"' +\n CONSTANTS.EXECUTE_JAVASCRIPT_ERROR +\n '\" + id, err);' +\n ' }' +\n '}'\n var script = WKUserScript.alloc().initWithSource_injectionTime_forMainFrameOnly(\n source,\n 0,\n true\n )\n webView\n .configuration()\n .userContentController()\n .addUserScript(script)\n}\n","function addEdgeConstraint(edge, subview, view, constant) {\n view.addConstraint(\n NSLayoutConstraint.constraintWithItem_attribute_relatedBy_toItem_attribute_multiplier_constant(\n subview,\n edge,\n NSLayoutRelationEqual,\n view,\n edge,\n 1,\n constant\n )\n )\n}\nmodule.exports = function fitSubviewToView(subview, view, constants) {\n constants = constants || []\n subview.setTranslatesAutoresizingMaskIntoConstraints(false)\n\n addEdgeConstraint(NSLayoutAttributeLeft, subview, view, constants[0] || 0)\n addEdgeConstraint(NSLayoutAttributeTop, subview, view, constants[1] || 0)\n addEdgeConstraint(NSLayoutAttributeRight, subview, view, constants[2] || 0)\n addEdgeConstraint(NSLayoutAttributeBottom, subview, view, constants[3] || 0)\n}\n","/* let's try to match the API from Electron's Browser window\n(https://github.com/electron/electron/blob/master/docs/api/browser-window.md) */\nvar EventEmitter = require('events')\nvar buildBrowserAPI = require('./browser-api')\nvar buildWebAPI = require('./webview-api')\nvar fitSubviewToView = require('./fitSubview')\nvar dispatchFirstClick = require('./dispatch-first-click')\nvar injectClientMessaging = require('./inject-client-messaging')\nvar movableArea = require('./movable-area')\nvar executeJavaScript = require('./execute-javascript')\nvar setDelegates = require('./set-delegates')\n\nfunction BrowserWindow(options) {\n options = options || {}\n\n var identifier = options.identifier || NSUUID.UUID().UUIDString()\n var threadDictionary = NSThread.mainThread().threadDictionary()\n\n var existingBrowserWindow = BrowserWindow.fromId(identifier)\n\n // if we already have a window opened, reuse it\n if (existingBrowserWindow) {\n return existingBrowserWindow\n }\n\n var browserWindow = new EventEmitter()\n browserWindow.id = identifier\n\n if (options.modal && !options.parent) {\n throw new Error('A modal needs to have a parent.')\n }\n\n // Long-running script\n var fiber = coscript.createFiber()\n\n // Window size\n var width = options.width || 800\n var height = options.height || 600\n var mainScreenRect = NSScreen.screens()\n .firstObject()\n .frame()\n var cocoaBounds = NSMakeRect(\n typeof options.x !== 'undefined'\n ? options.x\n : Math.round((NSWidth(mainScreenRect) - width) / 2),\n typeof options.y !== 'undefined'\n ? NSHeight(mainScreenRect) - options.y\n : Math.round((NSHeight(mainScreenRect) - height) / 2),\n width,\n height\n )\n\n if (options.titleBarStyle && options.titleBarStyle !== 'default') {\n options.frame = false\n }\n\n var useStandardWindow = options.windowType !== 'textured'\n var styleMask = NSTitledWindowMask\n\n // this is commented out because the toolbar doesn't appear otherwise :thinking-face:\n // if (!useStandardWindow || options.frame === false) {\n // styleMask = NSFullSizeContentViewWindowMask\n // }\n if (options.minimizable !== false) {\n styleMask |= NSMiniaturizableWindowMask\n }\n if (options.closable !== false) {\n styleMask |= NSClosableWindowMask\n }\n if (options.resizable !== false) {\n styleMask |= NSResizableWindowMask\n }\n if (!useStandardWindow || options.transparent || options.frame === false) {\n styleMask |= NSTexturedBackgroundWindowMask\n }\n\n var panel = NSPanel.alloc().initWithContentRect_styleMask_backing_defer(\n cocoaBounds,\n styleMask,\n NSBackingStoreBuffered,\n true\n )\n\n var wkwebviewConfig = WKWebViewConfiguration.alloc().init()\n var webView = WKWebView.alloc().initWithFrame_configuration(\n CGRectMake(0, 0, options.width || 800, options.height || 600),\n wkwebviewConfig\n )\n injectClientMessaging(webView)\n webView.setAutoresizingMask(NSViewWidthSizable | NSViewHeightSizable)\n\n buildBrowserAPI(browserWindow, panel, webView)\n buildWebAPI(browserWindow, panel, webView)\n setDelegates(browserWindow, panel, webView, options)\n\n if (options.windowType === 'desktop') {\n panel.setLevel(kCGDesktopWindowLevel - 1)\n // panel.setCanBecomeKeyWindow(false)\n panel.setCollectionBehavior(\n NSWindowCollectionBehaviorCanJoinAllSpaces |\n NSWindowCollectionBehaviorStationary |\n NSWindowCollectionBehaviorIgnoresCycle\n )\n }\n\n if (\n typeof options.minWidth !== 'undefined' ||\n typeof options.minHeight !== 'undefined'\n ) {\n browserWindow.setMinimumSize(options.minWidth || 0, options.minHeight || 0)\n }\n\n if (\n typeof options.maxWidth !== 'undefined' ||\n typeof options.maxHeight !== 'undefined'\n ) {\n browserWindow.setMaximumSize(\n options.maxWidth || 10000,\n options.maxHeight || 10000\n )\n }\n\n // if (options.focusable === false) {\n // panel.setCanBecomeKeyWindow(false)\n // }\n\n if (options.transparent || options.frame === false) {\n panel.titlebarAppearsTransparent = true\n panel.titleVisibility = NSWindowTitleHidden\n panel.setOpaque(0)\n panel.isMovableByWindowBackground = true\n var toolbar2 = NSToolbar.alloc().initWithIdentifier(\n 'titlebarStylingToolbar'\n )\n toolbar2.setShowsBaselineSeparator(false)\n panel.setToolbar(toolbar2)\n }\n\n if (options.titleBarStyle === 'hiddenInset') {\n var toolbar = NSToolbar.alloc().initWithIdentifier('titlebarStylingToolbar')\n toolbar.setShowsBaselineSeparator(false)\n panel.setToolbar(toolbar)\n }\n\n if (options.frame === false || !options.useContentSize) {\n browserWindow.setSize(width, height)\n }\n\n if (options.center) {\n browserWindow.center()\n }\n\n if (options.alwaysOnTop) {\n browserWindow.setAlwaysOnTop(true)\n }\n\n if (options.fullscreen) {\n browserWindow.setFullScreen(true)\n }\n browserWindow.setFullScreenable(!!options.fullscreenable)\n\n const title =\n options.title ||\n (typeof __command !== 'undefined' && __command.pluginBundle()\n ? __command.pluginBundle().name()\n : undefined)\n if (title) {\n browserWindow.setTitle(title)\n }\n\n var backgroundColor = options.backgroundColor\n if (options.transparent) {\n backgroundColor = NSColor.clearColor()\n }\n if (!backgroundColor && options.frame === false && options.vibrancy) {\n backgroundColor = NSColor.clearColor()\n }\n\n browserWindow._setBackgroundColor(\n backgroundColor || NSColor.windowBackgroundColor()\n )\n\n if (options.hasShadow === false) {\n browserWindow.setHasShadow(false)\n }\n\n if (typeof options.opacity !== 'undefined') {\n browserWindow.setOpacity(options.opacity)\n }\n\n options.webPreferences = options.webPreferences || {}\n\n webView\n .configuration()\n .preferences()\n .setValue_forKey(\n options.webPreferences.devTools !== false,\n 'developerExtrasEnabled'\n )\n webView\n .configuration()\n .preferences()\n .setValue_forKey(\n options.webPreferences.javascript !== false,\n 'javaScriptEnabled'\n )\n webView\n .configuration()\n .preferences()\n .setValue_forKey(!!options.webPreferences.plugins, 'plugInsEnabled')\n webView\n .configuration()\n .preferences()\n .setValue_forKey(\n options.webPreferences.minimumFontSize || 0,\n 'minimumFontSize'\n )\n\n if (options.webPreferences.zoomFactor) {\n webView.setMagnification(options.webPreferences.zoomFactor)\n }\n\n var contentView = panel.contentView()\n\n if (options.frame !== false) {\n webView.setFrame(contentView.bounds())\n contentView.addSubview(webView)\n } else {\n // In OSX 10.10, adding subviews to the root view for the NSView hierarchy\n // produces warnings. To eliminate the warnings, we resize the contentView\n // to fill the window, and add subviews to that.\n // http://crbug.com/380412\n contentView.setAutoresizingMask(NSViewWidthSizable | NSViewHeightSizable)\n fitSubviewToView(contentView, contentView.superview())\n\n webView.setFrame(contentView.bounds())\n contentView.addSubview(webView)\n\n // The fullscreen button should always be hidden for frameless window.\n if (panel.standardWindowButton(NSWindowFullScreenButton)) {\n panel.standardWindowButton(NSWindowFullScreenButton).setHidden(true)\n }\n\n if (!options.titleBarStyle || options.titleBarStyle === 'default') {\n // Hide the window buttons.\n panel.standardWindowButton(NSWindowZoomButton).setHidden(true)\n panel.standardWindowButton(NSWindowMiniaturizeButton).setHidden(true)\n panel.standardWindowButton(NSWindowCloseButton).setHidden(true)\n\n // Some third-party macOS utilities check the zoom button's enabled state to\n // determine whether to show custom UI on hover, so we disable it here to\n // prevent them from doing so in a frameless app window.\n panel.standardWindowButton(NSWindowZoomButton).setEnabled(false)\n }\n }\n\n if (options.vibrancy) {\n browserWindow.setVibrancy(options.vibrancy)\n }\n\n // Set maximizable state last to ensure zoom button does not get reset\n // by calls to other APIs.\n browserWindow.setMaximizable(options.maximizable !== false)\n\n if (options.acceptsFirstMouse) {\n browserWindow.on('focus', function(event) {\n if (event.type() === NSEventTypeLeftMouseDown) {\n browserWindow.webContents\n .executeJavaScript(dispatchFirstClick(webView, event))\n .catch(() => {})\n }\n })\n }\n\n executeJavaScript.injectScript(webView)\n movableArea.injectScript(webView)\n movableArea.setupHandler(browserWindow)\n\n if (options.show !== false) {\n browserWindow.show()\n }\n\n browserWindow.on('closed', function() {\n browserWindow._destroyed = true\n threadDictionary.removeObjectForKey(identifier)\n fiber.cleanup()\n })\n\n threadDictionary[identifier] = panel\n\n fiber.onCleanup(function() {\n if (!browserWindow._destroyed) {\n browserWindow.destroy()\n }\n })\n\n return browserWindow\n}\n\nBrowserWindow.fromId = function(identifier) {\n var threadDictionary = NSThread.mainThread().threadDictionary()\n\n if (threadDictionary[identifier]) {\n return BrowserWindow.fromPanel(threadDictionary[identifier], identifier)\n }\n\n return undefined\n}\n\nBrowserWindow.fromPanel = function(panel, identifier) {\n var browserWindow = new EventEmitter()\n browserWindow.id = identifier\n\n if (!panel || !panel.contentView) {\n throw new Error('needs to pass an NSPanel')\n }\n\n var webView = panel.contentView().subviews()[0]\n\n if (!webView) {\n throw new Error('The NSPanel needs to have a webview')\n }\n\n buildBrowserAPI(browserWindow, panel, webView)\n buildWebAPI(browserWindow, panel, webView)\n\n return browserWindow\n}\n\nmodule.exports = BrowserWindow\n","var CONSTANTS = require('./constants')\n\nmodule.exports = function(webView) {\n var source =\n 'window.originalPostMessage = window.postMessage;' +\n 'window.postMessage = function(actionName) {' +\n 'if (!actionName) {' +\n \"throw new Error('missing action name')\" +\n '}' +\n 'window.webkit.messageHandlers.' +\n CONSTANTS.JS_BRIDGE +\n '.postMessage(' +\n 'JSON.stringify([].slice.call(arguments))' +\n ');' +\n '}'\n var script = WKUserScript.alloc().initWithSource_injectionTime_forMainFrameOnly(\n source,\n 0,\n true\n )\n webView\n .configuration()\n .userContentController()\n .addUserScript(script)\n}\n","var CONSTANTS = require('./constants')\n\nmodule.exports.injectScript = function(webView) {\n var source =\n '(function () {' +\n 'var animationId = null;' +\n \"document.addEventListener('mousedown', onMouseDown);\" +\n '' +\n 'function shouldDrag(target) {' +\n ' if (!target || (target.dataset || {}).appRegion === \"no-drag\") { return false }' +\n ' if ((target.dataset || {}).appRegion === \"drag\") { return true }' +\n ' return shouldDrag(target.parentElement)' +\n '};' +\n '' +\n 'function onMouseDown(e) {' +\n ' if (e.button !== 0 || !shouldDrag(e.target)) { return }' +\n ' window.postMessage(\"' +\n CONSTANTS.START_MOVING_WINDOW +\n '\");' +\n \" document.addEventListener('mouseup', onMouseUp);\" +\n ' animationId = requestAnimationFrame(moveWindow);' +\n '};' +\n '' +\n 'function onMouseUp(e) {' +\n ' window.postMessage(\"' +\n CONSTANTS.STOP_MOVING_WINDOW +\n '\");' +\n \" document.removeEventListener('mouseup', onMouseUp);\" +\n ' cancelAnimationFrame(animationId);' +\n '};' +\n '' +\n 'function moveWindow(e) {' +\n ' window.postMessage(\"' +\n CONSTANTS.MOVE_WINDOW +\n '\");' +\n ' animationId = requestAnimationFrame(moveWindow);' +\n '}' +\n '})()'\n var script = WKUserScript.alloc().initWithSource_injectionTime_forMainFrameOnly(\n source,\n 0,\n true\n )\n webView\n .configuration()\n .userContentController()\n .addUserScript(script)\n}\n\nmodule.exports.setupHandler = function(browserWindow) {\n var initialMouseLocation = null\n var initialWindowPosition = null\n\n browserWindow.webContents.on(CONSTANTS.START_MOVING_WINDOW, function() {\n initialMouseLocation = NSEvent.mouseLocation()\n var position = browserWindow.getPosition()\n initialWindowPosition = {\n x: position[0],\n y: position[1],\n }\n })\n\n browserWindow.webContents.on(CONSTANTS.STOP_MOVING_WINDOW, function() {\n initialMouseLocation = null\n initialWindowPosition = null\n })\n\n browserWindow.webContents.on(CONSTANTS.MOVE_WINDOW, function() {\n if (!initialWindowPosition) {\n return\n }\n const mouse = NSEvent.mouseLocation()\n browserWindow.setPosition(\n initialWindowPosition.x + (mouse.x - initialMouseLocation.x),\n initialWindowPosition.y + (initialMouseLocation.y - mouse.y), // y is inverted\n false\n )\n })\n}\n","module.exports = function(webArguments) {\n var args = null\n try {\n args = JSON.parse(webArguments)\n } catch (e) {\n // malformed arguments\n }\n\n if (\n !args ||\n !args.constructor ||\n args.constructor !== Array ||\n args.length == 0\n ) {\n return null\n }\n\n return args\n}\n","var ObjCClass = require('cocoascript-class').default\nvar parseWebArguments = require('./parseWebArguments')\nvar CONSTANTS = require('./constants')\n\n// We create one ObjC class for ourselves here\nvar WindowDelegateClass\nvar NavigationDelegateClass\nvar WebScriptHandlerClass\n\n// TODO: events\n// - 'page-favicon-updated'\n// - 'new-window'\n// - 'did-navigate-in-page'\n// - 'will-prevent-unload'\n// - 'crashed'\n// - 'unresponsive'\n// - 'responsive'\n// - 'destroyed'\n// - 'before-input-event'\n// - 'certificate-error'\n// - 'found-in-page'\n// - 'media-started-playing'\n// - 'media-paused'\n// - 'did-change-theme-color'\n// - 'update-target-url'\n// - 'cursor-changed'\n// - 'context-menu'\n// - 'select-bluetooth-device'\n// - 'paint'\n// - 'console-message'\n\nmodule.exports = function(browserWindow, panel, webview, options) {\n if (!WindowDelegateClass) {\n WindowDelegateClass = ObjCClass({\n classname: 'WindowDelegateClass',\n utils: null,\n panel: null,\n\n 'windowDidResize:': function() {\n this.utils.emit('resize')\n },\n\n 'windowDidMiniaturize:': function() {\n this.utils.emit('minimize')\n },\n\n 'windowDidDeminiaturize:': function() {\n this.utils.emit('restore')\n },\n\n 'windowDidEnterFullScreen:': function() {\n this.utils.emit('enter-full-screen')\n },\n\n 'windowDidExitFullScreen:': function() {\n this.utils.emit('leave-full-screen')\n },\n\n 'windowDidMove:': function() {\n this.utils.emit('move')\n this.utils.emit('moved')\n },\n\n 'windowShouldClose:': function() {\n var shouldClose = true\n this.utils.emit('close', {\n get defaultPrevented() {\n return !shouldClose\n },\n preventDefault: function() {\n shouldClose = false\n },\n })\n return shouldClose\n },\n\n 'windowWillClose:': function() {\n this.utils.emit('closed')\n },\n\n 'windowDidBecomeKey:': function() {\n this.utils.emit('focus', this.panel.currentEvent())\n },\n\n 'windowDidResignKey:': function() {\n this.utils.emit('blur')\n },\n })\n }\n\n if (!NavigationDelegateClass) {\n NavigationDelegateClass = ObjCClass({\n classname: 'NavigationDelegateClass',\n state: NSMutableDictionary.dictionaryWithDictionary({\n wasReady: 0,\n }),\n utils: null,\n\n // // Called when the web view begins to receive web content.\n 'webView:didCommitNavigation:': function(webView) {\n this.utils.emit('will-navigate', {}, String(String(webView.URL())))\n },\n\n // // Called when web content begins to load in a web view.\n 'webView:didStartProvisionalNavigation:': function() {\n this.utils.emit('did-start-navigation')\n this.utils.emit('did-start-loading')\n },\n\n // Called when a web view receives a server redirect.\n 'webView:didReceiveServerRedirectForProvisionalNavigation:': function() {\n this.utils.emit('did-get-redirect-request')\n },\n\n // // Called when the web view needs to respond to an authentication challenge.\n // 'webView:didReceiveAuthenticationChallenge:completionHandler:': function(\n // webView,\n // challenge,\n // completionHandler\n // ) {\n // function callback(username, password) {\n // completionHandler(\n // 0,\n // NSURLCredential.credentialWithUser_password_persistence(\n // username,\n // password,\n // 1\n // )\n // )\n // }\n // var protectionSpace = challenge.protectionSpace()\n // this.utils.emit(\n // 'login',\n // {},\n // {\n // method: String(protectionSpace.authenticationMethod()),\n // url: 'not implemented', // TODO:\n // referrer: 'not implemented', // TODO:\n // },\n // {\n // isProxy: !!protectionSpace.isProxy(),\n // scheme: String(protectionSpace.protocol()),\n // host: String(protectionSpace.host()),\n // port: Number(protectionSpace.port()),\n // realm: String(protectionSpace.realm()),\n // },\n // callback\n // )\n // },\n\n // Called when an error occurs during navigation.\n // 'webView:didFailNavigation:withError:': function(\n // webView,\n // navigation,\n // error\n // ) {},\n\n // Called when an error occurs while the web view is loading content.\n 'webView:didFailProvisionalNavigation:withError:': function(\n webView,\n navigation,\n error\n ) {\n this.utils.emit('did-fail-load', error)\n },\n\n // Called when the navigation is complete.\n 'webView:didFinishNavigation:': function() {\n if (this.state.wasReady == 0) {\n this.state.setObject_forKey(1, 'wasReady')\n this.utils.emitBrowserEvent('ready-to-show')\n }\n this.utils.emit('did-navigate')\n this.utils.emit('did-frame-navigate')\n this.utils.emit('did-stop-loading')\n this.utils.emit('did-finish-load')\n this.utils.emit('did-frame-finish-load')\n },\n\n // Called when the web view’s web content process is terminated.\n 'webViewWebContentProcessDidTerminate:': function() {\n this.utils.emit('dom-ready')\n },\n\n // Decides whether to allow or cancel a navigation.\n // webView:decidePolicyForNavigationAction:decisionHandler:\n\n // Decides whether to allow or cancel a navigation after its response is known.\n // webView:decidePolicyForNavigationResponse:decisionHandler:\n })\n }\n\n if (!WebScriptHandlerClass) {\n WebScriptHandlerClass = ObjCClass({\n classname: 'WebScriptHandlerClass',\n utils: null,\n 'userContentController:didReceiveScriptMessage:': function(_, message) {\n var args = this.utils.parseWebArguments(String(message.body()))\n if (!args) {\n return\n }\n if (!args[0] || typeof args[0] !== 'string') {\n return\n }\n args[0] = String(args[0])\n\n this.utils.emit.apply(this, args)\n },\n })\n }\n\n var navigationDelegate = NavigationDelegateClass.new()\n navigationDelegate.utils = NSDictionary.dictionaryWithDictionary({\n setTitle: browserWindow.setTitle.bind(browserWindow),\n emitBrowserEvent() {\n try {\n browserWindow.emit.apply(browserWindow, arguments)\n } catch (err) {\n console.error(err)\n throw err\n }\n },\n emit() {\n try {\n browserWindow.webContents.emit.apply(\n browserWindow.webContents,\n arguments\n )\n } catch (err) {\n console.error(err)\n throw err\n }\n },\n })\n // reset state as well\n navigationDelegate.state = NSMutableDictionary.dictionaryWithDictionary({\n wasReady: 0,\n })\n\n webview.setNavigationDelegate(navigationDelegate)\n\n var webScriptHandler = WebScriptHandlerClass.new()\n webScriptHandler.utils = NSDictionary.dictionaryWithDictionary({\n emit() {\n try {\n browserWindow.webContents.emit.apply(\n browserWindow.webContents,\n arguments\n )\n } catch (err) {\n console.error(err)\n throw err\n }\n },\n parseWebArguments: parseWebArguments,\n })\n\n webview\n .configuration()\n .userContentController()\n .addScriptMessageHandler_name(webScriptHandler, CONSTANTS.JS_BRIDGE)\n\n var windowDelegate = WindowDelegateClass.new()\n var utils = {\n emit() {\n try {\n browserWindow.emit.apply(browserWindow, arguments)\n } catch (err) {\n console.error(err)\n throw err\n }\n },\n }\n if (options.modal) {\n // find the window of the document\n var msdocument\n if (options.parent.type === 'Document') {\n msdocument = options.parent.sketchObject\n } else {\n msdocument = options.parent\n }\n if (msdocument && String(msdocument.class()) === 'MSDocumentData') {\n // we only have an MSDocumentData instead of a MSDocument\n // let's try to get back to the MSDocument\n msdocument = msdocument.delegate()\n }\n utils.parentWindow = msdocument.windowForSheet()\n }\n\n windowDelegate.utils = NSDictionary.dictionaryWithDictionary(utils)\n windowDelegate.panel = panel\n\n panel.setDelegate(windowDelegate)\n}\n","var EventEmitter = require('events')\nvar executeJavaScript = require('./execute-javascript')\n\n// let's try to match https://github.com/electron/electron/blob/master/docs/api/web-contents.md\nmodule.exports = function buildAPI(browserWindow, panel, webview) {\n var webContents = new EventEmitter()\n\n webContents.loadURL = browserWindow.loadURL\n\n webContents.loadFile = function(/* filePath */) {\n // TODO:\n console.warn(\n 'Not implemented yet, please open a PR on https://github.com/skpm/sketch-module-web-view :)'\n )\n }\n\n webContents.downloadURL = function(/* filePath */) {\n // TODO:\n console.warn(\n 'Not implemented yet, please open a PR on https://github.com/skpm/sketch-module-web-view :)'\n )\n }\n\n webContents.getURL = function() {\n return String(webview.url())\n }\n\n webContents.getTitle = function() {\n return String(webview.title())\n }\n\n webContents.isDestroyed = function() {\n // TODO:\n console.warn(\n 'Not implemented yet, please open a PR on https://github.com/skpm/sketch-module-web-view :)'\n )\n }\n\n webContents.focus = browserWindow.focus\n webContents.isFocused = browserWindow.isFocused\n\n webContents.isLoading = function() {\n return !!webview.loading()\n }\n\n webContents.isLoadingMainFrame = function() {\n // TODO:\n return !!webview.loading()\n }\n\n webContents.isWaitingForResponse = function() {\n return !webview.loading()\n }\n\n webContents.stop = function() {\n webview.stopLoading()\n }\n webContents.reload = function() {\n webview.reload()\n }\n webContents.reloadIgnoringCache = function() {\n webview.reloadFromOrigin()\n }\n webContents.canGoBack = function() {\n return !!webview.canGoBack()\n }\n webContents.canGoForward = function() {\n return !!webview.canGoForward()\n }\n webContents.canGoToOffset = function(offset) {\n return !!webview.backForwardList().itemAtIndex(offset)\n }\n webContents.clearHistory = function() {\n // TODO:\n console.warn(\n 'Not implemented yet, please open a PR on https://github.com/skpm/sketch-module-web-view :)'\n )\n }\n webContents.goBack = function() {\n webview.goBack()\n }\n webContents.goForward = function() {\n webview.goForward()\n }\n webContents.goToIndex = function(index) {\n var backForwardList = webview.backForwardList()\n var backList = backForwardList.backList()\n var backListLength = backList.count()\n if (backListLength > index) {\n webview.loadRequest(NSURLRequest.requestWithURL(backList[index]))\n return\n }\n var forwardList = backForwardList.forwardList()\n if (forwardList.count() > index - backListLength) {\n webview.loadRequest(\n NSURLRequest.requestWithURL(forwardList[index - backListLength])\n )\n return\n }\n throw new Error('Cannot go to index ' + index)\n }\n webContents.goToOffset = function(offset) {\n if (!webContents.canGoToOffset(offset)) {\n throw new Error('Cannot go to offset ' + offset)\n }\n webview.loadRequest(\n NSURLRequest.requestWithURL(webview.backForwardList().itemAtIndex(offset))\n )\n }\n webContents.isCrashed = function() {\n // TODO:\n console.warn(\n 'Not implemented yet, please open a PR on https://github.com/skpm/sketch-module-web-view :)'\n )\n }\n webContents.setUserAgent = function(/* userAgent */) {\n // TODO:\n console.warn(\n 'Not implemented yet, please open a PR on https://github.com/skpm/sketch-module-web-view :)'\n )\n }\n webContents.getUserAgent = function() {\n const userAgent = webview.customUserAgent()\n return userAgent ? String(userAgent) : undefined\n }\n webContents.insertCSS = function(css) {\n var source =\n \"var style = document.createElement('style'); style.innerHTML = \" +\n css.replace(/\"/, '\\\\\"') +\n '; document.head.appendChild(style);'\n var script = WKUserScript.alloc().initWithSource_injectionTime_forMainFrameOnly(\n source,\n 0,\n true\n )\n webview\n .configuration()\n .userContentController()\n .addUserScript(script)\n }\n webContents.insertJS = function(source) {\n var script = WKUserScript.alloc().initWithSource_injectionTime_forMainFrameOnly(\n source,\n 0,\n true\n )\n webview\n .configuration()\n .userContentController()\n .addUserScript(script)\n }\n webContents.executeJavaScript = executeJavaScript(webview, browserWindow)\n webContents.setIgnoreMenuShortcuts = function() {\n // TODO:??\n console.warn(\n 'Not implemented yet, please open a PR on https://github.com/skpm/sketch-module-web-view :)'\n )\n }\n webContents.setAudioMuted = function(/* muted */) {\n // TODO:??\n console.warn(\n 'Not implemented yet, please open a PR on https://github.com/skpm/sketch-module-web-view :)'\n )\n }\n webContents.isAudioMuted = function() {\n // TODO:??\n console.warn(\n 'Not implemented yet, please open a PR on https://github.com/skpm/sketch-module-web-view :)'\n )\n }\n webContents.setZoomFactor = function(factor) {\n webview.setMagnification_centeredAtPoint(factor, CGPointMake(0, 0))\n }\n webContents.getZoomFactor = function(callback) {\n callback(Number(webview.magnification()))\n }\n webContents.setZoomLevel = function(level) {\n // eslint-disable-next-line no-restricted-properties\n webContents.setZoomFactor(Math.pow(1.2, level))\n }\n webContents.getZoomLevel = function(callback) {\n // eslint-disable-next-line no-restricted-properties\n callback(Math.log(Number(webview.magnification())) / Math.log(1.2))\n }\n webContents.setVisualZoomLevelLimits = function(/* minimumLevel, maximumLevel */) {\n // TODO:??\n console.warn(\n 'Not implemented yet, please open a PR on https://github.com/skpm/sketch-module-web-view :)'\n )\n }\n webContents.setLayoutZoomLevelLimits = function(/* minimumLevel, maximumLevel */) {\n // TODO:??\n console.warn(\n 'Not implemented yet, please open a PR on https://github.com/skpm/sketch-module-web-view :)'\n )\n }\n\n // TODO:\n // webContents.undo = function() {\n // webview.undoManager().undo()\n // }\n // webContents.redo = function() {\n // webview.undoManager().redo()\n // }\n // webContents.cut = webview.cut\n // webContents.copy = webview.copy\n // webContents.paste = webview.paste\n // webContents.pasteAndMatchStyle = webview.pasteAsRichText\n // webContents.delete = webview.delete\n // webContents.replace = webview.replaceSelectionWithText\n\n webContents.send = function() {\n const script =\n 'window.postMessage({' +\n 'isSketchMessage: true,' +\n \"origin: '\" +\n String(__command.identifier()) +\n \"',\" +\n 'args: ' +\n JSON.stringify([].slice.call(arguments)) +\n '}, \"*\")'\n webview.evaluateJavaScript_completionHandler(script, null)\n }\n\n webContents.getNativeWebview = function() {\n return webview\n }\n\n browserWindow.webContents = webContents\n}\n","module.exports = \"file://\" + context.plugin.urlForResourceNamed(\"_webpack_resources/43aa738f582ce37c61cb81dcdf421b63.html\").path();","import sketch from 'sketch';\n\n// resize logic\nconst resize = (selection, dimensions) => {\n for (let layer of selection.layers) {\n const frame = layer.frame;\n const finalDimensions = process(dimensions, frame.width, frame.height);\n frame.width = finalDimensions.width;\n frame.height = finalDimensions.height;\n }\n\n sketch.UI.message(`Resized ${selection.length} layer(s)`);\n};\n\n// process dimensions, and replace %w, and %h\nconst process = (dimensions, originalWidth, originalHeight) => ({\n width: eval(sanitize(dimensions.width, originalWidth, originalHeight)),\n height: eval(sanitize(dimensions.height, originalWidth, originalHeight)),\n});\n\nconst sanitize = (string, w, h) => string.replace(/%w/gi, w).replace(/%h/gi, h);\n\nexport default resize;\n","import BrowserWindow from 'sketch-module-web-view';\nimport sketch from 'sketch';\nimport resize from './actions/resize';\n\n// main function\nconst main = () => {\n const document = sketch.getSelectedDocument();\n const selection = document.selectedLayers;\n\n // return error message if selection is empty\n if (selection.isEmpty) {\n sketch.UI.message('No layer(s) selected!');\n return;\n }\n\n // define the webview, its options, and its content\n const options = {\n identifier: 'sketch.resize',\n width: 480,\n height: 316,\n center: true,\n show: false,\n backgroundColor: '#181818',\n titleBarStyle: 'hiddenInset',\n alwaysOnTop: true,\n resizable: false,\n fullscreenable: false,\n maximizable: false,\n minimizable: false,\n };\n\n let webview = new BrowserWindow(options);\n const webcontent = webview.webContents;\n\n // when loaded, show the webview\n webview.once('ready-to-show', () => webview.show());\n\n // cancel the overall process\n webcontent.on('cancel', () => close(webview));\n\n // process input, and trigger resize\n webcontent.on('submit', dimensions => {\n resize(selection, dimensions);\n close(webview);\n });\n\n // show the webview\n webview.loadURL(require('../resources/webview/webview.html'));\n};\n\n// close the window\nconst close = webview => {\n webview.close();\n webview = null;\n};\n\nexport default main;\n","module.exports = require(\"events\");","module.exports = require(\"sketch\");"],"sourceRoot":""} -------------------------------------------------------------------------------- /Sketch Resize.sketchplugin/Contents/Sketch/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Luca Orio", 3 | "name": "Resize", 4 | "compatibleVersion": 3, 5 | "bundleVersion": 1, 6 | "identifier": "com.sketch.resize", 7 | "icon": "sketch-icon.png", 8 | "appcast": "https://raw.githubusercontent.com/lucaorio/sketch-resize/master/appcast.xml", 9 | "commands": [ 10 | { 11 | "name": "Resize Elements", 12 | "identifier": "main", 13 | "script": "main.js", 14 | "description": "Resize (not scale) multiple layers at once via shortcut", 15 | "icon": "resize.png", 16 | "shortcut": "ctrl cmd k" 17 | } 18 | ], 19 | "menu": { 20 | "title": "Resize", 21 | "items": [ 22 | "main" 23 | ] 24 | }, 25 | "version": "2.1.0", 26 | "description": "Resize (not scale) multiple layers at once via shortcut", 27 | "homepage": "https://github.com/lucaorio/sketch-resize#readme", 28 | "disableCocoaScriptPreprocessor": true 29 | } -------------------------------------------------------------------------------- /appcast.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Sketch Resize 5 | https://github.com/lucaorio/sketch-resize 6 | Resize (not scale) multiple layers at once via shortcut. 7 | en 8 | 9 | Sketch Resize 1.0.0 10 | 11 | 13 |
  • First Release
  • 14 | 15 | ]]> 16 |
    17 | 18 |
    19 | 20 | Sketch Resize 1.0.1 21 | 22 | 24 |
  • Changed plugin description
  • 25 |
  • Changed dialog description
  • 26 | 27 | ]]> 28 |
    29 | 30 |
    31 | 32 | Sketch Resize 1.0.2 33 | 34 | 36 |
  • Various bugfixes
  • 37 | 38 | ]]> 39 |
    40 | 41 |
    42 | 43 | Sketch Resize 2.0.0 44 | 45 | 47 |
  • Total rewrite
  • 48 | 49 | ]]> 50 |
    51 | 52 |
    53 | 54 | Sketch Resize 2.1.0 55 | 56 | 58 |
  • Added regular icon
  • 59 | 60 | ]]> 61 |
    62 | 63 |
    64 |
    65 |
    66 | -------------------------------------------------------------------------------- /assets/resize.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucaorio/sketch-resize/b2babc188b7e6e362588c971807f75bf48b4dcd0/assets/resize.png -------------------------------------------------------------------------------- /assets/sketch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucaorio/sketch-resize/b2babc188b7e6e362588c971807f75bf48b4dcd0/assets/sketch-icon.png -------------------------------------------------------------------------------- /images/img-header.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucaorio/sketch-resize/b2babc188b7e6e362588c971807f75bf48b4dcd0/images/img-header.jpg -------------------------------------------------------------------------------- /images/img-sketch-resize.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucaorio/sketch-resize/b2babc188b7e6e362588c971807f75bf48b4dcd0/images/img-sketch-resize.jpg -------------------------------------------------------------------------------- /images/img-sketch-reverse.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucaorio/sketch-resize/b2babc188b7e6e362588c971807f75bf48b4dcd0/images/img-sketch-reverse.jpg -------------------------------------------------------------------------------- /images/img-sketch-runner.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucaorio/sketch-resize/b2babc188b7e6e362588c971807f75bf48b4dcd0/images/img-sketch-runner.jpg -------------------------------------------------------------------------------- /images/img-sketch-styles-generator.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucaorio/sketch-resize/b2babc188b7e6e362588c971807f75bf48b4dcd0/images/img-sketch-styles-generator.jpg -------------------------------------------------------------------------------- /images/img-usage.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucaorio/sketch-resize/b2babc188b7e6e362588c971807f75bf48b4dcd0/images/img-usage.gif -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Luca Orio (lucaorio.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sketch-resize", 3 | "version": "2.1.0", 4 | "description": "Resize (not scale) multiple layers at once via shortcut", 5 | "author": "Luca Orio ", 6 | "homepage": "https://github.com/lucaorio/sketch-resize#readme", 7 | "license": "MIT", 8 | "repository": { 9 | "type": "git", 10 | "url": "git+https://github.com/lucaorio/sketch-resize.git" 11 | }, 12 | "keywords": [ 13 | "sketch", 14 | "sketchapp", 15 | "plugin", 16 | "resize", 17 | "scale", 18 | "unproportional", 19 | "unproportional resize", 20 | "unproportional scale" 21 | ], 22 | "engines": { 23 | "sketch": ">=54.0" 24 | }, 25 | "bugs": { 26 | "url": "https://github.com/lucaorio/sketch-resize/issues" 27 | }, 28 | "scripts": { 29 | "watch": "skpm-build --watch", 30 | "lint": "eslint src/**/*.js && prettier --write src/**/*.js", 31 | "build": "yarn lint && skpm-build", 32 | "postinstall": "yarn build && skpm-link" 33 | }, 34 | "assets": [ 35 | "assets/**/*.*" 36 | ], 37 | "resources": [ 38 | "resources/webview/**/*.js" 39 | ], 40 | "skpm": { 41 | "name": "Sketch Resize", 42 | "manifest": "src/manifest.json", 43 | "main": "Sketch Resize.sketchplugin" 44 | }, 45 | "devDependencies": { 46 | "@skpm/builder": "^0.5.11", 47 | "@skpm/extract-loader": "^2.0.2", 48 | "babel-eslint": "^10.0.1", 49 | "css-loader": "^1.0.0", 50 | "eslint": "^5.11.1", 51 | "eslint-plugin-import": "^2.14.0", 52 | "html-loader": "^0.5.1", 53 | "prettier": "^1.16.4", 54 | "sketch-module-web-view": "^3.0.1" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Sketch Resize 2 | 3 | ![Sketch Resize](images/img-header.jpg) 4 | 5 | > Resize (not scale) multiple layers at once via shortcut 6 | 7 | **Sketch Resize** is a plugin made for [Sketch](http://sketchapp.com). It provides unproportional, unconstrained resize for multiple selected layer. It's triggered via shortcut, and doesn't require to reach nor focus the inspector panel. 8 | 9 | Given a selection containing multiple layers, _Resize_ tries to avoid the following limitations: 10 | 11 | - The **scale layers** feature (`cmd+k`) affects width, height, and some other properties at once, proportionally 12 | - After focusing the inspector panel via shortcut (`alt+tab`), in order to resize anything we need to tab twice, enter the width, tab once, enter the height, blur the panel (or press enter) 13 | - The inspector panel could be very far away from the current position of the mouse cursor 14 | - Nudging and dragging could be slow, or imprecise 15 | 16 | Follow me on Twitter [@lucaorio\_](https://twitter.com/lucaorio_) for updates, help and other stuff! 🎉 17 | 18 | _Looking for other plugins? Give these a try!_ 😎 19 | 20 | [Sketch Styles Generator](https://github.com/lucaorio/sketch-styles-generator) 21 | [Sketch Reverse](https://github.com/lucaorio/sketch-reverse) 22 | 23 | ## Contents 24 | 25 | - [Installation](#installation) 26 | - [Usage](#usage) 27 | - [Integrations](#integrations) 28 | - [License](#license) 29 | - [Contacts](#contacts) 30 | 31 | ## Installation 32 | 33 | #### Manual 34 | 35 | - [Download](https://github.com/lucaorio/sketch-resize/releases/latest) the latest release of the plugin [`sketch-resize.zip`](https://github.com/lucaorio/sketch-resize/releases/latest) 36 | - Uncompress the downloaded file 37 | - Double-click `Sketch Resize.sketchplugin` to install 38 | 39 | #### Via Sketch Runner 40 | 41 | - Trigger [Sketch Runner](http://bit.ly/SketchRunnerWebsite) (`cmd+'`) 42 | - Move to the _Install_ tab 43 | - Search for _Resize_, and install 44 | 45 | ## Usage 46 | 47 | - **Select** layer(s) 48 | - **Run** the plugin by clicking `Plugins->Resize->Resize Elements`, or by using the `ctrl+cmd+k` shortcut 49 | - Specify the new **dimension(s)**, and press `enter` to confirm 50 | 51 | #### Some additional notes 52 | 53 | - The plugin can be used on _single layers_, too 54 | - Width/height can be both _declared at once_. Leaving a field empty, or in its default state (%w/%h) will _skip_ the related dimension 55 | - Width/height input fields accepts _basic math operations_ (ie.`%w+%h+(10+20)-8/4*2`), where %w/%h are references to the _original size of each layer_ 56 | - Press `tab` to _focus_ between the width/height input fields, and cancel/confirm buttons 57 | - Press `esc` to _close the panel_ and leave the dimensions unchanged 58 | - Pressing `enter` will always trigger the _confirm_ button, no matter the focused element 59 | 60 | ![Resize Usage](images/img-usage.gif) 61 | 62 | ## Integrations 63 | 64 | _Sketch Resize_ is fully integrated with [Sketch Runner](http://bit.ly/SketchRunnerWebsite), the ultimate tool to speed up your Sketch workflow. You can trigger the plugin by simply typing its name. 65 | 66 | ![Sketch Runner Integration](images/img-sketch-runner.jpg) 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | ## License 77 | 78 | ![https://github.com/lucaorio/sketch-resize/blob/master/license](https://img.shields.io/badge/license-MIT-blue.svg) 79 | 80 | --- 81 | 82 | ## Contacts 83 | 84 | - 🐦 Twitter [@lucaorio\_](http://twitter.com/@lucaorio_) 85 | - 🕸 Website [lucaorio.com](http://lucaorio.com) 86 | - 📬 Email [luca.o@me.com](mailto:luca.o@me.com) 87 | -------------------------------------------------------------------------------- /resources/webview/script.js: -------------------------------------------------------------------------------- 1 | // disable context menu 2 | document.addEventListener('contextmenu', event => { 3 | event.preventDefault(); 4 | }); 5 | 6 | // cancel button 7 | document.getElementById('cancel').addEventListener('click', event => { 8 | event.preventDefault(); 9 | window.postMessage('cancel'); 10 | }); 11 | 12 | // submit on enter 13 | document.addEventListener('keypress', event => { 14 | if (event.keyCode !== 13) return; 15 | event.preventDefault(); 16 | window.postMessage('submit', getValues()); 17 | }); 18 | 19 | // submit button 20 | document.getElementById('submit').addEventListener('click', event => { 21 | event.preventDefault(); 22 | window.postMessage('submit', getValues()); 23 | }); 24 | 25 | // get inputs value 26 | const getValues = () => ({ 27 | width: document.getElementById('width').value, 28 | height: document.getElementById('height').value 29 | }); 30 | -------------------------------------------------------------------------------- /resources/webview/styles.css: -------------------------------------------------------------------------------- 1 | html { 2 | box-sizing: border-box; 3 | overflow: hidden; 4 | padding: 1px; 5 | color: #9c9c9c; 6 | background: #171717; 7 | font-family: -apple-system; 8 | font-size: 13px; 9 | line-height: 20px; 10 | cursor: default; 11 | } 12 | 13 | *, 14 | *:before, 15 | *:after { 16 | position: relative; 17 | box-sizing: inherit; 18 | margin: 0; 19 | padding: 0; 20 | -webkit-user-select: none; 21 | user-select: none; 22 | } 23 | 24 | input, 25 | textarea { 26 | -webkit-user-select: auto; 27 | user-select: auto; 28 | } 29 | 30 | /* -------------------------------- */ 31 | /* header/menubar */ 32 | /* -------------------------------- */ 33 | 34 | header { 35 | border-bottom: 1px solid #000000; 36 | height: 37px; 37 | color: rgba(255, 255, 255, 0.7); 38 | background-color: #242424; 39 | font-weight: 600; 40 | line-height: 37px; 41 | text-align: center; 42 | } 43 | 44 | /* -------------------------------- */ 45 | /* section/content */ 46 | /* -------------------------------- */ 47 | 48 | section { 49 | padding: 24px; 50 | } 51 | 52 | section > p.info { 53 | margin-bottom: 16px; 54 | font-weight: 400; 55 | color: rgba(255, 255, 255, 0.7); 56 | } 57 | 58 | section > p.info > strong { 59 | color: #ffffff; 60 | } 61 | 62 | section > div { 63 | font-size: 0; 64 | } 65 | 66 | section > div > input { 67 | display: inline-block; 68 | margin-bottom: 16px; 69 | border: 2px solid #242424; 70 | border-radius: 4px; 71 | padding: 12px; 72 | width: calc(50% - 4px); 73 | background-color: #242424; 74 | color: #ffffff; 75 | font-size: 13px; 76 | } 77 | 78 | section > div > input:focus { 79 | outline: 0; 80 | border: 2px solid #1c7ed5; 81 | } 82 | 83 | section > div > input::placeholder { 84 | color: rgba(255, 255, 255, 0.4); 85 | } 86 | 87 | section > div > input#width { 88 | margin-right: 8px; 89 | } 90 | 91 | section > p.tip { 92 | font-size: 12px; 93 | font-weight: 400; 94 | color: rgba(255, 255, 255, 0.4); 95 | } 96 | 97 | /* -------------------------------- */ 98 | /* footer/actions */ 99 | /* -------------------------------- */ 100 | 101 | footer { 102 | border-top: 1px solid rgba(255, 255, 255, 0.1); 103 | padding: 16px 24px; 104 | background-color: #242424; 105 | font-size: 0; 106 | text-align: right; 107 | } 108 | 109 | footer > button { 110 | display: inline-block; 111 | height: 40px; 112 | border: 0; 113 | border-radius: 4px; 114 | padding: 0 12px; 115 | color: #ffffff; 116 | font-size: 13px; 117 | line-height: 16px; 118 | font-weight: 600; 119 | } 120 | 121 | footer > button:focus { 122 | outline: 0; 123 | } 124 | 125 | footer > button:focus:after { 126 | content: ''; 127 | display: block; 128 | position: absolute; 129 | top: -5px; 130 | left: -5px; 131 | width: calc(100% + 10px); 132 | height: calc(100% + 10px); 133 | border: 2px solid #1c7ed5; 134 | border-radius: 6px; 135 | } 136 | 137 | footer > button#cancel { 138 | margin-right: 8px; 139 | border: 1px solid rgba(255, 255, 255, 0.4); 140 | background-color: transparent; 141 | } 142 | 143 | footer > button#cancel:hover { 144 | background-color: rgba(255, 255, 255, 0.1); 145 | } 146 | 147 | footer > button#submit { 148 | border: 1px solid #1c7ed5; 149 | background-color: #1c7ed5; 150 | } 151 | 152 | footer > button#submit:hover { 153 | background-color: #186bb5; 154 | } 155 | -------------------------------------------------------------------------------- /resources/webview/webview.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Sketch Resize 7 | 8 | 9 | 10 | 11 |
    12 | Resize Element(s) 13 |
    14 | 15 |
    16 |

    Enter the new width/height of every selected element.

    17 |
    18 | 19 | 20 |
    21 |

    22 | Use %w, and %h as wildcards for math operations (i.e. %w + 24). The resizing is unconstrained, 23 | and doesn't affect any other property like corner radius, border thickness, shadow size, etc. 24 |

    25 |
    26 | 27 |
    28 | 29 | 30 |
    31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/actions/resize.js: -------------------------------------------------------------------------------- 1 | import sketch from 'sketch'; 2 | 3 | // resize logic 4 | const resize = (selection, dimensions) => { 5 | for (let layer of selection.layers) { 6 | const frame = layer.frame; 7 | const finalDimensions = process(dimensions, frame.width, frame.height); 8 | frame.width = finalDimensions.width; 9 | frame.height = finalDimensions.height; 10 | } 11 | 12 | sketch.UI.message(`Resized ${selection.length} layer(s)`); 13 | }; 14 | 15 | // process dimensions, and replace %w, and %h 16 | const process = (dimensions, originalWidth, originalHeight) => ({ 17 | width: eval(sanitize(dimensions.width, originalWidth, originalHeight)), 18 | height: eval(sanitize(dimensions.height, originalWidth, originalHeight)), 19 | }); 20 | 21 | const sanitize = (string, w, h) => string.replace(/%w/gi, w).replace(/%h/gi, h); 22 | 23 | export default resize; 24 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import BrowserWindow from 'sketch-module-web-view'; 2 | import sketch from 'sketch'; 3 | import resize from './actions/resize'; 4 | 5 | // main function 6 | const main = () => { 7 | const document = sketch.getSelectedDocument(); 8 | const selection = document.selectedLayers; 9 | 10 | // return error message if selection is empty 11 | if (selection.isEmpty) { 12 | sketch.UI.message('No layer(s) selected!'); 13 | return; 14 | } 15 | 16 | // define the webview, its options, and its content 17 | const options = { 18 | identifier: 'sketch.resize', 19 | width: 480, 20 | height: 316, 21 | center: true, 22 | show: false, 23 | backgroundColor: '#181818', 24 | titleBarStyle: 'hiddenInset', 25 | alwaysOnTop: true, 26 | resizable: false, 27 | fullscreenable: false, 28 | maximizable: false, 29 | minimizable: false, 30 | }; 31 | 32 | let webview = new BrowserWindow(options); 33 | const webcontent = webview.webContents; 34 | 35 | // when loaded, show the webview 36 | webview.once('ready-to-show', () => webview.show()); 37 | 38 | // cancel the overall process 39 | webcontent.on('cancel', () => close(webview)); 40 | 41 | // process input, and trigger resize 42 | webcontent.on('submit', dimensions => { 43 | resize(selection, dimensions); 44 | close(webview); 45 | }); 46 | 47 | // show the webview 48 | webview.loadURL(require('../resources/webview/webview.html')); 49 | }; 50 | 51 | // close the window 52 | const close = webview => { 53 | webview.close(); 54 | webview = null; 55 | }; 56 | 57 | export default main; 58 | -------------------------------------------------------------------------------- /src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Luca Orio", 3 | "name": "Resize", 4 | "compatibleVersion": 3, 5 | "bundleVersion": 1, 6 | "identifier": "com.sketch.resize", 7 | "icon": "sketch-icon.png", 8 | "appcast": "https://raw.githubusercontent.com/lucaorio/sketch-resize/master/appcast.xml", 9 | "commands": [ 10 | { 11 | "name": "Resize Elements", 12 | "identifier": "main", 13 | "script": "./main.js", 14 | "description": "Resize (not scale) multiple layers at once via shortcut", 15 | "icon": "resize.png", 16 | "shortcut": "ctrl cmd k" 17 | } 18 | ], 19 | "menu": { 20 | "title": "Resize", 21 | "items": ["main"] 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /webpack.skpm.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function(config) { 2 | config.module.rules.push({ 3 | test: /\.(html)$/, 4 | use: [ 5 | { 6 | loader: '@skpm/extract-loader', 7 | }, 8 | { 9 | loader: 'html-loader', 10 | options: { 11 | attrs: ['img:src', 'link:href'], 12 | interpolate: true, 13 | }, 14 | }, 15 | ], 16 | }); 17 | config.module.rules.push({ 18 | test: /\.(css)$/, 19 | use: [ 20 | { 21 | loader: '@skpm/extract-loader', 22 | }, 23 | { 24 | loader: 'css-loader', 25 | }, 26 | ], 27 | }); 28 | }; 29 | --------------------------------------------------------------------------------