├── .debug ├── .gitignore ├── CSXS └── manifest.xml ├── LICENSE ├── README.md ├── _builds ├── Scribe1.00.zxp ├── Scribe1.01.zxp └── test.md ├── assets ├── fonts │ ├── Swap-Font.svg │ ├── Swap-Font.ttf │ └── Swap-Font.woff ├── iconLight.png ├── iconLightRollover.png └── icons │ ├── artboard.svg │ ├── both.svg │ ├── doc.svg │ ├── fill.svg │ ├── layers.svg │ ├── selection.svg │ ├── stroke.svg │ ├── swapH.svg │ └── swapV.svg ├── client ├── index.html ├── libs │ ├── CSInterface.js │ ├── events.js │ ├── menus.js │ ├── preStyle.css │ ├── reference.js │ ├── reset.css │ └── v-outside-events.min.js ├── main.js └── style.css ├── host ├── ILST │ ├── host.jsx │ ├── host.ts │ └── tsconfig.json ├── PHXS │ ├── host.jsx │ ├── host.ts │ └── tsconfig.json └── universal │ ├── Console.jsx │ └── json2.jsx └── notes.md /.debug: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /CSXS/manifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | ./client/index.html 23 | 24 | 25 | 26 | true 27 | 28 | 29 | Panel 30 | Scribe 31 | 32 | 33 | 50 34 | 200 35 | 36 | 44 | 45 | 46 | ./assets/iconLight.png 47 | ./assets/iconLightRollover.png 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Tom Scharstein 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Scribe 2 | 3 | ## DEPRECATED -- This panel has been rebuilt as [ProtoLayers](https://github.com/Inventsable/protoLayers). 4 | 5 | Simplified layer renaming utility -- rename Illustrator layers the same way (and even easier/faster) than you do in After Effects. 6 | 7 | ![](https://thumbs.gfycat.com/InconsequentialPhonyCow-size_restricted.gif) 8 | 9 | ## install 10 | 11 | ```bash 12 | # .../AppData/Roaming/Adobe/CEP/extensions 13 | git clone https://github.com/Inventsable/Scribe.git 14 | ``` 15 | 16 | # to-do 17 | 18 | - Optional nested groups and parent/children naming (ctrl + left/right) 19 | - Renaming multiple layers produces sequential names 20 | 21 | # requests from [promotional reddit thread](https://www.reddit.com/r/AdobeIllustrator/comments/a1eglc/working_on_an_open_source_panel_to_make_layer/): 22 | 23 | - Support for artboards 24 | - Automatic stripping of " copy" from layer/artboard names 25 | - Optional prefix/suffix inputs 26 | - Photoshop and After Effects support 27 | - Quick change of color for labels 28 | -------------------------------------------------------------------------------- /_builds/Scribe1.00.zxp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Inventsable/scribe/c07d05ce8cf015c7a9ec0338b4f9dbed27b6a48d/_builds/Scribe1.00.zxp -------------------------------------------------------------------------------- /_builds/Scribe1.01.zxp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Inventsable/scribe/c07d05ce8cf015c7a9ec0338b4f9dbed27b6a48d/_builds/Scribe1.01.zxp -------------------------------------------------------------------------------- /_builds/test.md: -------------------------------------------------------------------------------- 1 | hello -------------------------------------------------------------------------------- /assets/fonts/Swap-Font.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Generated by IcoMoon 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /assets/fonts/Swap-Font.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Inventsable/scribe/c07d05ce8cf015c7a9ec0338b4f9dbed27b6a48d/assets/fonts/Swap-Font.ttf -------------------------------------------------------------------------------- /assets/fonts/Swap-Font.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Inventsable/scribe/c07d05ce8cf015c7a9ec0338b4f9dbed27b6a48d/assets/fonts/Swap-Font.woff -------------------------------------------------------------------------------- /assets/iconLight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Inventsable/scribe/c07d05ce8cf015c7a9ec0338b4f9dbed27b6a48d/assets/iconLight.png -------------------------------------------------------------------------------- /assets/iconLightRollover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Inventsable/scribe/c07d05ce8cf015c7a9ec0338b4f9dbed27b6a48d/assets/iconLightRollover.png -------------------------------------------------------------------------------- /assets/icons/artboard.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 13 | artboard 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /assets/icons/both.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 13 | both 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /assets/icons/doc.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 13 | doc 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /assets/icons/fill.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 13 | fill 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /assets/icons/layers.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 13 | layers 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /assets/icons/selection.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 13 | selection 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /assets/icons/stroke.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 13 | stroke 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /assets/icons/swapH.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 13 | swapH 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /assets/icons/swapV.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 13 | swapV 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /client/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Scribe 11 | 12 | 13 | 14 |
15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /client/libs/CSInterface.js: -------------------------------------------------------------------------------- 1 | /************************************************************************************************** 2 | * 3 | * ADOBE SYSTEMS INCORPORATED 4 | * Copyright 2013 Adobe Systems Incorporated 5 | * All Rights Reserved. 6 | * 7 | * NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the 8 | * terms of the Adobe license agreement accompanying it. If you have received this file from a 9 | * source other than Adobe, then your use, modification, or distribution of it requires the prior 10 | * written permission of Adobe. 11 | * 12 | **************************************************************************************************/ 13 | 14 | /** CSInterface - v9.2.0 */ 15 | 16 | /** 17 | * Stores constants for the window types supported by the CSXS infrastructure. 18 | */ 19 | function CSXSWindowType() { 20 | } 21 | 22 | /** Constant for the CSXS window type Panel. */ 23 | CSXSWindowType._PANEL = "Panel"; 24 | 25 | /** Constant for the CSXS window type Modeless. */ 26 | CSXSWindowType._MODELESS = "Modeless"; 27 | 28 | /** Constant for the CSXS window type ModalDialog. */ 29 | CSXSWindowType._MODAL_DIALOG = "ModalDialog"; 30 | 31 | /** EvalScript error message */ 32 | EvalScript_ErrMessage = "EvalScript error."; 33 | 34 | /** 35 | * @class Version 36 | * Defines a version number with major, minor, micro, and special 37 | * components. The major, minor and micro values are numeric; the special 38 | * value can be any string. 39 | * 40 | * @param major The major version component, a positive integer up to nine digits long. 41 | * @param minor The minor version component, a positive integer up to nine digits long. 42 | * @param micro The micro version component, a positive integer up to nine digits long. 43 | * @param special The special version component, an arbitrary string. 44 | * 45 | * @return A new \c Version object. 46 | */ 47 | function Version(major, minor, micro, special) { 48 | this.major = major; 49 | this.minor = minor; 50 | this.micro = micro; 51 | this.special = special; 52 | } 53 | 54 | /** 55 | * The maximum value allowed for a numeric version component. 56 | * This reflects the maximum value allowed in PlugPlug and the manifest schema. 57 | */ 58 | Version.MAX_NUM = 999999999; 59 | 60 | /** 61 | * @class VersionBound 62 | * Defines a boundary for a version range, which associates a \c Version object 63 | * with a flag for whether it is an inclusive or exclusive boundary. 64 | * 65 | * @param version The \c #Version object. 66 | * @param inclusive True if this boundary is inclusive, false if it is exclusive. 67 | * 68 | * @return A new \c VersionBound object. 69 | */ 70 | function VersionBound(version, inclusive) { 71 | this.version = version; 72 | this.inclusive = inclusive; 73 | } 74 | 75 | /** 76 | * @class VersionRange 77 | * Defines a range of versions using a lower boundary and optional upper boundary. 78 | * 79 | * @param lowerBound The \c #VersionBound object. 80 | * @param upperBound The \c #VersionBound object, or null for a range with no upper boundary. 81 | * 82 | * @return A new \c VersionRange object. 83 | */ 84 | function VersionRange(lowerBound, upperBound) { 85 | this.lowerBound = lowerBound; 86 | this.upperBound = upperBound; 87 | } 88 | 89 | /** 90 | * @class Runtime 91 | * Represents a runtime related to the CEP infrastructure. 92 | * Extensions can declare dependencies on particular 93 | * CEP runtime versions in the extension manifest. 94 | * 95 | * @param name The runtime name. 96 | * @param version A \c #VersionRange object that defines a range of valid versions. 97 | * 98 | * @return A new \c Runtime object. 99 | */ 100 | function Runtime(name, versionRange) { 101 | this.name = name; 102 | this.versionRange = versionRange; 103 | } 104 | 105 | /** 106 | * @class Extension 107 | * Encapsulates a CEP-based extension to an Adobe application. 108 | * 109 | * @param id The unique identifier of this extension. 110 | * @param name The localizable display name of this extension. 111 | * @param mainPath The path of the "index.html" file. 112 | * @param basePath The base path of this extension. 113 | * @param windowType The window type of the main window of this extension. 114 | Valid values are defined by \c #CSXSWindowType. 115 | * @param width The default width in pixels of the main window of this extension. 116 | * @param height The default height in pixels of the main window of this extension. 117 | * @param minWidth The minimum width in pixels of the main window of this extension. 118 | * @param minHeight The minimum height in pixels of the main window of this extension. 119 | * @param maxWidth The maximum width in pixels of the main window of this extension. 120 | * @param maxHeight The maximum height in pixels of the main window of this extension. 121 | * @param defaultExtensionDataXml The extension data contained in the default \c ExtensionDispatchInfo section of the extension manifest. 122 | * @param specialExtensionDataXml The extension data contained in the application-specific \c ExtensionDispatchInfo section of the extension manifest. 123 | * @param requiredRuntimeList An array of \c Runtime objects for runtimes required by this extension. 124 | * @param isAutoVisible True if this extension is visible on loading. 125 | * @param isPluginExtension True if this extension has been deployed in the Plugins folder of the host application. 126 | * 127 | * @return A new \c Extension object. 128 | */ 129 | function Extension(id, name, mainPath, basePath, windowType, width, height, minWidth, minHeight, maxWidth, maxHeight, 130 | defaultExtensionDataXml, specialExtensionDataXml, requiredRuntimeList, isAutoVisible, isPluginExtension) { 131 | this.id = id; 132 | this.name = name; 133 | this.mainPath = mainPath; 134 | this.basePath = basePath; 135 | this.windowType = windowType; 136 | this.width = width; 137 | this.height = height; 138 | this.minWidth = minWidth; 139 | this.minHeight = minHeight; 140 | this.maxWidth = maxWidth; 141 | this.maxHeight = maxHeight; 142 | this.defaultExtensionDataXml = defaultExtensionDataXml; 143 | this.specialExtensionDataXml = specialExtensionDataXml; 144 | this.requiredRuntimeList = requiredRuntimeList; 145 | this.isAutoVisible = isAutoVisible; 146 | this.isPluginExtension = isPluginExtension; 147 | } 148 | 149 | /** 150 | * @class CSEvent 151 | * A standard JavaScript event, the base class for CEP events. 152 | * 153 | * @param type The name of the event type. 154 | * @param scope The scope of event, can be "GLOBAL" or "APPLICATION". 155 | * @param appId The unique identifier of the application that generated the event. 156 | * @param extensionId The unique identifier of the extension that generated the event. 157 | * 158 | * @return A new \c CSEvent object 159 | */ 160 | function CSEvent(type, scope, appId, extensionId) { 161 | this.type = type; 162 | this.scope = scope; 163 | this.appId = appId; 164 | this.extensionId = extensionId; 165 | } 166 | 167 | /** Event-specific data. */ 168 | CSEvent.prototype.data = ""; 169 | 170 | /** 171 | * @class SystemPath 172 | * Stores operating-system-specific location constants for use in the 173 | * \c #CSInterface.getSystemPath() method. 174 | * @return A new \c SystemPath object. 175 | */ 176 | function SystemPath() { 177 | } 178 | 179 | /** The path to user data. */ 180 | SystemPath.USER_DATA = "userData"; 181 | 182 | /** The path to common files for Adobe applications. */ 183 | SystemPath.COMMON_FILES = "commonFiles"; 184 | 185 | /** The path to the user's default document folder. */ 186 | SystemPath.MY_DOCUMENTS = "myDocuments"; 187 | 188 | /** @deprecated. Use \c #SystemPath.Extension. */ 189 | SystemPath.APPLICATION = "application"; 190 | 191 | /** The path to current extension. */ 192 | SystemPath.EXTENSION = "extension"; 193 | 194 | /** The path to hosting application's executable. */ 195 | SystemPath.HOST_APPLICATION = "hostApplication"; 196 | 197 | /** 198 | * @class ColorType 199 | * Stores color-type constants. 200 | */ 201 | function ColorType() { 202 | } 203 | 204 | /** RGB color type. */ 205 | ColorType.RGB = "rgb"; 206 | 207 | /** Gradient color type. */ 208 | ColorType.GRADIENT = "gradient"; 209 | 210 | /** Null color type. */ 211 | ColorType.NONE = "none"; 212 | 213 | /** 214 | * @class RGBColor 215 | * Stores an RGB color with red, green, blue, and alpha values. 216 | * All values are in the range [0.0 to 255.0]. Invalid numeric values are 217 | * converted to numbers within this range. 218 | * 219 | * @param red The red value, in the range [0.0 to 255.0]. 220 | * @param green The green value, in the range [0.0 to 255.0]. 221 | * @param blue The blue value, in the range [0.0 to 255.0]. 222 | * @param alpha The alpha (transparency) value, in the range [0.0 to 255.0]. 223 | * The default, 255.0, means that the color is fully opaque. 224 | * 225 | * @return A new RGBColor object. 226 | */ 227 | function RGBColor(red, green, blue, alpha) { 228 | this.red = red; 229 | this.green = green; 230 | this.blue = blue; 231 | this.alpha = alpha; 232 | } 233 | 234 | /** 235 | * @class Direction 236 | * A point value in which the y component is 0 and the x component 237 | * is positive or negative for a right or left direction, 238 | * or the x component is 0 and the y component is positive or negative for 239 | * an up or down direction. 240 | * 241 | * @param x The horizontal component of the point. 242 | * @param y The vertical component of the point. 243 | * 244 | * @return A new \c Direction object. 245 | */ 246 | function Direction(x, y) { 247 | this.x = x; 248 | this.y = y; 249 | } 250 | 251 | /** 252 | * @class GradientStop 253 | * Stores gradient stop information. 254 | * 255 | * @param offset The offset of the gradient stop, in the range [0.0 to 1.0]. 256 | * @param rgbColor The color of the gradient at this point, an \c #RGBColor object. 257 | * 258 | * @return GradientStop object. 259 | */ 260 | function GradientStop(offset, rgbColor) { 261 | this.offset = offset; 262 | this.rgbColor = rgbColor; 263 | } 264 | 265 | /** 266 | * @class GradientColor 267 | * Stores gradient color information. 268 | * 269 | * @param type The gradient type, must be "linear". 270 | * @param direction A \c #Direction object for the direction of the gradient 271 | (up, down, right, or left). 272 | * @param numStops The number of stops in the gradient. 273 | * @param gradientStopList An array of \c #GradientStop objects. 274 | * 275 | * @return A new \c GradientColor object. 276 | */ 277 | function GradientColor(type, direction, numStops, arrGradientStop) { 278 | this.type = type; 279 | this.direction = direction; 280 | this.numStops = numStops; 281 | this.arrGradientStop = arrGradientStop; 282 | } 283 | 284 | /** 285 | * @class UIColor 286 | * Stores color information, including the type, anti-alias level, and specific color 287 | * values in a color object of an appropriate type. 288 | * 289 | * @param type The color type, 1 for "rgb" and 2 for "gradient". 290 | The supplied color object must correspond to this type. 291 | * @param antialiasLevel The anti-alias level constant. 292 | * @param color A \c #RGBColor or \c #GradientColor object containing specific color information. 293 | * 294 | * @return A new \c UIColor object. 295 | */ 296 | function UIColor(type, antialiasLevel, color) { 297 | this.type = type; 298 | this.antialiasLevel = antialiasLevel; 299 | this.color = color; 300 | } 301 | 302 | /** 303 | * @class AppSkinInfo 304 | * Stores window-skin properties, such as color and font. All color parameter values are \c #UIColor objects except that systemHighlightColor is \c #RGBColor object. 305 | * 306 | * @param baseFontFamily The base font family of the application. 307 | * @param baseFontSize The base font size of the application. 308 | * @param appBarBackgroundColor The application bar background color. 309 | * @param panelBackgroundColor The background color of the extension panel. 310 | * @param appBarBackgroundColorSRGB The application bar background color, as sRGB. 311 | * @param panelBackgroundColorSRGB The background color of the extension panel, as sRGB. 312 | * @param systemHighlightColor The highlight color of the extension panel, if provided by the host application. Otherwise, the operating-system highlight color. 313 | * 314 | * @return AppSkinInfo object. 315 | */ 316 | function AppSkinInfo(baseFontFamily, baseFontSize, appBarBackgroundColor, panelBackgroundColor, appBarBackgroundColorSRGB, panelBackgroundColorSRGB, systemHighlightColor) { 317 | this.baseFontFamily = baseFontFamily; 318 | this.baseFontSize = baseFontSize; 319 | this.appBarBackgroundColor = appBarBackgroundColor; 320 | this.panelBackgroundColor = panelBackgroundColor; 321 | this.appBarBackgroundColorSRGB = appBarBackgroundColorSRGB; 322 | this.panelBackgroundColorSRGB = panelBackgroundColorSRGB; 323 | this.systemHighlightColor = systemHighlightColor; 324 | } 325 | 326 | /** 327 | * @class HostEnvironment 328 | * Stores information about the environment in which the extension is loaded. 329 | * 330 | * @param appName The application's name. 331 | * @param appVersion The application's version. 332 | * @param appLocale The application's current license locale. 333 | * @param appUILocale The application's current UI locale. 334 | * @param appId The application's unique identifier. 335 | * @param isAppOnline True if the application is currently online. 336 | * @param appSkinInfo An \c #AppSkinInfo object containing the application's default color and font styles. 337 | * 338 | * @return A new \c HostEnvironment object. 339 | */ 340 | function HostEnvironment(appName, appVersion, appLocale, appUILocale, appId, isAppOnline, appSkinInfo) { 341 | this.appName = appName; 342 | this.appVersion = appVersion; 343 | this.appLocale = appLocale; 344 | this.appUILocale = appUILocale; 345 | this.appId = appId; 346 | this.isAppOnline = isAppOnline; 347 | this.appSkinInfo = appSkinInfo; 348 | } 349 | 350 | /** 351 | * @class HostCapabilities 352 | * Stores information about the host capabilities. 353 | * 354 | * @param EXTENDED_PANEL_MENU True if the application supports panel menu. 355 | * @param EXTENDED_PANEL_ICONS True if the application supports panel icon. 356 | * @param DELEGATE_APE_ENGINE True if the application supports delegated APE engine. 357 | * @param SUPPORT_HTML_EXTENSIONS True if the application supports HTML extensions. 358 | * @param DISABLE_FLASH_EXTENSIONS True if the application disables FLASH extensions. 359 | * 360 | * @return A new \c HostCapabilities object. 361 | */ 362 | function HostCapabilities(EXTENDED_PANEL_MENU, EXTENDED_PANEL_ICONS, DELEGATE_APE_ENGINE, SUPPORT_HTML_EXTENSIONS, DISABLE_FLASH_EXTENSIONS) { 363 | this.EXTENDED_PANEL_MENU = EXTENDED_PANEL_MENU; 364 | this.EXTENDED_PANEL_ICONS = EXTENDED_PANEL_ICONS; 365 | this.DELEGATE_APE_ENGINE = DELEGATE_APE_ENGINE; 366 | this.SUPPORT_HTML_EXTENSIONS = SUPPORT_HTML_EXTENSIONS; 367 | this.DISABLE_FLASH_EXTENSIONS = DISABLE_FLASH_EXTENSIONS; // Since 5.0.0 368 | } 369 | 370 | /** 371 | * @class ApiVersion 372 | * Stores current api version. 373 | * 374 | * Since 4.2.0 375 | * 376 | * @param major The major version 377 | * @param minor The minor version. 378 | * @param micro The micro version. 379 | * 380 | * @return ApiVersion object. 381 | */ 382 | function ApiVersion(major, minor, micro) { 383 | this.major = major; 384 | this.minor = minor; 385 | this.micro = micro; 386 | } 387 | 388 | /** 389 | * @class MenuItemStatus 390 | * Stores flyout menu item status 391 | * 392 | * Since 5.2.0 393 | * 394 | * @param menuItemLabel The menu item label. 395 | * @param enabled True if user wants to enable the menu item. 396 | * @param checked True if user wants to check the menu item. 397 | * 398 | * @return MenuItemStatus object. 399 | */ 400 | function MenuItemStatus(menuItemLabel, enabled, checked) { 401 | this.menuItemLabel = menuItemLabel; 402 | this.enabled = enabled; 403 | this.checked = checked; 404 | } 405 | 406 | /** 407 | * @class ContextMenuItemStatus 408 | * Stores the status of the context menu item. 409 | * 410 | * Since 5.2.0 411 | * 412 | * @param menuItemID The menu item id. 413 | * @param enabled True if user wants to enable the menu item. 414 | * @param checked True if user wants to check the menu item. 415 | * 416 | * @return MenuItemStatus object. 417 | */ 418 | function ContextMenuItemStatus(menuItemID, enabled, checked) { 419 | this.menuItemID = menuItemID; 420 | this.enabled = enabled; 421 | this.checked = checked; 422 | } 423 | //------------------------------ CSInterface ---------------------------------- 424 | 425 | /** 426 | * @class CSInterface 427 | * This is the entry point to the CEP extensibility infrastructure. 428 | * Instantiate this object and use it to: 429 | * 434 | * 435 | * @return A new \c CSInterface object 436 | */ 437 | function CSInterface() { 438 | } 439 | 440 | /** 441 | * User can add this event listener to handle native application theme color changes. 442 | * Callback function gives extensions ability to fine-tune their theme color after the 443 | * global theme color has been changed. 444 | * The callback function should be like below: 445 | * 446 | * @example 447 | * // event is a CSEvent object, but user can ignore it. 448 | * function OnAppThemeColorChanged(event) 449 | * { 450 | * // Should get a latest HostEnvironment object from application. 451 | * var skinInfo = JSON.parse(window.__adobe_cep__.getHostEnvironment()).appSkinInfo; 452 | * // Gets the style information such as color info from the skinInfo, 453 | * // and redraw all UI controls of your extension according to the style info. 454 | * } 455 | */ 456 | CSInterface.THEME_COLOR_CHANGED_EVENT = "com.adobe.csxs.events.ThemeColorChanged"; 457 | 458 | /** The host environment data object. */ 459 | CSInterface.prototype.hostEnvironment = window.__adobe_cep__ ? JSON.parse(window.__adobe_cep__.getHostEnvironment()) : null; 460 | 461 | /** Retrieves information about the host environment in which the 462 | * extension is currently running. 463 | * 464 | * @return A \c #HostEnvironment object. 465 | */ 466 | CSInterface.prototype.getHostEnvironment = function () { 467 | this.hostEnvironment = JSON.parse(window.__adobe_cep__.getHostEnvironment()); 468 | return this.hostEnvironment; 469 | }; 470 | 471 | /** Closes this extension. */ 472 | CSInterface.prototype.closeExtension = function () { 473 | window.__adobe_cep__.closeExtension(); 474 | }; 475 | 476 | /** 477 | * Retrieves a path for which a constant is defined in the system. 478 | * 479 | * @param pathType The path-type constant defined in \c #SystemPath , 480 | * 481 | * @return The platform-specific system path string. 482 | */ 483 | CSInterface.prototype.getSystemPath = function (pathType) { 484 | var path = decodeURI(window.__adobe_cep__.getSystemPath(pathType)); 485 | var OSVersion = this.getOSInformation(); 486 | if (OSVersion.indexOf("Windows") >= 0) { 487 | path = path.replace("file:///", ""); 488 | } 489 | else if (OSVersion.indexOf("Mac") >= 0) { 490 | path = path.replace("file://", ""); 491 | } 492 | return path; 493 | }; 494 | 495 | /** 496 | * Evaluates a JavaScript script, which can use the JavaScript DOM 497 | * of the host application. 498 | * 499 | * @param script The JavaScript script. 500 | * @param callback Optional. A callback function that receives the result of execution. 501 | * If execution fails, the callback function receives the error message \c EvalScript_ErrMessage. 502 | */ 503 | CSInterface.prototype.evalScript = function (script, callback) { 504 | if (callback === null || callback === undefined) { 505 | callback = function (result) { }; 506 | } 507 | window.__adobe_cep__.evalScript(script, callback); 508 | }; 509 | 510 | /** 511 | * Retrieves the unique identifier of the application. 512 | * in which the extension is currently running. 513 | * 514 | * @return The unique ID string. 515 | */ 516 | CSInterface.prototype.getApplicationID = function () { 517 | var appId = this.hostEnvironment.appId; 518 | return appId; 519 | }; 520 | 521 | /** 522 | * Retrieves host capability information for the application 523 | * in which the extension is currently running. 524 | * 525 | * @return A \c #HostCapabilities object. 526 | */ 527 | CSInterface.prototype.getHostCapabilities = function () { 528 | var hostCapabilities = JSON.parse(window.__adobe_cep__.getHostCapabilities()); 529 | return hostCapabilities; 530 | }; 531 | 532 | /** 533 | * Triggers a CEP event programmatically. Yoy can use it to dispatch 534 | * an event of a predefined type, or of a type you have defined. 535 | * 536 | * @param event A \c CSEvent object. 537 | */ 538 | CSInterface.prototype.dispatchEvent = function (event) { 539 | if (typeof event.data == "object") { 540 | event.data = JSON.stringify(event.data); 541 | } 542 | 543 | window.__adobe_cep__.dispatchEvent(event); 544 | }; 545 | 546 | /** 547 | * Registers an interest in a CEP event of a particular type, and 548 | * assigns an event handler. 549 | * The event infrastructure notifies your extension when events of this type occur, 550 | * passing the event object to the registered handler function. 551 | * 552 | * @param type The name of the event type of interest. 553 | * @param listener The JavaScript handler function or method. 554 | * @param obj Optional, the object containing the handler method, if any. 555 | * Default is null. 556 | */ 557 | CSInterface.prototype.addEventListener = function (type, listener, obj) { 558 | window.__adobe_cep__.addEventListener(type, listener, obj); 559 | }; 560 | 561 | /** 562 | * Removes a registered event listener. 563 | * 564 | * @param type The name of the event type of interest. 565 | * @param listener The JavaScript handler function or method that was registered. 566 | * @param obj Optional, the object containing the handler method, if any. 567 | * Default is null. 568 | */ 569 | CSInterface.prototype.removeEventListener = function (type, listener, obj) { 570 | window.__adobe_cep__.removeEventListener(type, listener, obj); 571 | }; 572 | 573 | /** 574 | * Loads and launches another extension, or activates the extension if it is already loaded. 575 | * 576 | * @param extensionId The extension's unique identifier. 577 | * @param startupParams Not currently used, pass "". 578 | * 579 | * @example 580 | * To launch the extension "help" with ID "HLP" from this extension, call: 581 | * requestOpenExtension("HLP", ""); 582 | * 583 | */ 584 | CSInterface.prototype.requestOpenExtension = function (extensionId, params) { 585 | window.__adobe_cep__.requestOpenExtension(extensionId, params); 586 | }; 587 | 588 | /** 589 | * Retrieves the list of extensions currently loaded in the current host application. 590 | * The extension list is initialized once, and remains the same during the lifetime 591 | * of the CEP session. 592 | * 593 | * @param extensionIds Optional, an array of unique identifiers for extensions of interest. 594 | * If omitted, retrieves data for all extensions. 595 | * 596 | * @return Zero or more \c #Extension objects. 597 | */ 598 | CSInterface.prototype.getExtensions = function (extensionIds) { 599 | var extensionIdsStr = JSON.stringify(extensionIds); 600 | var extensionsStr = window.__adobe_cep__.getExtensions(extensionIdsStr); 601 | 602 | var extensions = JSON.parse(extensionsStr); 603 | return extensions; 604 | }; 605 | 606 | /** 607 | * Retrieves network-related preferences. 608 | * 609 | * @return A JavaScript object containing network preferences. 610 | */ 611 | CSInterface.prototype.getNetworkPreferences = function () { 612 | var result = window.__adobe_cep__.getNetworkPreferences(); 613 | var networkPre = JSON.parse(result); 614 | 615 | return networkPre; 616 | }; 617 | 618 | /** 619 | * Initializes the resource bundle for this extension with property values 620 | * for the current application and locale. 621 | * To support multiple locales, you must define a property file for each locale, 622 | * containing keyed display-string values for that locale. 623 | * See localization documentation for Extension Builder and related products. 624 | * 625 | * Keys can be in the 626 | * form key.value="localized string", for use in HTML text elements. 627 | * For example, in this input element, the localized \c key.value string is displayed 628 | * instead of the empty \c value string: 629 | * 630 | * 631 | * 632 | * @return An object containing the resource bundle information. 633 | */ 634 | CSInterface.prototype.initResourceBundle = function () { 635 | var resourceBundle = JSON.parse(window.__adobe_cep__.initResourceBundle()); 636 | var resElms = document.querySelectorAll('[data-locale]'); 637 | for (var n = 0; n < resElms.length; n++) { 638 | var resEl = resElms[n]; 639 | // Get the resource key from the element. 640 | var resKey = resEl.getAttribute('data-locale'); 641 | if (resKey) { 642 | // Get all the resources that start with the key. 643 | for (var key in resourceBundle) { 644 | if (key.indexOf(resKey) === 0) { 645 | var resValue = resourceBundle[key]; 646 | if (key.length == resKey.length) { 647 | resEl.innerHTML = resValue; 648 | } 649 | else if ('.' == key.charAt(resKey.length)) { 650 | var attrKey = key.substring(resKey.length + 1); 651 | resEl[attrKey] = resValue; 652 | } 653 | } 654 | } 655 | } 656 | } 657 | return resourceBundle; 658 | }; 659 | 660 | /** 661 | * Writes installation information to a file. 662 | * 663 | * @return The file path. 664 | */ 665 | CSInterface.prototype.dumpInstallationInfo = function () { 666 | return window.__adobe_cep__.dumpInstallationInfo(); 667 | }; 668 | 669 | /** 670 | * Retrieves version information for the current Operating System, 671 | * See http://www.useragentstring.com/pages/Chrome/ for Chrome \c navigator.userAgent values. 672 | * 673 | * @return A string containing the OS version, or "unknown Operation System". 674 | * If user customizes the User Agent by setting CEF command parameter "--user-agent", only 675 | * "Mac OS X" or "Windows" will be returned. 676 | */ 677 | CSInterface.prototype.getOSInformation = function () { 678 | var userAgent = navigator.userAgent; 679 | 680 | if ((navigator.platform == "Win32") || (navigator.platform == "Windows")) { 681 | var winVersion = "Windows"; 682 | var winBit = ""; 683 | if (userAgent.indexOf("Windows") > -1) { 684 | if (userAgent.indexOf("Windows NT 5.0") > -1) { 685 | winVersion = "Windows 2000"; 686 | } 687 | else if (userAgent.indexOf("Windows NT 5.1") > -1) { 688 | winVersion = "Windows XP"; 689 | } 690 | else if (userAgent.indexOf("Windows NT 5.2") > -1) { 691 | winVersion = "Windows Server 2003"; 692 | } 693 | else if (userAgent.indexOf("Windows NT 6.0") > -1) { 694 | winVersion = "Windows Vista"; 695 | } 696 | else if (userAgent.indexOf("Windows NT 6.1") > -1) { 697 | winVersion = "Windows 7"; 698 | } 699 | else if (userAgent.indexOf("Windows NT 6.2") > -1) { 700 | winVersion = "Windows 8"; 701 | } 702 | else if (userAgent.indexOf("Windows NT 6.3") > -1) { 703 | winVersion = "Windows 8.1"; 704 | } 705 | else if (userAgent.indexOf("Windows NT 10") > -1) { 706 | winVersion = "Windows 10"; 707 | } 708 | 709 | if (userAgent.indexOf("WOW64") > -1 || userAgent.indexOf("Win64") > -1) { 710 | winBit = " 64-bit"; 711 | } 712 | else { 713 | winBit = " 32-bit"; 714 | } 715 | } 716 | 717 | return winVersion + winBit; 718 | } 719 | else if ((navigator.platform == "MacIntel") || (navigator.platform == "Macintosh")) { 720 | var result = "Mac OS X"; 721 | 722 | if (userAgent.indexOf("Mac OS X") > -1) { 723 | result = userAgent.substring(userAgent.indexOf("Mac OS X"), userAgent.indexOf(")")); 724 | result = result.replace(/_/g, "."); 725 | } 726 | 727 | return result; 728 | } 729 | 730 | return "Unknown Operation System"; 731 | }; 732 | 733 | /** 734 | * Opens a page in the default system browser. 735 | * 736 | * Since 4.2.0 737 | * 738 | * @param url The URL of the page/file to open, or the email address. 739 | * Must use HTTP/HTTPS/file/mailto protocol. For example: 740 | * "http://www.adobe.com" 741 | * "https://github.com" 742 | * "file:///C:/log.txt" 743 | * "mailto:test@adobe.com" 744 | * 745 | * @return One of these error codes:\n 746 | * \n 752 | */ 753 | CSInterface.prototype.openURLInDefaultBrowser = function (url) { 754 | return cep.util.openURLInDefaultBrowser(url); 755 | }; 756 | 757 | /** 758 | * Retrieves extension ID. 759 | * 760 | * Since 4.2.0 761 | * 762 | * @return extension ID. 763 | */ 764 | CSInterface.prototype.getExtensionID = function () { 765 | return window.__adobe_cep__.getExtensionId(); 766 | }; 767 | 768 | /** 769 | * Retrieves the scale factor of screen. 770 | * On Windows platform, the value of scale factor might be different from operating system's scale factor, 771 | * since host application may use its self-defined scale factor. 772 | * 773 | * Since 4.2.0 774 | * 775 | * @return One of the following float number. 776 | * \n 781 | */ 782 | CSInterface.prototype.getScaleFactor = function () { 783 | return window.__adobe_cep__.getScaleFactor(); 784 | }; 785 | 786 | /** 787 | * Retrieves the scale factor of Monitor. 788 | * 789 | * Since 8.5.0 790 | * 791 | * @return value >= 1.0f 792 | * only available for windows machine 793 | */ 794 | if (navigator.appVersion.toLowerCase().indexOf("windows") >= 0) { 795 | CSInterface.prototype.getMonitorScaleFactor = function () { 796 | return window.__adobe_cep__.getMonitorScaleFactor(); 797 | }; 798 | } 799 | 800 | /** 801 | * Set a handler to detect any changes of scale factor. This only works on Mac. 802 | * 803 | * Since 4.2.0 804 | * 805 | * @param handler The function to be called when scale factor is changed. 806 | * 807 | */ 808 | CSInterface.prototype.setScaleFactorChangedHandler = function (handler) { 809 | window.__adobe_cep__.setScaleFactorChangedHandler(handler); 810 | }; 811 | 812 | /** 813 | * Retrieves current API version. 814 | * 815 | * Since 4.2.0 816 | * 817 | * @return ApiVersion object. 818 | * 819 | */ 820 | CSInterface.prototype.getCurrentApiVersion = function () { 821 | var apiVersion = JSON.parse(window.__adobe_cep__.getCurrentApiVersion()); 822 | return apiVersion; 823 | }; 824 | 825 | /** 826 | * Set panel flyout menu by an XML. 827 | * 828 | * Since 5.2.0 829 | * 830 | * Register a callback function for "com.adobe.csxs.events.flyoutMenuClicked" to get notified when a 831 | * menu item is clicked. 832 | * The "data" attribute of event is an object which contains "menuId" and "menuName" attributes. 833 | * 834 | * Register callback functions for "com.adobe.csxs.events.flyoutMenuOpened" and "com.adobe.csxs.events.flyoutMenuClosed" 835 | * respectively to get notified when flyout menu is opened or closed. 836 | * 837 | * @param menu A XML string which describes menu structure. 838 | * An example menu XML: 839 | * 840 | * 841 | * 842 | * 843 | * 844 | * 845 | * 846 | * 847 | * 848 | * 849 | * 850 | * 851 | */ 852 | CSInterface.prototype.setPanelFlyoutMenu = function (menu) { 853 | if ("string" != typeof menu) { 854 | return; 855 | } 856 | 857 | window.__adobe_cep__.invokeSync("setPanelFlyoutMenu", menu); 858 | }; 859 | 860 | /** 861 | * Updates a menu item in the extension window's flyout menu, by setting the enabled 862 | * and selection status. 863 | * 864 | * Since 5.2.0 865 | * 866 | * @param menuItemLabel The menu item label. 867 | * @param enabled True to enable the item, false to disable it (gray it out). 868 | * @param checked True to select the item, false to deselect it. 869 | * 870 | * @return false when the host application does not support this functionality (HostCapabilities.EXTENDED_PANEL_MENU is false). 871 | * Fails silently if menu label is invalid. 872 | * 873 | * @see HostCapabilities.EXTENDED_PANEL_MENU 874 | */ 875 | CSInterface.prototype.updatePanelMenuItem = function (menuItemLabel, enabled, checked) { 876 | var ret = false; 877 | if (this.getHostCapabilities().EXTENDED_PANEL_MENU) { 878 | var itemStatus = new MenuItemStatus(menuItemLabel, enabled, checked); 879 | ret = window.__adobe_cep__.invokeSync("updatePanelMenuItem", JSON.stringify(itemStatus)); 880 | } 881 | return ret; 882 | }; 883 | 884 | 885 | /** 886 | * Set context menu by XML string. 887 | * 888 | * Since 5.2.0 889 | * 890 | * There are a number of conventions used to communicate what type of menu item to create and how it should be handled. 891 | * - an item without menu ID or menu name is disabled and is not shown. 892 | * - if the item name is "---" (three hyphens) then it is treated as a separator. The menu ID in this case will always be NULL. 893 | * - Checkable attribute takes precedence over Checked attribute. 894 | * - a PNG icon. For optimal display results please supply a 16 x 16px icon as larger dimensions will increase the size of the menu item. 895 | The Chrome extension contextMenus API was taken as a reference. 896 | https://developer.chrome.com/extensions/contextMenus 897 | * - the items with icons and checkable items cannot coexist on the same menu level. The former take precedences over the latter. 898 | * 899 | * @param menu A XML string which describes menu structure. 900 | * @param callback The callback function which is called when a menu item is clicked. The only parameter is the returned ID of clicked menu item. 901 | * 902 | * @description An example menu XML: 903 | * 904 | * 905 | * 906 | * 907 | * 908 | * 909 | * 910 | * 911 | * 912 | * 913 | * 914 | */ 915 | CSInterface.prototype.setContextMenu = function (menu, callback) { 916 | if ("string" != typeof menu) { 917 | return; 918 | } 919 | 920 | window.__adobe_cep__.invokeAsync("setContextMenu", menu, callback); 921 | }; 922 | 923 | /** 924 | * Set context menu by JSON string. 925 | * 926 | * Since 6.0.0 927 | * 928 | * There are a number of conventions used to communicate what type of menu item to create and how it should be handled. 929 | * - an item without menu ID or menu name is disabled and is not shown. 930 | * - if the item label is "---" (three hyphens) then it is treated as a separator. The menu ID in this case will always be NULL. 931 | * - Checkable attribute takes precedence over Checked attribute. 932 | * - a PNG icon. For optimal display results please supply a 16 x 16px icon as larger dimensions will increase the size of the menu item. 933 | The Chrome extension contextMenus API was taken as a reference. 934 | * - the items with icons and checkable items cannot coexist on the same menu level. The former take precedences over the latter. 935 | https://developer.chrome.com/extensions/contextMenus 936 | * 937 | * @param menu A JSON string which describes menu structure. 938 | * @param callback The callback function which is called when a menu item is clicked. The only parameter is the returned ID of clicked menu item. 939 | * 940 | * @description An example menu JSON: 941 | * 942 | * { 943 | * "menu": [ 944 | * { 945 | * "id": "menuItemId1", 946 | * "label": "testExample1", 947 | * "enabled": true, 948 | * "checkable": true, 949 | * "checked": false, 950 | * "icon": "./image/small_16X16.png" 951 | * }, 952 | * { 953 | * "id": "menuItemId2", 954 | * "label": "testExample2", 955 | * "menu": [ 956 | * { 957 | * "id": "menuItemId2-1", 958 | * "label": "testExample2-1", 959 | * "menu": [ 960 | * { 961 | * "id": "menuItemId2-1-1", 962 | * "label": "testExample2-1-1", 963 | * "enabled": false, 964 | * "checkable": true, 965 | * "checked": true 966 | * } 967 | * ] 968 | * }, 969 | * { 970 | * "id": "menuItemId2-2", 971 | * "label": "testExample2-2", 972 | * "enabled": true, 973 | * "checkable": true, 974 | * "checked": true 975 | * } 976 | * ] 977 | * }, 978 | * { 979 | * "label": "---" 980 | * }, 981 | * { 982 | * "id": "menuItemId3", 983 | * "label": "testExample3", 984 | * "enabled": false, 985 | * "checkable": true, 986 | * "checked": false 987 | * } 988 | * ] 989 | * } 990 | * 991 | */ 992 | CSInterface.prototype.setContextMenuByJSON = function (menu, callback) { 993 | if ("string" != typeof menu) { 994 | return; 995 | } 996 | 997 | window.__adobe_cep__.invokeAsync("setContextMenuByJSON", menu, callback); 998 | }; 999 | 1000 | /** 1001 | * Updates a context menu item by setting the enabled and selection status. 1002 | * 1003 | * Since 5.2.0 1004 | * 1005 | * @param menuItemID The menu item ID. 1006 | * @param enabled True to enable the item, false to disable it (gray it out). 1007 | * @param checked True to select the item, false to deselect it. 1008 | */ 1009 | CSInterface.prototype.updateContextMenuItem = function (menuItemID, enabled, checked) { 1010 | var itemStatus = new ContextMenuItemStatus(menuItemID, enabled, checked); 1011 | ret = window.__adobe_cep__.invokeSync("updateContextMenuItem", JSON.stringify(itemStatus)); 1012 | }; 1013 | 1014 | /** 1015 | * Get the visibility status of an extension window. 1016 | * 1017 | * Since 6.0.0 1018 | * 1019 | * @return true if the extension window is visible; false if the extension window is hidden. 1020 | */ 1021 | CSInterface.prototype.isWindowVisible = function () { 1022 | return window.__adobe_cep__.invokeSync("isWindowVisible", ""); 1023 | }; 1024 | 1025 | /** 1026 | * Resize extension's content to the specified dimensions. 1027 | * 1. Works with modal and modeless extensions in all Adobe products. 1028 | * 2. Extension's manifest min/max size constraints apply and take precedence. 1029 | * 3. For panel extensions 1030 | * 3.1 This works in all Adobe products except: 1031 | * * Premiere Pro 1032 | * * Prelude 1033 | * * After Effects 1034 | * 3.2 When the panel is in certain states (especially when being docked), 1035 | * it will not change to the desired dimensions even when the 1036 | * specified size satisfies min/max constraints. 1037 | * 1038 | * Since 6.0.0 1039 | * 1040 | * @param width The new width 1041 | * @param height The new height 1042 | */ 1043 | CSInterface.prototype.resizeContent = function (width, height) { 1044 | window.__adobe_cep__.resizeContent(width, height); 1045 | }; 1046 | 1047 | /** 1048 | * Register the invalid certificate callback for an extension. 1049 | * This callback will be triggered when the extension tries to access the web site that contains the invalid certificate on the main frame. 1050 | * But if the extension does not call this function and tries to access the web site containing the invalid certificate, a default error page will be shown. 1051 | * 1052 | * Since 6.1.0 1053 | * 1054 | * @param callback the callback function 1055 | */ 1056 | CSInterface.prototype.registerInvalidCertificateCallback = function (callback) { 1057 | return window.__adobe_cep__.registerInvalidCertificateCallback(callback); 1058 | }; 1059 | 1060 | /** 1061 | * Register an interest in some key events to prevent them from being sent to the host application. 1062 | * 1063 | * This function works with modeless extensions and panel extensions. 1064 | * Generally all the key events will be sent to the host application for these two extensions if the current focused element 1065 | * is not text input or dropdown, 1066 | * If you want to intercept some key events and want them to be handled in the extension, please call this function 1067 | * in advance to prevent them being sent to the host application. 1068 | * 1069 | * Since 6.1.0 1070 | * 1071 | * @param keyEventsInterest A JSON string describing those key events you are interested in. A null object or 1072 | an empty string will lead to removing the interest 1073 | * 1074 | * This JSON string should be an array, each object has following keys: 1075 | * 1076 | * keyCode: [Required] represents an OS system dependent virtual key code identifying 1077 | * the unmodified value of the pressed key. 1078 | * ctrlKey: [optional] a Boolean that indicates if the control key was pressed (true) or not (false) when the event occurred. 1079 | * altKey: [optional] a Boolean that indicates if the alt key was pressed (true) or not (false) when the event occurred. 1080 | * shiftKey: [optional] a Boolean that indicates if the shift key was pressed (true) or not (false) when the event occurred. 1081 | * metaKey: [optional] (Mac Only) a Boolean that indicates if the Meta key was pressed (true) or not (false) when the event occurred. 1082 | * On Macintosh keyboards, this is the command key. To detect Windows key on Windows, please use keyCode instead. 1083 | * An example JSON string: 1084 | * 1085 | * [ 1086 | * { 1087 | * "keyCode": 48 1088 | * }, 1089 | * { 1090 | * "keyCode": 123, 1091 | * "ctrlKey": true 1092 | * }, 1093 | * { 1094 | * "keyCode": 123, 1095 | * "ctrlKey": true, 1096 | * "metaKey": true 1097 | * } 1098 | * ] 1099 | * 1100 | */ 1101 | CSInterface.prototype.registerKeyEventsInterest = function (keyEventsInterest) { 1102 | return window.__adobe_cep__.registerKeyEventsInterest(keyEventsInterest); 1103 | }; 1104 | 1105 | /** 1106 | * Set the title of the extension window. 1107 | * This function works with modal and modeless extensions in all Adobe products, and panel extensions in Photoshop, InDesign, InCopy, Illustrator, Flash Pro and Dreamweaver. 1108 | * 1109 | * Since 6.1.0 1110 | * 1111 | * @param title The window title. 1112 | */ 1113 | CSInterface.prototype.setWindowTitle = function (title) { 1114 | window.__adobe_cep__.invokeSync("setWindowTitle", title); 1115 | }; 1116 | 1117 | /** 1118 | * Get the title of the extension window. 1119 | * This function works with modal and modeless extensions in all Adobe products, and panel extensions in Photoshop, InDesign, InCopy, Illustrator, Flash Pro and Dreamweaver. 1120 | * 1121 | * Since 6.1.0 1122 | * 1123 | * @return The window title. 1124 | */ 1125 | CSInterface.prototype.getWindowTitle = function () { 1126 | return window.__adobe_cep__.invokeSync("getWindowTitle", ""); 1127 | }; -------------------------------------------------------------------------------- /client/libs/events.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | var csInterface = new CSInterface(); 5 | 6 | csInterface.addEventListener('console', function(evt) { 7 | console.log('Host > ' + evt.data); 8 | }); 9 | 10 | csInterface.addEventListener('mighty.rollcall', function(evt) { 11 | dispatchEvent('mighty.rollanswer', extFolder()) 12 | }); 13 | 14 | function dispatchEvent(name, data) { 15 | var event = new CSEvent(name, 'APPLICATION'); 16 | event.data = data; 17 | csInterface.dispatchEvent(event); 18 | } 19 | 20 | }()); 21 | -------------------------------------------------------------------------------- /client/libs/menus.js: -------------------------------------------------------------------------------- 1 | var csInterface = new CSInterface(); 2 | // var isFlipped = false; 3 | 4 | var menu_FlyoutXML = ' \ 5 | \ 6 | '; 7 | // \ 8 | // \ 9 | // \ 10 | 11 | csInterface.setPanelFlyoutMenu(menu_FlyoutXML); 12 | csInterface.addEventListener("com.adobe.csxs.events.flyoutMenuClicked", setPanelCallback); 13 | 14 | 15 | function setPanelCallback(event) { 16 | if (event.data.menuId == "refresh") { 17 | location.reload(); 18 | } 19 | } 20 | 21 | // var menu_ContextXML = ' \ 22 | // \ 23 | // \ 24 | // \ 25 | // \ 26 | // '; 27 | 28 | // csInterface.setContextMenu(menu_ContextXML, setContextMenuCallback); 29 | 30 | // function setContextMenuCallback(event) { 31 | // if (event == "refresh") { 32 | // location.reload(); 33 | // } else if (event == "width") { 34 | // alert(window.innerWidth); 35 | // } else if (event === 'resize') { 36 | // // csInterface.resizeContent(200, 200) 37 | // } else { 38 | // console.log(event); 39 | // } 40 | // } 41 | -------------------------------------------------------------------------------- /client/libs/preStyle.css: -------------------------------------------------------------------------------- 1 | :root { 2 | font-size: 12px; 3 | --quad: cubic-bezier(0.48, 0.04, 0.52, 0.96); 4 | --quart: cubic-bezier(0.76, 0.00, 0.24, 1.00); 5 | --quint: cubic-bezier(0.84, 0.00, 0.16, 1.00); 6 | --color-bg: #323232; 7 | --color-scroll: #2a2a2a; 8 | --color-scroll-thumb: #3e3e3e; 9 | --color-scroll-thumb-hover: #525252; 10 | --color-ui-idle: #3d3d3d; 11 | --color-ui-active: #4b4b4b; 12 | --color-ui-ultra: #797979; 13 | --color-text-idle: #b9b9b9; 14 | --color-text-hover: #c0c0c0; 15 | --color-text-active: #d0d0d0; 16 | --color-ui-selected: #46a0f5; 17 | --color-ui-border: #3e3e3e; 18 | --color-ui-hover: #292929; 19 | --color-text: #a1a1a1; 20 | --font-size: .875rem; 21 | 22 | --color-R: #eb6d5a; 23 | --color-G: #66ff3b; 24 | --color-B: #3593eb; 25 | 26 | --panel-height: 50px; 27 | --panel-width: 50px; 28 | font-family: Rubik; 29 | color: #a1a1a1; 30 | margin: .5rem .25rem 0px .25rem; 31 | background-color: var(--color-bg); 32 | } 33 | 34 | svg { 35 | width: 100%; 36 | } 37 | 38 | ::-webkit-scrollbar { 39 | width: 10px; 40 | background: var(--color-scroll); 41 | } 42 | ::-webkit-scrollbar-thumb { 43 | width: var(--thumb-width); 44 | background: var(--color-scroll-thumb); 45 | border-radius: var(--scroll-radius); 46 | } 47 | ::-webkit-scrollbar-thumb:hover { 48 | background: var(--color-scroll-thumb-hover); 49 | } 50 | ::-webkit-scrollbar-corner, ::-webkit-scrollbar-resizer, ::-webkit-scrollbar-button { 51 | display: none; 52 | } 53 | 54 | /* ::-webkit-scrollbar-button { 55 | display: none; 56 | } */ 57 | 58 | 59 | #app { 60 | /* max-width: 50px; */ 61 | /* min-width: 30px; */ 62 | box-sizing: border-box; 63 | /* border: 2px solid red; */ 64 | /* height: var(--panel-height); */ 65 | width: 100%; 66 | } 67 | 68 | .visualizerModKeys { 69 | display: grid; 70 | grid-column-gap: .25rem; 71 | background-color: var(--color-ui-hover); 72 | } 73 | [class^="modKey"] { 74 | box-sizing: border-box; 75 | display: flex; 76 | justify-content: center; 77 | align-items: center; 78 | } 79 | .modKey-Shift-Active { background-color: var(--color-G);} 80 | .modKey-Ctrl-Active { background-color: var(--color-R);} 81 | .modKey-Alt-Active { background-color: var(--color-B);} 82 | 83 | 84 | /* @font-face { 85 | font-family: 'Swap-Font'; 86 | src: 87 | url('../../UI/fonts/Swap-Font.ttf?ehpwfl') format('truetype'), 88 | url('../../UI/fonts/Swap-Font.woff?ehpwfl') format('woff'), 89 | url('../../UI/fonts/Swap-Font.svg?ehpwfl#Swap-Font') format('svg'); 90 | font-weight: normal; 91 | font-style: normal; 92 | } */ 93 | 94 | .set { 95 | width: 100%; 96 | height: 2rem; 97 | font-size: 1.25rem; 98 | cursor: default; 99 | user-select: none; 100 | display: flex; 101 | justify-content: space-around; 102 | align-items: center; 103 | } 104 | 105 | /* icomoon font */ 106 | /* [class^="swap-icon-"], [class*=" swap-icon-"] { 107 | font-family: 'Swap-Font' !important; 108 | speak: none; 109 | font-style: normal; 110 | font-weight: normal; 111 | font-variant: normal; 112 | text-transform: none; 113 | line-height: 1; 114 | -webkit-font-smoothing: antialiased; 115 | } 116 | 117 | .swap-icon-swapH:before { 118 | content: "\e907"; 119 | } 120 | .swap-icon-swapV:before { 121 | content: "\e908"; 122 | } 123 | .swap-icon-artboard:before { 124 | content: "\e900"; 125 | } 126 | .swap-icon-doc:before { 127 | content: "\e901"; 128 | } 129 | .swap-icon-layer:before { 130 | content: "\e902"; 131 | } 132 | .swap-icon-selection:before { 133 | content: "\e903"; 134 | } 135 | .swap-icon-both:before { 136 | content: "\e904"; 137 | } 138 | .swap-icon-fill:before { 139 | content: "\e905"; 140 | } 141 | .swap-icon-stroke:before { 142 | content: "\e906"; 143 | } 144 | .swap-icon-close:before { 145 | content: "\e91f"; 146 | } 147 | .swap-icon-angleDown:before { 148 | content: "\e926"; 149 | } 150 | .swap-icon-angleLeft:before { 151 | content: "\e927"; 152 | } 153 | .swap-icon-angleRight:before { 154 | content: "\e928"; 155 | } 156 | .swap-icon-angleUp:before { 157 | content: "\e929"; 158 | } 159 | .swap-icon-angleUpDown:before { 160 | content: "\e915"; 161 | } */ 162 | 163 | /* Scrollbar */ 164 | ::-webkit-scrollbar { 165 | /* if none, uncomment: */ 166 | /* display: none */ 167 | background: #2A2A2A; 168 | } 169 | 170 | ::-webkit-scrollbar-thumb { 171 | background: #3E3E3E; 172 | border-radius: 20px; 173 | } 174 | 175 | ::-webkit-scrollbar-thumb:hover { 176 | background: #525252; 177 | } 178 | 179 | ::-webkit-scrollbar-button { 180 | display: none; 181 | } 182 | 183 | ::-webkit-scrollbar:single-button { 184 | /* margin: 1rem 0px; */ 185 | } 186 | 187 | ::-webkit-scrollbar-button:start { 188 | /* background-image: url('./scrollbarUpBtnAlt.png'); */ 189 | } 190 | 191 | ::-webkit-scrollbar-button:end { 192 | /* background-image: url('./scrollbarDownBtnAlt.png'); */ 193 | } 194 | 195 | ::-webkit-scrollbar-button:start:hover { 196 | /* background-image: url('./scrollbarUpBtnAltHover.png'); */ 197 | } 198 | 199 | ::-webkit-scrollbar-button:end:hover { 200 | /* background-image: url('./scrollbarDownBtnAltHover.png'); */ 201 | } 202 | -------------------------------------------------------------------------------- /client/libs/reference.js: -------------------------------------------------------------------------------- 1 | var csInterface = new CSInterface(); 2 | 3 | function extFolder() { 4 | var str = csInterface.getSystemPath(SystemPath.EXTENSION); 5 | var parent = str.substring(str.lastIndexOf('/') + 1, str.length); 6 | return parent; 7 | } 8 | 9 | function loadJSX(fileName) { 10 | var root = csInterface.getSystemPath(SystemPath.EXTENSION) + "/host/"; 11 | csInterface.evalScript('$.evalFile("' + root + fileName + '")'); 12 | } 13 | 14 | function loadUniversalJSXLibraries() { 15 | var libs = ["json2.jsx", "Console.jsx"]; 16 | var root = csInterface.getSystemPath(SystemPath.EXTENSION) + "/host/universal/"; 17 | for (var i = 0; i < libs.length; i++) 18 | csInterface.evalScript('$.evalFile("' + root + libs[i] + '")'); 19 | } 20 | 21 | /** 22 | * Premiere Pro Panel 23 | */ 24 | function toHex(color, delta) { 25 | function computeValue(value, delta) { 26 | var computedValue = !isNaN(delta) ? value + delta : value; 27 | if (computedValue < 0) { 28 | computedValue = 0; 29 | } else if (computedValue > 255) { 30 | computedValue = 255; 31 | } 32 | 33 | computedValue = Math.round(computedValue).toString(16); 34 | return computedValue.length == 1 ? "0" + computedValue : computedValue; 35 | } 36 | 37 | var hex = ""; 38 | if (color) { 39 | with (color) { 40 | hex = computeValue(red, delta) + computeValue(green, delta) + computeValue(blue, delta); 41 | }; 42 | } 43 | return "#" + hex; 44 | } 45 | 46 | // https://stackoverflow.com/a/11923973 47 | function rgbToHsl(c) { 48 | var r = c[0] / 255, g = c[1] / 255, b = c[2] / 255; 49 | var max = Math.max(r, g, b), min = Math.min(r, g, b); 50 | var h, s, l = (max + min) / 2; 51 | 52 | if (max == min) { 53 | h = s = 0; // achromatic 54 | } else { 55 | var d = max - min; 56 | s = l > 0.5 ? d / (2 - max - min) : d / (max + min); 57 | switch (max) { 58 | case r: h = (g - b) / d + (g < b ? 6 : 0); break; 59 | case g: h = (b - r) / d + 2; break; 60 | case b: h = (r - g) / d + 4; break; 61 | } 62 | h /= 6; 63 | } 64 | // return new Array(h, s, l); 65 | return new Array(h * 360, s * 100, l * 100); 66 | } 67 | 68 | // https://stackoverflow.com/a/44134328 69 | function hslToHex(h, s, l) { 70 | h /= 360; 71 | s /= 100; 72 | l /= 100; 73 | let r, g, b; 74 | if (s === 0) { 75 | r = g = b = l; // achromatic 76 | } else { 77 | const hue2rgb = (p, q, t) => { 78 | if (t < 0) t += 1; 79 | if (t > 1) t -= 1; 80 | if (t < 1 / 6) return p + (q - p) * 6 * t; 81 | if (t < 1 / 2) return q; 82 | if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6; 83 | return p; 84 | }; 85 | const q = l < 0.5 ? l * (1 + s) : l + s - l * s; 86 | const p = 2 * l - q; 87 | r = hue2rgb(p, q, h + 1 / 3); 88 | g = hue2rgb(p, q, h); 89 | b = hue2rgb(p, q, h - 1 / 3); 90 | } 91 | const toHex = x => { 92 | const hex = Math.round(x * 255).toString(16); 93 | return hex.length === 1 ? '0' + hex : hex; 94 | }; 95 | return `#${toHex(r)}${toHex(g)}${toHex(b)}`; 96 | } 97 | 98 | /// https://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb 99 | function componentToHex(c) { 100 | var hex = c.toString(16); 101 | return hex.length == 1 ? "0" + hex : hex; 102 | } 103 | 104 | function rgbToHex(r, g, b) { 105 | return componentToHex(r) + componentToHex(g) + componentToHex(b); 106 | } 107 | 108 | function hexToRgb(hex) { 109 | var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); 110 | return result ? { 111 | r: parseInt(result[1], 16), 112 | g: parseInt(result[2], 16), 113 | b: parseInt(result[3], 16) 114 | } : null; 115 | } 116 | -------------------------------------------------------------------------------- /client/libs/reset.css: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ 2 | v2.0 | 20110126 3 | License: none (public domain) 4 | */ 5 | 6 | html, body, div, span, applet, object, iframe, 7 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 8 | a, abbr, acronym, address, big, cite, code, 9 | del, dfn, em, img, ins, kbd, q, s, samp, 10 | small, strike, strong, sub, sup, tt, var, 11 | b, u, i, center, 12 | dl, dt, dd, ol, ul, li, 13 | fieldset, form, label, legend, 14 | table, caption, tbody, tfoot, thead, tr, th, td, 15 | article, aside, canvas, details, embed, 16 | figure, figcaption, footer, header, hgroup, 17 | menu, nav, output, ruby, section, summary, 18 | time, mark, audio, video { 19 | margin: 0; 20 | padding: 0; 21 | border: 0; 22 | font-size: 100%; 23 | font: inherit; 24 | vertical-align: baseline; 25 | } 26 | /* HTML5 display-role reset for older browsers */ 27 | article, aside, details, figcaption, figure, 28 | footer, header, hgroup, menu, nav, section { 29 | display: block; 30 | } 31 | body { 32 | line-height: 1; 33 | } 34 | ol, ul { 35 | list-style: none; 36 | } 37 | blockquote, q { 38 | quotes: none; 39 | } 40 | blockquote:before, blockquote:after, 41 | q:before, q:after { 42 | content: ''; 43 | content: none; 44 | } 45 | table { 46 | border-collapse: collapse; 47 | border-spacing: 0; 48 | } 49 | -------------------------------------------------------------------------------- /client/libs/v-outside-events.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * vue-outside-events @ 1.1.3 3 | * Nicholas Hutchind 4 | * 5 | * Vue directive to react to various events outside the current element 6 | * 7 | * License: MIT 8 | */ 9 | !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e["vue-outside-events"]=t()}(this,function(){"use strict";var e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},t=function(t,i){var n={};return n.directiveName=t,n.eventName=i,n.bind=function(n,o,u){var d=void 0!==console.error?console.error:console.log,r=null,v=void 0;if("function"!=typeof o.value)if("object"===e(o.value)&&o.value.hasOwnProperty("handler")&&"function"==typeof o.value.handler)r=o.value.handler,delete(v=Object.assign({},o.value)).handler;else{var a="["+t+"]: provided expression '"+o.expression+"' must be a function or an object containing a property named 'handler' that is a function.";u.context.name&&(a+="\nFound in component '"+u.context.name+"'"),d(a)}else r=o.value;var c=function(e){n.contains(e.target)||n===e.target||r(e,n,v)};n["__vueEventOutside__"+i]=c,document.addEventListener(i,c)},n.unbind=function(e,t){document.removeEventListener(i,e["__vueEventOutside__"+i]),e["__vueEventOutside__"+i]=null},n},i={directiveName:"event-outside",bind:function(t,i,n){var o=void 0!==console.error?console.error:console.log,u=void 0;if("object"!==e(i.value)||void 0===i.value.name||"string"!=typeof i.value.name||void 0===i.value.handler||"function"!=typeof i.value.handler){var d="[v-event-outside]: provided expression '"+i.expression+'\' must be an object containing a "name" string and a "handler" function.';return n.context.name&&(d+="\nFound in component '"+n.context.name+"'"),void o(d)}if(delete(u=Object.assign({},i.value)).name,delete u.handler,i.modifiers.jquery&&void 0===window.$&&void 0===window.jQuery){var r="[v-event-outside]: jQuery is not present in window.";return n.context.name&&(r+="\nFound in component '"+n.context.name+"'"),void o(r)}var v=function(e){t.contains(e.target)||t===e.target||i.value.handler(e,t,u)};t["__vueEventOutside__"+i.value.name]=v,i.modifiers.jquery?jQuery(document).on(i.value.name,v):document.addEventListener(i.value.name,v)},unbind:function(e,t){t.modifiers.jquery?jQuery(document).off(t.value.name,e["__vueEventOutside__"+t.value.name]):document.removeEventListener(t.value.name,e["__vueEventOutside__"+t.value.name]),e["__vueEventOutside__"+t.value.name]=null}},n=t("click-outside","click"),o=t("dblclick-outside","dblclick"),u=t("focus-outside","focusin"),d=t("blur-outside","focusout"),r=t("mousemove-outside","mousemove"),v=t("mousedown-outside","mousedown"),a=t("mouseup-outside","mouseup"),c=t("mouseover-outside","mouseover"),s=t("mouseout-outside","mouseout"),m=t("change-outside","change"),l=t("select-outside","select"),f=t("submit-outside","submit"),p=t("keydown-outside","keydown"),y=t("keypress-outside","keypress"),_=t("keyup-outside","keyup"),b={install:function(e){e.directive(n.directiveName,n),e.directive(o.directiveName,o),e.directive(u.directiveName,u),e.directive(d.directiveName,d),e.directive(r.directiveName,r),e.directive(v.directiveName,v),e.directive(a.directiveName,a),e.directive(c.directiveName,c),e.directive(s.directiveName,s),e.directive(m.directiveName,m),e.directive(l.directiveName,l),e.directive(f.directiveName,f),e.directive(p.directiveName,p),e.directive(y.directiveName,y),e.directive(y.directiveName,y),e.directive(_.directiveName,_),e.directive(i.directiveName,i)}};return"undefined"!=typeof window&&window.Vue&&window.Vue.use(b),b}); 10 | -------------------------------------------------------------------------------- /client/main.js: -------------------------------------------------------------------------------- 1 | var csInterface = new CSInterface(); 2 | loadUniversalJSXLibraries(); 3 | loadJSX(csInterface.hostEnvironment.appName + '/host.jsx'); 4 | window.Event = new Vue(); 5 | 6 | Vue.component('app', { 7 | template: ` 8 |
9 | 10 | 11 | 12 |
13 | `, 14 | data() { 15 | return { 16 | showFoot: false, 17 | } 18 | }, 19 | methods: { 20 | wakeApp(evt) { 21 | this.$root.wake(); 22 | }, 23 | sleepApp(evt) { 24 | this.$root.sleep(); 25 | Event.$emit('clearMods'); 26 | } 27 | } 28 | }) 29 | 30 | Vue.component('scribe', { 31 | template: ` 32 |
33 |
34 | 40 | 47 | 53 |
54 | `, 55 | data() { 56 | return { 57 | prefix: '', 58 | label: '', 59 | suffix: '', 60 | hide: false, 61 | msg: '', 62 | index: 0, 63 | placeholder: 'layer name', 64 | activeLayer: [], 65 | total: [], 66 | } 67 | }, 68 | methods: { 69 | getText(evt) { 70 | console.log(evt) 71 | }, 72 | travelUp() { 73 | // console.log('Travelling up...') 74 | csInterface.evalScript(`travelUpLayer()`); 75 | }, 76 | travelDown() { 77 | // console.log('Travelling down...') 78 | csInterface.evalScript(`travelDownLayer()`); 79 | }, 80 | updateSelection(layer) { 81 | // console.log(layer); 82 | this.activeLayer = layer; 83 | this.placeholder = layer[0]; 84 | this.index = layer[1]; 85 | this.label = layer[2]; 86 | if (!this.$root.isWake) 87 | this.clearFocus(); 88 | }, 89 | getClass() { 90 | var style = '' 91 | if (this.$root.isWake) { 92 | style = 'texter-input'; 93 | } else { 94 | style = 'texter-idle'; 95 | } 96 | return style; 97 | }, 98 | setFocus() { 99 | this.$nextTick(() => this.$refs.input.focus()); 100 | // this.checkPosition(); 101 | }, 102 | clearFocus() { 103 | this.$nextTick(() => this.$refs.input.blur()); 104 | // this.checkPosition(); 105 | }, 106 | submitTest(msg) { 107 | if (msg.length) { 108 | console.log(`Rename current layer to ${msg}`) 109 | this.msg = ''; 110 | csInterface.evalScript(`renameActiveLayer('${msg}')`) 111 | this.travelUp(); 112 | } else { 113 | this.travelUp(); 114 | } 115 | // Event.$emit('recheckSelection') 116 | }, 117 | autocomplete() { 118 | console.log(`Autocomplete ${msg} => ${placeholder}`) 119 | }, 120 | clearScribe() { 121 | this.msg = ''; 122 | } 123 | }, 124 | computed: { 125 | isWake: function() { 126 | return this.$root.isWake; 127 | }, 128 | showPrefix: function() { 129 | return this.$root.showPrefix; 130 | }, 131 | showSuffix: function() { 132 | return this.$root.showSuffix; 133 | } 134 | // placeholder: function () { 135 | // return `layer name`; 136 | // } 137 | }, 138 | mounted() { 139 | Event.$on('showTexter', this.displayTexter); 140 | Event.$on('clearScribe', this.clearScribe); 141 | Event.$on('clearScribe', this.clearScribe); 142 | Event.$on('updateSelection', this.updateSelection); 143 | Event.$on('TravelUp', this.travelUp); 144 | Event.$on('TravelDown', this.travelDown); 145 | Event.$on('suffixToggle', this.toggleSuffix); 146 | Event.$on('prefixToggle', this.togglePrefix); 147 | } 148 | }) 149 | 150 | Vue.component('scanner', { 151 | template: ` 152 |
153 | `, 154 | data() { 155 | return { 156 | isScanning: false, 157 | lastSelection: [], 158 | timer: { 159 | selection: null, 160 | }, 161 | } 162 | }, 163 | methods: { 164 | selectionRead(msg) { 165 | // msg is CSV of pageItem.index 166 | if (msg !== this.lastSelection) { 167 | console.log(msg); 168 | var result = []; 169 | if (!/./.test(msg)) { 170 | result = []; 171 | console.log(`Empty values`); 172 | } else if (/\;/.test(msg)) { 173 | result = msg.split(';'); 174 | // Event.$emit('updateSelection', result) 175 | // console.log(`Values are CSV`); 176 | } 177 | this.lastSelection = msg; 178 | Event.$emit('updateSelection', result) 179 | } 180 | }, 181 | selectionCheck() { 182 | var self = this; 183 | // console.log(this.$root.activeApp) 184 | if (this.$root.activeApp == 'ILST' || 'AEFT') 185 | csInterface.evalScript(`scanSelection()`, self.selectionRead) 186 | }, 187 | scanSelection(state) { 188 | var self = this; 189 | if (state) 190 | this.timer.selection = setInterval(self.selectionCheck, 250); 191 | }, 192 | stopSelectionScan() { 193 | clearInterval(this.timer.selection); 194 | }, 195 | toggleSelectionScan(e) { 196 | console.log('World?') 197 | this.isScanning = !this.isScanning; 198 | if (this.isScanning) { 199 | console.log('Currently scanning...') 200 | this.scanSelection(this.isScanning); 201 | } else { 202 | console.log('Not currently scanning') 203 | this.stopSelectionScan(); 204 | } 205 | // console.log(`Selection is ${this.isScanning}`); 206 | }, 207 | toggleScans() { 208 | console.log('Hello?') 209 | this.toggleSelectionScan() 210 | if (!this.isScanning) { 211 | this.stopSelectionScan(); 212 | } 213 | }, 214 | }, 215 | mounted() { 216 | var self = this; 217 | Event.$on('stopSelectionScan', self.stopSelectionScan); 218 | Event.$on('startSelectionScan', self.startSelectionScan); 219 | this.toggleScans(); 220 | } 221 | }) 222 | 223 | Vue.component('event-manager', { 224 | template: ` 225 |
232 |
233 |
234 | `, 235 | data() { 236 | return { 237 | activeList: [ 238 | { name: 'Ctrl' }, 239 | { name: 'Shift' }, 240 | { name: 'Alt' }, 241 | ], 242 | Shift: false, 243 | Ctrl: false, 244 | Alt: false, 245 | } 246 | }, 247 | mounted() { 248 | var self = this; 249 | this.activeMods(); 250 | }, 251 | methods: { 252 | activeMods() { 253 | var mirror = [], child = {}; 254 | if (this.Ctrl) { 255 | child = { name: 'Ctrl', key: 0 } 256 | mirror.push(child); 257 | } 258 | if (this.Shift) { 259 | child = { name: 'Shift', key: 1 } 260 | mirror.push(child); 261 | } 262 | if (this.Alt) { 263 | child = { name: 'Alt', key: 2 } 264 | mirror.push(child); 265 | } 266 | this.activeList = mirror; 267 | }, 268 | clearMods() { 269 | this.Shift = false, this.Alt = false, this.Ctrl = false; 270 | this.activeList = []; 271 | }, 272 | updateMods() { 273 | this.Ctrl = this.$root.Ctrl, this.Shift = this.$root.Shift, this.Alt = this.$root.Alt; 274 | this.activeMods(); 275 | }, 276 | onMouseOutside(e, el) { 277 | this.$root.parseModifiers(e); 278 | }, 279 | onClickOutside(e, el) { 280 | if (this.$root.showCrosshair) { 281 | Event.$emit('setMarker'); 282 | } 283 | }, 284 | onKeyDownOutside(e, el) { 285 | this.$root.parseModifiers(e); 286 | }, 287 | onKeyUpOutside(e, el) { 288 | if (e.key == 'ArrowDown') { 289 | Event.$emit('TravelUp'); 290 | } else if (e.key == 'ArrowUp') { 291 | Event.$emit('TravelDown'); 292 | } 293 | this.$root.parseModifiers(e); 294 | }, 295 | }, 296 | computed: { 297 | isDefault: function () { return this.$root.isDefault }, 298 | }, 299 | }) 300 | 301 | var app = new Vue({ 302 | el: '#app', 303 | data: { 304 | macOS: false, 305 | panelWidth: 100, 306 | panelHeight: 200, 307 | showPrefix: true, 308 | showSuffix: true, 309 | persistent: true, 310 | // storage: window.localStorage, 311 | activeApp: csInterface.hostEnvironment.appName, 312 | activeTheme: 'darkest', 313 | isWake: false, 314 | Shift: false, 315 | Ctrl: false, 316 | Alt: false, 317 | context: { 318 | menu: [ 319 | { id: "refresh", label: "Refresh panel", enabled: true, checkable: false, checked: false, }, 320 | // { id: "persistent", label: "Persistent Y/N", enabled: true, checkable: true, checked: true, }, 321 | { label: "---" }, 322 | { id: "prefix", label: "Show prefix", enabled: true, checkable: true, checked: true, }, 323 | { id: "suffix", label: "Show suffix", enabled: true, checkable: true, checked: true, }, 324 | { label: "---" }, 325 | { 326 | id: "autocorrect", label: "Autocorrect copies", menu: [ 327 | { id: "isauto", label: "Autocorrecting", enabled: true, checkable: true, checked: true, }, 328 | { label: "---" }, 329 | { id: "sequence", label: "As sequence", enabled: true, checkable: true, checked: true, ingroup: true }, 330 | { id: "blank", label: "Leave blank", enabled: true, checkable: true, checked: false, ingroup: true } ] 331 | }, 332 | ], 333 | }, 334 | }, 335 | computed: { 336 | menuString: function () { return JSON.stringify(this.context); }, 337 | isDefault: function () { 338 | var result = true; 339 | if ((this.Shift) | (this.Ctrl) | (this.Alt)) 340 | result = false; 341 | return result; 342 | }, 343 | }, 344 | mounted: function () { 345 | var self = this; 346 | if (navigator.platform.indexOf('Win') > -1) { this.macOS = false; } else if (navigator.platform.indexOf('Mac') > -1) { this.macOS = true; } 347 | // this.startStorage(); 348 | this.readStorage(); 349 | this.setContextMenu(); 350 | this.handleResize(null); 351 | window.addEventListener('resize', this.handleResize); 352 | Event.$on('modsUpdate', self.parseModifiers); 353 | Event.$on('updateStorage', self.updateStorage); 354 | csInterface.addEventListener('documentAfterActivate', self.reset); 355 | csInterface.addEventListener('applicationActive', self.activate); 356 | csInterface.addEventListener(CSInterface.THEME_COLOR_CHANGED_EVENT, self.appThemeChanged); 357 | this.appThemeChanged(); 358 | }, 359 | methods: { 360 | reset() { 361 | location.reload(); 362 | // console.log('reset'); 363 | }, 364 | activate() { 365 | console.log('activated'); 366 | }, 367 | togglePrefix(state) { 368 | // console.log(`Receiving state of prefix as ${state}`) 369 | this.showPrefix = state; 370 | }, 371 | toggleSuffix(state) { 372 | // console.log(`Receiving state of suffix as ${state}`) 373 | this.showSuffix = state; 374 | }, 375 | startStorage(storage) { 376 | storage.setItem('contextmenu', JSON.stringify(this.context.menu)); 377 | storage.setItem('persistent', JSON.stringify(false)); 378 | // storage.setItem('theme', 'darkest'); 379 | storage.setItem('prefix', this.showPrefix); 380 | storage.setItem('suffix', this.showSuffix); 381 | }, 382 | readStorage() { 383 | var storage = window.localStorage; 384 | if (!storage.length) { 385 | console.log('There was no pre-existing session data'); 386 | // this.startStorage(); 387 | // storage.setItem('appName', self.activeApp); 388 | } else { 389 | console.log('Detected previous session data'); 390 | this.context.menu = JSON.parse(storage.getItem('contextmenu')); 391 | this.persistent = JSON.parse(storage.getItem('persistent')); 392 | // this.activeTheme = storage.getItem('theme'); 393 | // this.activeApp = storage.getItem('appName'); 394 | // this.showPrefix = storage.getItem('prefix'); 395 | // this.showSuffix = storage.getItem('suffix'); 396 | } 397 | this.showPrefix = this.context.menu[2].checked; 398 | this.showSuffix = this.context.menu[3].checked; 399 | // if (!this.showPrefix) 400 | console.log(`${this.showPrefix} : ${this.showSuffix}`); 401 | }, 402 | updateStorage() { 403 | var storage = window.localStorage, self = this; 404 | storage.setItem('contextmenu', JSON.stringify(self.context.menu)); 405 | storage.setItem('persistent', JSON.stringify(self.persistent)); 406 | storage.setItem('theme', self.activeTheme); 407 | // storage.setItem('appName', self.activeApp); 408 | console.log(`Updating local storage: 409 | Persistent: ${this.persistent} 410 | Theme: ${this.activeTheme}`) 411 | }, 412 | appThemeChanged(event) { 413 | var skinInfo = JSON.parse(window.__adobe_cep__.getHostEnvironment()).appSkinInfo; 414 | this.findTheme(skinInfo); 415 | console.log(`Theme changed to ${this.activeTheme}`); 416 | this.updateStorage(); 417 | }, 418 | findTheme(appSkin) { 419 | // AE uses smooth gradients. Isolate the others apps from it 420 | if ((this.activeApp == 'ILST') || (this.activeApp == 'PHXS')) { 421 | if (toHex(appSkin.panelBackgroundColor.color) == '#f0f0f0') { 422 | this.activeTheme = 'lightest'; 423 | if (this.activeApp == 'ILST') { 424 | this.setCSS('color-scroll', '#fbfbfb'); 425 | this.setCSS('color-scroll-thumb', '#dcdcdc'); 426 | this.setCSS('color-scroll-thumb-hover', '#a6a6a6'); 427 | } else if (this.activeApp == 'PHXS') { 428 | this.setCSS('color-scroll', '#e3e3e3'); 429 | this.setCSS('color-scroll-thumb', '#bdbdbd'); 430 | this.setCSS('color-scroll-thumb-hover', '#bdbdbd'); 431 | } 432 | } else if (toHex(appSkin.panelBackgroundColor.color) == '#b8b8b8') { 433 | this.activeTheme = 'light'; 434 | if (this.activeApp == 'ILST') { 435 | this.setCSS('color-scroll', '#c4c4c4'); 436 | this.setCSS('color-scroll-thumb', '#a8a8a8'); 437 | this.setCSS('color-scroll-thumb-hover', '#7b7b7b'); 438 | } else if (this.activeApp == 'PHXS') { 439 | this.setCSS('color-scroll', '#ababab'); 440 | this.setCSS('color-scroll-thumb', '#858585'); 441 | this.setCSS('color-scroll-thumb-hover', '#858585'); 442 | } 443 | } else if (toHex(appSkin.panelBackgroundColor.color) == '#535353') { 444 | this.activeTheme = 'dark'; 445 | if (this.activeApp == 'ILST') { 446 | this.setCSS('color-scroll', '#4b4b4b'); 447 | this.setCSS('color-scroll-thumb', '#606060'); 448 | this.setCSS('color-scroll-thumb-hover', '#747474'); 449 | } else if (this.activeApp == 'PHXS') { 450 | this.setCSS('color-scroll', '#4a4a4a'); 451 | this.setCSS('color-scroll-thumb', '#696969'); 452 | this.setCSS('color-scroll-thumb-hover', '#696969'); 453 | } 454 | } else if (toHex(appSkin.panelBackgroundColor.color) == '#323232') { 455 | this.activeTheme = 'darkest'; 456 | if (this.activeApp == 'ILST') { 457 | this.setCSS('color-scroll', '#2a2a2a'); 458 | this.setCSS('color-scroll-thumb', '#383838'); 459 | this.setCSS('color-scroll-thumb-hover', '#525252'); 460 | } else if (this.activeApp == 'PHXS') { 461 | this.setCSS('color-scroll', '#292929'); 462 | this.setCSS('color-scroll-thumb', '#474747'); 463 | this.setCSS('color-scroll-thumb-hover', '#474747'); 464 | } 465 | } 466 | this.setCSS('color-bg', toHex(appSkin.panelBackgroundColor.color)); 467 | this.setCSS('color-ui-hover', this.getCSS('color-scroll')); 468 | if (this.activeApp == 'ILST') { 469 | this.setCSS('scroll-radius', '20px'); 470 | this.setCSS('thumb-radius', '10px'); 471 | } else { 472 | this.setCSS('scroll-radius', '1px'); 473 | this.setCSS('thumb-width', '8px'); 474 | } 475 | } else { 476 | console.log('This is an After Effects theme'); 477 | this.activeTheme = 'gradient'; 478 | this.setCSS('color-bg', toHex(appSkin.panelBackgroundColor.color)); 479 | this.setCSS('color-ui-hover', toHex(appSkin.panelBackgroundColor.color, -10)); 480 | this.setCSS('color-scroll', toHex(appSkin.panelBackgroundColor.color, -20)); 481 | this.setCSS('color-scroll-thumb', toHex(appSkin.panelBackgroundColor.color)); 482 | this.setCSS('color-scroll-thumb-hover', toHex(appSkin.panelBackgroundColor.color, 10)); 483 | this.setCSS('scroll-radius', '20px'); 484 | this.setCSS('thumb-width', '10px'); 485 | } 486 | }, 487 | findMenuItemParentById(id) { 488 | var result; 489 | for (var i = 0; i < this.context.menu.length; i++) { 490 | for (let [key, value] of Object.entries(this.context.menu[i])) { 491 | if (key == "menu") { 492 | for (var v = 0; v < value.length; v++) { 493 | // console.log(value[v]) 494 | for (let [index, data] of Object.entries(value[v])) { 495 | // console.log(`${index} : ${data}`) 496 | if ((index == "id") && (data == id)) 497 | result = this.context.menu[i]; 498 | } 499 | } 500 | } 501 | if ((key == "id") && (value == id)) { 502 | result = this.context.menu[i]; 503 | } 504 | } 505 | } 506 | return result; 507 | }, 508 | findMenuItemById(id) { 509 | var result; 510 | for (var i = 0; i < this.context.menu.length; i++) { 511 | for (let [key, value] of Object.entries(this.context.menu[i])) { 512 | if (key == "menu") { 513 | for (var v = 0; v < value.length; v++) { 514 | for (let [index, data] of Object.entries(value[v])) { 515 | if ((index == "id") && (data == id)) 516 | result = value[v]; 517 | } 518 | } 519 | } 520 | if ((key == "id") && (value == id)) { 521 | result = this.context.menu[i]; 522 | } 523 | } 524 | } 525 | return result; 526 | }, 527 | toggleMenuItemSiblings(parent, exclude, state) { 528 | if (parent.length) { 529 | for (var i = 0; i < parent.length; i++) { 530 | if (parent[i].id !== exclude) 531 | console.log(`Should turn ${parent[i].id} to ${state}`) 532 | csInterface.updateContextMenuItem(parent[i].id, true, state); 533 | } 534 | } 535 | }, 536 | setContextMenu() { 537 | var self = this; 538 | csInterface.setContextMenuByJSON(self.menuString, self.contextMenuClicked); 539 | csInterface.updateContextMenuItem('persistent', true, self.persistent); 540 | }, 541 | contextMenuClicked(id) { 542 | var target = this.findMenuItemById(id), mirror; 543 | var parent = this.findMenuItemParentById(id); 544 | if ((target.checkable) && (target.ingroup) && (!target.checked)) { 545 | console.log(`${target.id} is ${target.checked}`) 546 | this.toggleMenuItemSiblings(parent.menu, target.id, target.checked); 547 | target.checked = !target.checked; 548 | console.log(`${target.id} is ${target.checked}`) 549 | csInterface.updateContextMenuItem(target.id, true, target.checked); 550 | } else if ((target.checkable) && (target.ingroup) && (target.checked)) { 551 | console.log('Already checked'); 552 | csInterface.updateContextMenuItem(id, true, true); 553 | } else { 554 | target.checked = !target.checked; 555 | // target.checked = true; 556 | } 557 | if (target.id == this.context.menu[5].menu[0].id) { 558 | mirror = this.context.menu[5].menu[0]; 559 | mirror.checked = target.checked; 560 | this.context.menu[5].menu[1].checked = !target.checked; 561 | // console.log('This is by sequence') 562 | } else if (target.id == this.context.menu[5].menu[1].id) { 563 | mirror = this.context.menu[5].menu[1]; 564 | mirror.checked = target.checked; 565 | this.context.menu[5].menu[0].checked = !target.checked; 566 | // console.log('This is blank') 567 | } 568 | if (id == 'refresh') 569 | location.reload(); 570 | if (id == 'suffix'||'prefix') { 571 | // Event.$emit(`${target.id}Toggle`, target.checked) 572 | if (id == 'suffix') { 573 | this.toggleSuffix(target.checked); 574 | } else { 575 | this.togglePrefix(target.checked); 576 | } 577 | } 578 | console.log(target) 579 | this.updateStorage(); 580 | }, 581 | handleResize(evt) { 582 | if (this.$root.activeApp == 'AEFT') { 583 | // console.log(`w: ${this.panelWidth}, h: ${this.panelHeight}`); 584 | this.panelHeight = document.documentElement.clientHeight; 585 | // this.setPanelCSS(); 586 | console.log(evt); 587 | } else { 588 | this.panelWidth = document.documentElement.clientWidth; 589 | this.panelHeight = document.documentElement.clientHeight; 590 | this.setPanelCSS(); 591 | } 592 | }, 593 | flushModifiers() { 594 | this.Ctrl = false; 595 | this.Shift = false; 596 | this.Alt = false; 597 | Event.$emit('clearMods'); 598 | }, 599 | parseModifiers(evt, key=null) { 600 | // console.log(evt) 601 | var lastMods = [this.Ctrl, this.Shift, this.Alt] 602 | if (this.isWake) { 603 | if (((!this.macOS) && (evt.ctrlKey)) || ((this.macOS) && (evt.metaKey))) { 604 | this.Ctrl = true; 605 | } else { 606 | this.Ctrl = false; 607 | } 608 | if (evt.shiftKey) 609 | this.Shift = true; 610 | else 611 | this.Shift = false; 612 | if (evt.altKey) { 613 | evt.preventDefault(); 614 | this.Alt = true; 615 | } else { 616 | this.Alt = false; 617 | }; 618 | var thisMods = [this.Ctrl, this.Shift, this.Alt] 619 | if (!this.isEqualArray(lastMods, thisMods)) { 620 | 621 | console.log(`${thisMods} : ${lastMods}`) 622 | } 623 | // Event.$emit('updateModsUI'); 624 | } else { 625 | Event.$emit('clearMods'); 626 | } 627 | }, 628 | flushModifiers() { 629 | this.Ctrl = false; 630 | this.Shift = false; 631 | this.Alt = false; 632 | Event.$emit('clearMods'); 633 | }, 634 | wake() { 635 | this.isWake = true; 636 | }, 637 | sleep() { 638 | this.isWake = false; 639 | this.flushModifiers(); 640 | }, 641 | testCS(evt) { 642 | this.cs.evalScript(`alert('${evt}')`) 643 | }, 644 | setPanelCSS() { 645 | this.setCSS('panel-height', `${this.panelHeight - 10}px`); 646 | // this.setCSS('panel-width', `${this.panelWidth}px`); 647 | }, 648 | getCSS(prop) { 649 | return window.getComputedStyle(document.documentElement).getPropertyValue('--' + prop); 650 | }, 651 | setCSS(prop, data) { 652 | document.documentElement.style.setProperty('--' + prop, data); 653 | }, 654 | isEqualArray(array1, array2) { 655 | array1 = array1.join().split(','), array2 = array2.join().split(','); 656 | var errors = 0, result; 657 | for (var i = 0; i < array1.length; i++) { 658 | if (array1[i] !== array2[i]) 659 | errors++; 660 | } 661 | if (errors > 0) 662 | result = false; 663 | else 664 | result = true; 665 | return result; 666 | }, 667 | removeEmptyValues(keyList, mirror = []) { 668 | // console.log(keyList); 669 | for (var i = 0; i < keyList.length; i++) { 670 | var targ = keyList[i]; 671 | if ((/\s/.test(targ)) || (targ.length < 6)) { 672 | // console.log('Empty'); 673 | } else { 674 | mirror.push(targ); 675 | } 676 | } 677 | return mirror; 678 | }, 679 | removeDuplicatesInArray(keyList) { 680 | try { 681 | var uniq = keyList 682 | .map((name) => { 683 | return { count: 1, name: name } 684 | }) 685 | .reduce((a, b) => { 686 | a[b.name] = (a[b.name] || 0) + b.count 687 | return a 688 | }, {}) 689 | var sorted = Object.keys(uniq).sort((a, b) => uniq[a] < uniq[b]) 690 | } catch (err) { 691 | sorted = keyList 692 | } finally { 693 | return sorted; 694 | } 695 | }, 696 | } 697 | }); 698 | 699 | // Vue.component('test-btn', { 700 | // props: ['label'], 701 | // template: ` 702 | //
705 | // {{label}} 706 | //
707 | // `, 708 | // methods: { 709 | // runTest: function(e) { 710 | // var targ = this.$root.compi, self = this; 711 | // try { 712 | // if (/run/.test(e)) 713 | // csInterface.evalScript(`kickstart()`, self.recolor) 714 | // else if (/color/.test(e)) 715 | // csInterface.evalScript(`colorcode()`, this.$root.getNames) 716 | // else if (/reset/.test(e)) 717 | // csInterface.evalScript(`displayColorLabels()`) 718 | // else 719 | // csInterface.evalScript(`${e}()`) 720 | // // console.log('nothing happened'); 721 | // } catch(err) { 722 | // console.log(err.data); 723 | // } finally { 724 | // console.log(`Ran ${e}`); 725 | // } 726 | // }, 727 | // recolor: function(e) { 728 | // var targ = this.$root.compi; 729 | // csInterface.evalScript(`colorcode()`, this.$root.getNames) 730 | // } 731 | // } 732 | // }) 733 | 734 | // Vue.component('test-toolbar', { 735 | // template: ` 736 | //
737 | // 738 | // 739 | // 740 | //
741 | // `, 742 | // }) 743 | -------------------------------------------------------------------------------- /client/style.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --color-input-bg: #262626; 3 | --color-input-focus: #fcfcfc; 4 | --color-input-text: #cdcdcd; 5 | --color-input-text-focus: #2a2a2a; 6 | --color-border-focus: #46a0f5; 7 | --color-border: #3e3e3e; 8 | } 9 | 10 | 11 | .appGrid { 12 | box-sizing: border-box; 13 | width: 100%; 14 | /* height: 42px; */ 15 | /* border: 2px solid red; */ 16 | height: var(--panel-height); 17 | overflow: hidden; 18 | } 19 | 20 | .label { 21 | box-sizing: border-box; 22 | border: 1.35px solid var(--color-border); 23 | width: .65rem; 24 | margin-left: .5rem; 25 | } 26 | 27 | 28 | 29 | .texter { 30 | display: flex; 31 | justify-content: flex-start; 32 | /* width: 100%; */ 33 | flex-wrap: nowrap; 34 | font-size: .75rem; 35 | font-family: 'Rubik'; 36 | margin: .5rem 0px 0px .5rem; 37 | /* border: 2px solid red; */ 38 | } 39 | 40 | .texter-input, .texter-idle { 41 | border: 1.35px solid var(--color-border); 42 | border-radius: .25rem; 43 | margin-left: .25rem; 44 | background-color: var(--color-input-bg); 45 | color: var(--color-input-text); 46 | padding: .25rem .5rem; 47 | } 48 | 49 | .texter-prefix { 50 | border: 1.35px solid var(--color-border); 51 | border-radius: .25rem; 52 | max-width: 3rem; 53 | margin-left: .25rem; 54 | background-color: var(--color-input-bg); 55 | color: var(--color-input-text); 56 | padding: .25rem .5rem; 57 | } 58 | 59 | .texter-prefix-idle { 60 | max-width: 3rem; 61 | border: 1.35px solid var(--color-border); 62 | border-radius: .25rem; 63 | padding: .25rem .5rem; 64 | margin-left: .25rem; 65 | } 66 | 67 | .texter-idle:not(:focus), .texter-prefix-idle:not(:focus) { 68 | background-color: var(--color-input-idle); 69 | color: var(--color-input-text); 70 | } 71 | 72 | .texter-input:focus, .texter-idle:focus, .texter-prefix:focus, .texter-prefix-idle:focus { 73 | background-color: var(--color-input-focus); 74 | color: var(--color-input-text-focus); 75 | } 76 | -------------------------------------------------------------------------------- /host/ILST/host.jsx: -------------------------------------------------------------------------------- 1 | var doc = app.documents[0]; 2 | function scanSelection(mirror) { 3 | if (mirror === void 0) { mirror = []; } 4 | if (doc.activeLayer) { 5 | for (var i = 0; i < doc.layers.length; i++) { 6 | var child = doc.layers[i]; 7 | if (child == doc.activeLayer) 8 | return child.name + ";" + i + ";" + toHex(child.color); 9 | } 10 | } 11 | else { 12 | return ''; 13 | } 14 | // return mirror; 15 | } 16 | function findActiveLayer() { 17 | var index; 18 | for (var i = 0; i < doc.layers.length; i++) { 19 | var child = doc.layers[i]; 20 | if (child == doc.activeLayer) { 21 | index = i; 22 | } 23 | } 24 | return index; 25 | } 26 | function travelUpLayer() { 27 | var index = findActiveLayer(); 28 | if (index == doc.layers.length - 1) 29 | index = 0; 30 | else 31 | index++; 32 | doc.activeLayer = doc.layers[index]; 33 | } 34 | function renameActiveLayer(name) { 35 | var index = findActiveLayer(); 36 | doc.layers[index].name = name; 37 | } 38 | function travelDownLayer() { 39 | var index = findActiveLayer(); 40 | if (index == 0) 41 | index = doc.layers.length - 1; 42 | else 43 | index--; 44 | doc.activeLayer = doc.layers[index]; 45 | } 46 | // quickSelect(2) 47 | function quickSelect(index) { 48 | if ((index < doc.layers.length) && (doc.layers[index].pageItems.length)) { 49 | doc.layers[index].pageItems[0].selected = true; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /host/ILST/host.ts: -------------------------------------------------------------------------------- 1 | var doc = app.documents[0]; 2 | 3 | function scanSelection(mirror = []) { 4 | if (doc.activeLayer) { 5 | for (var i = 0; i < doc.layers.length; i++) { 6 | var child = doc.layers[i]; 7 | if (child == doc.activeLayer) 8 | return `${child.name};${i};${toHex(child.color)}`; 9 | } 10 | } else { 11 | return ''; 12 | } 13 | // return mirror; 14 | } 15 | 16 | function findActiveLayer() { 17 | var index; 18 | for (var i = 0; i < doc.layers.length; i++) { 19 | var child = doc.layers[i]; 20 | if (child == doc.activeLayer) { 21 | index = i; 22 | } 23 | } 24 | return index; 25 | } 26 | 27 | function travelUpLayer() { 28 | var index = findActiveLayer(); 29 | if (index == doc.layers.length - 1) 30 | index = 0; 31 | else 32 | index++; 33 | doc.activeLayer = doc.layers[index]; 34 | } 35 | 36 | function renameActiveLayer(name) { 37 | var index = findActiveLayer(); 38 | doc.layers[index].name = name; 39 | } 40 | 41 | function travelDownLayer() { 42 | var index = findActiveLayer(); 43 | if (index == 0) 44 | index = doc.layers.length - 1; 45 | else 46 | index--; 47 | doc.activeLayer = doc.layers[index]; 48 | } 49 | 50 | // quickSelect(2) 51 | function quickSelect(index) { 52 | if ((index < doc.layers.length) && (doc.layers[index].pageItems.length)) { 53 | doc.layers[index].pageItems[0].selected = true; 54 | } 55 | } -------------------------------------------------------------------------------- /host/ILST/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": true, 3 | "compilerOptions": { 4 | "outFile": "./host.jsx", 5 | "allowJs": true, 6 | "noLib": true, 7 | // "module": "commonjs", 8 | "types": [ 9 | "types-for-adobe/illustrator/2015.3" 10 | ] 11 | }, 12 | "files": [ 13 | "host.ts" 14 | ], 15 | "exclude": [ 16 | "node_modules" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /host/PHXS/host.jsx: -------------------------------------------------------------------------------- 1 | // var doc = app.documents[0]; 2 | function scanSelection() { 3 | // if (doc.activeLayer) { 4 | // for (var i = 0; i < doc.layers.length; i++) { 5 | // var child = doc.layers[i]; 6 | // if (child == doc.activeLayer) 7 | // return `${child.name};${i};${toHex(child.color)}`; 8 | // } 9 | // } else { 10 | // return ''; 11 | // } 12 | // return mirror; 13 | } 14 | function findActiveLayer() { 15 | // var index; 16 | // for (var i = 0; i < doc.layers.length; i++) { 17 | // var child = doc.layers[i]; 18 | // if (child == doc.activeLayer) { 19 | // index = i; 20 | // } 21 | // } 22 | // return index; 23 | } 24 | function travelUpLayer() { 25 | // var index = findActiveLayer(); 26 | // if (index == doc.layers.length - 1) 27 | // index = 0; 28 | // else 29 | // index++; 30 | // doc.activeLayer = doc.layers[index]; 31 | } 32 | function renameActiveLayer(name) { 33 | // var index = findActiveLayer(); 34 | // doc.layers[index].name = name; 35 | } 36 | function travelDownLayer() { 37 | // var index = findActiveLayer(); 38 | // if (index == 0) 39 | // index = doc.layers.length - 1; 40 | // else 41 | // index--; 42 | // doc.activeLayer = doc.layers[index]; 43 | } 44 | // quickSelect(2) 45 | // function quickSelect(index) { 46 | // if ((index < doc.layers.length) && (doc.layers[index].pageItems.length)) { 47 | // doc.layers[index].pageItems[0].selected = true; 48 | // } 49 | // } 50 | -------------------------------------------------------------------------------- /host/PHXS/host.ts: -------------------------------------------------------------------------------- 1 | // var doc = app.documents[0]; 2 | 3 | function scanSelection() { 4 | // if (doc.activeLayer) { 5 | // for (var i = 0; i < doc.layers.length; i++) { 6 | // var child = doc.layers[i]; 7 | // if (child == doc.activeLayer) 8 | // return `${child.name};${i};${toHex(child.color)}`; 9 | // } 10 | // } else { 11 | // return ''; 12 | // } 13 | // return mirror; 14 | } 15 | 16 | function findActiveLayer() { 17 | // var index; 18 | // for (var i = 0; i < doc.layers.length; i++) { 19 | // var child = doc.layers[i]; 20 | // if (child == doc.activeLayer) { 21 | // index = i; 22 | // } 23 | // } 24 | // return index; 25 | } 26 | 27 | function travelUpLayer() { 28 | // var index = findActiveLayer(); 29 | // if (index == doc.layers.length - 1) 30 | // index = 0; 31 | // else 32 | // index++; 33 | // doc.activeLayer = doc.layers[index]; 34 | } 35 | 36 | function renameActiveLayer(name) { 37 | // var index = findActiveLayer(); 38 | // doc.layers[index].name = name; 39 | } 40 | 41 | function travelDownLayer() { 42 | // var index = findActiveLayer(); 43 | // if (index == 0) 44 | // index = doc.layers.length - 1; 45 | // else 46 | // index--; 47 | // doc.activeLayer = doc.layers[index]; 48 | } 49 | 50 | // quickSelect(2) 51 | // function quickSelect(index) { 52 | // if ((index < doc.layers.length) && (doc.layers[index].pageItems.length)) { 53 | // doc.layers[index].pageItems[0].selected = true; 54 | // } 55 | // } -------------------------------------------------------------------------------- /host/PHXS/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": true, 3 | "compilerOptions": { 4 | "outFile": "./host.jsx", 5 | "allowJs": true, 6 | "noLib": true, 7 | // "module": "commonjs", 8 | "types": [ 9 | "types-for-adobe/Photoshop/2015.5" 10 | ] 11 | }, 12 | "files": [ 13 | "host.ts" 14 | ], 15 | "exclude": [ 16 | "node_modules" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /host/universal/Console.jsx: -------------------------------------------------------------------------------- 1 | JSXEvent('Loading...', 'console') 2 | 3 | var doc = app.documents[0]; 4 | var directory; 5 | var setPath; 6 | var setName; 7 | 8 | var thisDoc = app.documents[0]; 9 | var activeAB = thisDoc.artboards.getActiveArtboardIndex(); 10 | var lastAB = 0; 11 | var lastABOffset, isOrigin, thisAB, absAB, relAB; 12 | 13 | function getName(){ 14 | return app.documents[0].name; 15 | } 16 | 17 | function setDirectory(path){ 18 | setPath = path; 19 | var setFolder = new Folder(path); 20 | setFolder.create(); 21 | } 22 | 23 | function deleteFolder(path) { 24 | var thisFolder = Folder(path); 25 | try { 26 | thisFolder.remove(); 27 | return true; 28 | } catch(e){return false;} 29 | } 30 | 31 | function verifyFile(name){ 32 | var newFile = File(setPath + "/" + name + ".svg"); 33 | try {newFile.open('r'); 34 | } catch(e){alert(e)}; 35 | var contents = newFile.read(); 36 | return contents; 37 | } 38 | 39 | function clearSet(){ 40 | var setFolder = Folder(setPath); 41 | var setFile = setFolder.getFiles("*.svg"); 42 | if ( !setFile.length ) { 43 | // alert("No files"); 44 | return; 45 | } else { 46 | for (var i = 0; i < setFile.length; i++) { 47 | setFile[i].remove(); 48 | } 49 | } 50 | } 51 | 52 | function runScript(path) { 53 | try { 54 | $.evalFile(path) 55 | } catch (e) { 56 | JSXEvent(e.name + "," + e.line + "," + e + "," + e.message, "console") 57 | } 58 | } 59 | 60 | function JSXEvent(payload, eventType) { 61 | try { 62 | var xLib = new ExternalObject("lib:\PlugPlugExternalObject"); 63 | } catch (e) { 64 | JSXEvent(e, 'console') 65 | } 66 | if (xLib) { 67 | var eventObj = new CSXSEvent(); 68 | eventObj.type = eventType; 69 | eventObj.data = payload; 70 | eventObj.dispatch(); 71 | } 72 | return; 73 | } 74 | 75 | var console = { 76 | log : function(data) {JSXEvent(data, 'console')} 77 | }; 78 | 79 | /// https://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb 80 | function componentToHex(c) { 81 | var hex = c.toString(16); 82 | return hex.length == 1 ? "0" + hex : hex; 83 | } 84 | 85 | function rgbToHex(r, g, b) { 86 | return componentToHex(r) + componentToHex(g) + componentToHex(b); 87 | } 88 | 89 | function hexToRgb(hex) { 90 | var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); 91 | return result ? { 92 | r: parseInt(result[1], 16), 93 | g: parseInt(result[2], 16), 94 | b: parseInt(result[3], 16) 95 | } : null; 96 | } 97 | 98 | function toHex(color, delta) { 99 | function computeValue(value, delta) { 100 | var computedValue = !isNaN(delta) ? value + delta : value; 101 | if (computedValue < 0) { 102 | computedValue = 0; 103 | } else if (computedValue > 255) { 104 | computedValue = 255; 105 | } 106 | 107 | computedValue = Math.round(computedValue).toString(16); 108 | return computedValue.length == 1 ? "0" + computedValue : computedValue; 109 | } 110 | 111 | var hex = ""; 112 | if (color) { 113 | with (color) { 114 | hex = computeValue(red, delta) + computeValue(green, delta) + computeValue(blue, delta); 115 | }; 116 | } 117 | return "#" + hex; 118 | } -------------------------------------------------------------------------------- /host/universal/json2.jsx: -------------------------------------------------------------------------------- 1 | // https://github.com/douglascrockford/JSON-js/blob/master/json2.js 2 | // json2.js 3 | // 2017-06-12 4 | // Public Domain. 5 | // NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. 6 | 7 | // USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO 8 | // NOT CONTROL. 9 | 10 | // This file creates a global JSON object containing two methods: stringify 11 | // and parse. This file provides the ES5 JSON capability to ES3 systems. 12 | // If a project might run on IE8 or earlier, then this file should be included. 13 | // This file does nothing on ES5 systems. 14 | 15 | // JSON.stringify(value, replacer, space) 16 | // value any JavaScript value, usually an object or array. 17 | // replacer an optional parameter that determines how object 18 | // values are stringified for objects. It can be a 19 | // function or an array of strings. 20 | // space an optional parameter that specifies the indentation 21 | // of nested structures. If it is omitted, the text will 22 | // be packed without extra whitespace. If it is a number, 23 | // it will specify the number of spaces to indent at each 24 | // level. If it is a string (such as "\t" or " "), 25 | // it contains the characters used to indent at each level. 26 | // This method produces a JSON text from a JavaScript value. 27 | // When an object value is found, if the object contains a toJSON 28 | // method, its toJSON method will be called and the result will be 29 | // stringified. A toJSON method does not serialize: it returns the 30 | // value represented by the name/value pair that should be serialized, 31 | // or undefined if nothing should be serialized. The toJSON method 32 | // will be passed the key associated with the value, and this will be 33 | // bound to the value. 34 | 35 | // For example, this would serialize Dates as ISO strings. 36 | 37 | // Date.prototype.toJSON = function (key) { 38 | // function f(n) { 39 | // // Format integers to have at least two digits. 40 | // return (n < 10) 41 | // ? "0" + n 42 | // : n; 43 | // } 44 | // return this.getUTCFullYear() + "-" + 45 | // f(this.getUTCMonth() + 1) + "-" + 46 | // f(this.getUTCDate()) + "T" + 47 | // f(this.getUTCHours()) + ":" + 48 | // f(this.getUTCMinutes()) + ":" + 49 | // f(this.getUTCSeconds()) + "Z"; 50 | // }; 51 | 52 | // You can provide an optional replacer method. It will be passed the 53 | // key and value of each member, with this bound to the containing 54 | // object. The value that is returned from your method will be 55 | // serialized. If your method returns undefined, then the member will 56 | // be excluded from the serialization. 57 | 58 | // If the replacer parameter is an array of strings, then it will be 59 | // used to select the members to be serialized. It filters the results 60 | // such that only members with keys listed in the replacer array are 61 | // stringified. 62 | 63 | // Values that do not have JSON representations, such as undefined or 64 | // functions, will not be serialized. Such values in objects will be 65 | // dropped; in arrays they will be replaced with null. You can use 66 | // a replacer function to replace those with JSON values. 67 | 68 | // JSON.stringify(undefined) returns undefined. 69 | 70 | // The optional space parameter produces a stringification of the 71 | // value that is filled with line breaks and indentation to make it 72 | // easier to read. 73 | 74 | // If the space parameter is a non-empty string, then that string will 75 | // be used for indentation. If the space parameter is a number, then 76 | // the indentation will be that many spaces. 77 | 78 | // Example: 79 | 80 | // text = JSON.stringify(["e", {pluribus: "unum"}]); 81 | // // text is '["e",{"pluribus":"unum"}]' 82 | 83 | // text = JSON.stringify(["e", {pluribus: "unum"}], null, "\t"); 84 | // // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]' 85 | 86 | // text = JSON.stringify([new Date()], function (key, value) { 87 | // return this[key] instanceof Date 88 | // ? "Date(" + this[key] + ")" 89 | // : value; 90 | // }); 91 | // // text is '["Date(---current time---)"]' 92 | 93 | // JSON.parse(text, reviver) 94 | // This method parses a JSON text to produce an object or array. 95 | // It can throw a SyntaxError exception. 96 | 97 | // The optional reviver parameter is a function that can filter and 98 | // transform the results. It receives each of the keys and values, 99 | // and its return value is used instead of the original value. 100 | // If it returns what it received, then the structure is not modified. 101 | // If it returns undefined then the member is deleted. 102 | 103 | // Example: 104 | 105 | // // Parse the text. Values that look like ISO date strings will 106 | // // be converted to Date objects. 107 | 108 | // myData = JSON.parse(text, function (key, value) { 109 | // var a; 110 | // if (typeof value === "string") { 111 | // a = 112 | // /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); 113 | // if (a) { 114 | // return new Date(Date.UTC( 115 | // +a[1], +a[2] - 1, +a[3], +a[4], +a[5], +a[6] 116 | // )); 117 | // } 118 | // return value; 119 | // } 120 | // }); 121 | 122 | // myData = JSON.parse( 123 | // "[\"Date(09/09/2001)\"]", 124 | // function (key, value) { 125 | // var d; 126 | // if ( 127 | // typeof value === "string" 128 | // && value.slice(0, 5) === "Date(" 129 | // && value.slice(-1) === ")" 130 | // ) { 131 | // d = new Date(value.slice(5, -1)); 132 | // if (d) { 133 | // return d; 134 | // } 135 | // } 136 | // return value; 137 | // } 138 | // ); 139 | 140 | // This is a reference implementation. You are free to copy, modify, or 141 | // redistribute. 142 | 143 | /*jslint 144 | eval, for, this 145 | */ 146 | 147 | /*property 148 | JSON, apply, call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, 149 | getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, 150 | lastIndex, length, parse, prototype, push, replace, slice, stringify, 151 | test, toJSON, toString, valueOf 152 | */ 153 | 154 | 155 | // Create a JSON object only if one does not already exist. We create the 156 | // methods in a closure to avoid creating global variables. 157 | 158 | if (typeof JSON !== "object") { 159 | JSON = {}; 160 | } 161 | 162 | (function () { 163 | "use strict"; 164 | 165 | var rx_one = /^[\],:{}\s]*$/; 166 | var rx_two = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g; 167 | var rx_three = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g; 168 | var rx_four = /(?:^|:|,)(?:\s*\[)+/g; 169 | var rx_escapable = /[\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; 170 | var rx_dangerous = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; 171 | 172 | function f(n) { 173 | // Format integers to have at least two digits. 174 | return (n < 10) 175 | ? "0" + n 176 | : n; 177 | } 178 | 179 | function this_value() { 180 | return this.valueOf(); 181 | } 182 | 183 | if (typeof Date.prototype.toJSON !== "function") { 184 | 185 | Date.prototype.toJSON = function () { 186 | 187 | return isFinite(this.valueOf()) 188 | ? ( 189 | this.getUTCFullYear() 190 | + "-" 191 | + f(this.getUTCMonth() + 1) 192 | + "-" 193 | + f(this.getUTCDate()) 194 | + "T" 195 | + f(this.getUTCHours()) 196 | + ":" 197 | + f(this.getUTCMinutes()) 198 | + ":" 199 | + f(this.getUTCSeconds()) 200 | + "Z" 201 | ) 202 | : null; 203 | }; 204 | 205 | Boolean.prototype.toJSON = this_value; 206 | Number.prototype.toJSON = this_value; 207 | String.prototype.toJSON = this_value; 208 | } 209 | 210 | var gap; 211 | var indent; 212 | var meta; 213 | var rep; 214 | 215 | 216 | function quote(string) { 217 | 218 | // If the string contains no control characters, no quote characters, and no 219 | // backslash characters, then we can safely slap some quotes around it. 220 | // Otherwise we must also replace the offending characters with safe escape 221 | // sequences. 222 | 223 | rx_escapable.lastIndex = 0; 224 | return rx_escapable.test(string) 225 | ? "\"" + string.replace(rx_escapable, function (a) { 226 | var c = meta[a]; 227 | return typeof c === "string" 228 | ? c 229 | : "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4); 230 | }) + "\"" 231 | : "\"" + string + "\""; 232 | } 233 | 234 | 235 | function str(key, holder) { 236 | 237 | // Produce a string from holder[key]. 238 | 239 | var i; // The loop counter. 240 | var k; // The member key. 241 | var v; // The member value. 242 | var length; 243 | var mind = gap; 244 | var partial; 245 | var value = holder[key]; 246 | 247 | // If the value has a toJSON method, call it to obtain a replacement value. 248 | 249 | if ( 250 | value 251 | && typeof value === "object" 252 | && typeof value.toJSON === "function" 253 | ) { 254 | value = value.toJSON(key); 255 | } 256 | 257 | // If we were called with a replacer function, then call the replacer to 258 | // obtain a replacement value. 259 | 260 | if (typeof rep === "function") { 261 | value = rep.call(holder, key, value); 262 | } 263 | 264 | // What happens next depends on the value's type. 265 | 266 | switch (typeof value) { 267 | case "string": 268 | return quote(value); 269 | 270 | case "number": 271 | 272 | // JSON numbers must be finite. Encode non-finite numbers as null. 273 | 274 | return (isFinite(value)) 275 | ? String(value) 276 | : "null"; 277 | 278 | case "boolean": 279 | case "null": 280 | 281 | // If the value is a boolean or null, convert it to a string. Note: 282 | // typeof null does not produce "null". The case is included here in 283 | // the remote chance that this gets fixed someday. 284 | 285 | return String(value); 286 | 287 | // If the type is "object", we might be dealing with an object or an array or 288 | // null. 289 | 290 | case "object": 291 | 292 | // Due to a specification blunder in ECMAScript, typeof null is "object", 293 | // so watch out for that case. 294 | 295 | if (!value) { 296 | return "null"; 297 | } 298 | 299 | // Make an array to hold the partial results of stringifying this object value. 300 | 301 | gap += indent; 302 | partial = []; 303 | 304 | // Is the value an array? 305 | 306 | if (Object.prototype.toString.apply(value) === "[object Array]") { 307 | 308 | // The value is an array. Stringify every element. Use null as a placeholder 309 | // for non-JSON values. 310 | 311 | length = value.length; 312 | for (i = 0; i < length; i += 1) { 313 | partial[i] = str(i, value) || "null"; 314 | } 315 | 316 | // Join all of the elements together, separated with commas, and wrap them in 317 | // brackets. 318 | 319 | v = partial.length === 0 320 | ? "[]" 321 | : gap 322 | ? ( 323 | "[\n" 324 | + gap 325 | + partial.join(",\n" + gap) 326 | + "\n" 327 | + mind 328 | + "]" 329 | ) 330 | : "[" + partial.join(",") + "]"; 331 | gap = mind; 332 | return v; 333 | } 334 | 335 | // If the replacer is an array, use it to select the members to be stringified. 336 | 337 | if (rep && typeof rep === "object") { 338 | length = rep.length; 339 | for (i = 0; i < length; i += 1) { 340 | if (typeof rep[i] === "string") { 341 | k = rep[i]; 342 | v = str(k, value); 343 | if (v) { 344 | partial.push(quote(k) + ( 345 | (gap) 346 | ? ": " 347 | : ":" 348 | ) + v); 349 | } 350 | } 351 | } 352 | } else { 353 | 354 | // Otherwise, iterate through all of the keys in the object. 355 | 356 | for (k in value) { 357 | if (Object.prototype.hasOwnProperty.call(value, k)) { 358 | v = str(k, value); 359 | if (v) { 360 | partial.push(quote(k) + ( 361 | (gap) 362 | ? ": " 363 | : ":" 364 | ) + v); 365 | } 366 | } 367 | } 368 | } 369 | 370 | // Join all of the member texts together, separated with commas, 371 | // and wrap them in braces. 372 | 373 | v = partial.length === 0 374 | ? "{}" 375 | : gap 376 | ? "{\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "}" 377 | : "{" + partial.join(",") + "}"; 378 | gap = mind; 379 | return v; 380 | } 381 | } 382 | 383 | // If the JSON object does not yet have a stringify method, give it one. 384 | 385 | if (typeof JSON.stringify !== "function") { 386 | meta = { // table of character substitutions 387 | "\b": "\\b", 388 | "\t": "\\t", 389 | "\n": "\\n", 390 | "\f": "\\f", 391 | "\r": "\\r", 392 | "\"": "\\\"", 393 | "\\": "\\\\" 394 | }; 395 | JSON.stringify = function (value, replacer, space) { 396 | 397 | // The stringify method takes a value and an optional replacer, and an optional 398 | // space parameter, and returns a JSON text. The replacer can be a function 399 | // that can replace values, or an array of strings that will select the keys. 400 | // A default replacer method can be provided. Use of the space parameter can 401 | // produce text that is more easily readable. 402 | 403 | var i; 404 | gap = ""; 405 | indent = ""; 406 | 407 | // If the space parameter is a number, make an indent string containing that 408 | // many spaces. 409 | 410 | if (typeof space === "number") { 411 | for (i = 0; i < space; i += 1) { 412 | indent += " "; 413 | } 414 | 415 | // If the space parameter is a string, it will be used as the indent string. 416 | 417 | } else if (typeof space === "string") { 418 | indent = space; 419 | } 420 | 421 | // If there is a replacer, it must be a function or an array. 422 | // Otherwise, throw an error. 423 | 424 | rep = replacer; 425 | if (replacer && typeof replacer !== "function" && ( 426 | typeof replacer !== "object" 427 | || typeof replacer.length !== "number" 428 | )) { 429 | throw new Error("JSON.stringify"); 430 | } 431 | 432 | // Make a fake root object containing our value under the key of "". 433 | // Return the result of stringifying the value. 434 | 435 | return str("", {"": value}); 436 | }; 437 | } 438 | 439 | 440 | // If the JSON object does not yet have a parse method, give it one. 441 | 442 | if (typeof JSON.parse !== "function") { 443 | JSON.parse = function (text, reviver) { 444 | 445 | // The parse method takes a text and an optional reviver function, and returns 446 | // a JavaScript value if the text is a valid JSON text. 447 | 448 | var j; 449 | 450 | function walk(holder, key) { 451 | 452 | // The walk method is used to recursively walk the resulting structure so 453 | // that modifications can be made. 454 | 455 | var k; 456 | var v; 457 | var value = holder[key]; 458 | if (value && typeof value === "object") { 459 | for (k in value) { 460 | if (Object.prototype.hasOwnProperty.call(value, k)) { 461 | v = walk(value, k); 462 | if (v !== undefined) { 463 | value[k] = v; 464 | } else { 465 | delete value[k]; 466 | } 467 | } 468 | } 469 | } 470 | return reviver.call(holder, key, value); 471 | } 472 | 473 | 474 | // Parsing happens in four stages. In the first stage, we replace certain 475 | // Unicode characters with escape sequences. JavaScript handles many characters 476 | // incorrectly, either silently deleting them, or treating them as line endings. 477 | 478 | text = String(text); 479 | rx_dangerous.lastIndex = 0; 480 | if (rx_dangerous.test(text)) { 481 | text = text.replace(rx_dangerous, function (a) { 482 | return ( 483 | "\\u" 484 | + ("0000" + a.charCodeAt(0).toString(16)).slice(-4) 485 | ); 486 | }); 487 | } 488 | 489 | // In the second stage, we run the text against regular expressions that look 490 | // for non-JSON patterns. We are especially concerned with "()" and "new" 491 | // because they can cause invocation, and "=" because it can cause mutation. 492 | // But just to be safe, we want to reject all unexpected forms. 493 | 494 | // We split the second stage into 4 regexp operations in order to work around 495 | // crippling inefficiencies in IE's and Safari's regexp engines. First we 496 | // replace the JSON backslash pairs with "@" (a non-JSON character). Second, we 497 | // replace all simple value tokens with "]" characters. Third, we delete all 498 | // open brackets that follow a colon or comma or that begin the text. Finally, 499 | // we look to see that the remaining characters are only whitespace or "]" or 500 | // "," or ":" or "{" or "}". If that is so, then the text is safe for eval. 501 | 502 | if ( 503 | rx_one.test( 504 | text 505 | .replace(rx_two, "@") 506 | .replace(rx_three, "]") 507 | .replace(rx_four, "") 508 | ) 509 | ) { 510 | 511 | // In the third stage we use the eval function to compile the text into a 512 | // JavaScript structure. The "{" operator is subject to a syntactic ambiguity 513 | // in JavaScript: it can begin a block or an object literal. We wrap the text 514 | // in parens to eliminate the ambiguity. 515 | 516 | j = eval("(" + text + ")"); 517 | 518 | // In the optional fourth stage, we recursively walk the new structure, passing 519 | // each name/value pair to a reviver function for possible transformation. 520 | 521 | return (typeof reviver === "function") 522 | ? walk({"": j}, "") 523 | : j; 524 | } 525 | 526 | // If the text is not JSON parseable, then a SyntaxError is thrown. 527 | 528 | throw new SyntaxError("JSON.parse"); 529 | }; 530 | } 531 | }()); 532 | -------------------------------------------------------------------------------- /notes.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Inventsable/scribe/c07d05ce8cf015c7a9ec0338b4f9dbed27b6a48d/notes.md --------------------------------------------------------------------------------