Showing preview only (1,321K chars total). Download the full file or copy to clipboard to get everything.
Repository: ingrammicro/puzzle-publisher
Branch: master
Commit: fa75c9887c29
Files: 162
Total size: 17.9 MB
Directory structure:
gitextract_x9zr0ace/
├── .gitignore
├── CHANGELOG.md
├── Hints.md
├── Ideas.md
├── LICENSE
├── PuzzlePublisher.sketchplugin/
│ └── Contents/
│ ├── Resources/
│ │ └── advpng
│ └── Sketch/
│ ├── cmd-actions.js
│ ├── cmdline-functions.js
│ ├── constants.js
│ ├── exporter/
│ │ ├── PZArtboard.js
│ │ ├── PZDoc.js
│ │ ├── PZLayer.js
│ │ ├── PZPage.js
│ │ ├── exporter-run.js
│ │ ├── exporter.js
│ │ └── publisher.js
│ ├── lib/
│ │ ├── ga.js
│ │ ├── uidialog.js
│ │ ├── uipanel.js
│ │ └── utils.js
│ ├── manifest.json
│ ├── menu-cmd-other.js
│ ├── menu-cmd-publish-miro.js
│ ├── menu-cmd-publish.js
│ ├── menu-conf-artboard.js
│ ├── menu-conf-document.js
│ ├── menu-conf-layer.js
│ ├── menu-conf-plugin-export.js
│ ├── menu-conf-plugin-publishing.js
│ ├── menu-conf-plugin.js
│ ├── menu-edit-annotations.js
│ ├── menu-export-html-adv.js
│ ├── menu-export-html.js
│ ├── menu-external-link.js
│ ├── miro/
│ │ ├── api.js
│ │ └── utils.js
│ ├── pp-viewer/
│ │ ├── exporter/
│ │ │ └── exporter-build-html.js
│ │ └── viewer/
│ │ ├── js/
│ │ │ ├── AbstractViewer.js
│ │ │ ├── CommentsViewer.js
│ │ │ ├── ExpViewer.js
│ │ │ ├── GalleryViewer.js
│ │ │ ├── InfoViewer.js
│ │ │ ├── PresenterViewer.js
│ │ │ ├── SymbolViewer.js
│ │ │ ├── Viewer.js
│ │ │ ├── ViewerPage.js
│ │ │ └── other/
│ │ │ └── jquery.hotkeys.js
│ │ └── resources/
│ │ ├── animations.css
│ │ ├── viewer-center.css
│ │ ├── viewer-top.css
│ │ └── viewer.css
│ ├── scripts/
│ │ ├── export.sh
│ │ ├── preparePublish.sh
│ │ └── publish.sh
│ └── tokens/
│ └── DSExporter.js
├── README.md
├── appcast.xml
├── artworks/
│ └── UI-Images.sketch
├── comments/
│ ├── backend/
│ │ ├── .gitignore
│ │ ├── config/
│ │ │ ├── .htaccess
│ │ │ └── forums.json
│ │ ├── data/
│ │ │ └── .htaccess
│ │ ├── lib/
│ │ │ ├── Comments.js
│ │ │ ├── Forum.php
│ │ │ └── Frontend.php
│ │ └── server.php
│ ├── frontend/
│ │ ├── index.php
│ │ └── test.html
│ └── readme.txt
├── docs/
│ ├── Favicon/
│ │ ├── index.html
│ │ ├── resources/
│ │ │ ├── animations.css
│ │ │ ├── cb-modern-ui.css
│ │ │ ├── jquery.hotkeys.js
│ │ │ ├── ux1-ui.css
│ │ │ ├── viewer-center.css
│ │ │ ├── viewer-fonts.css
│ │ │ ├── viewer-top.css
│ │ │ └── viewer.css
│ │ └── viewer/
│ │ ├── AbstractViewer.js
│ │ ├── CommentsViewer.js
│ │ ├── GalleryViewer.js
│ │ ├── LayersData.js
│ │ ├── SymbolViewer.js
│ │ ├── VersionViewer.js
│ │ ├── story.js
│ │ ├── viewer-page.js
│ │ └── viewer.js
│ ├── FixedLayers/
│ │ ├── index.html
│ │ ├── resources/
│ │ │ ├── animations.css
│ │ │ ├── cb-modern-ui.css
│ │ │ ├── jquery.hotkeys.js
│ │ │ ├── ux1-ui.css
│ │ │ ├── viewer-center.css
│ │ │ ├── viewer-fonts.css
│ │ │ ├── viewer-top.css
│ │ │ └── viewer.css
│ │ └── viewer/
│ │ ├── AbstractViewer.js
│ │ ├── CommentsViewer.js
│ │ ├── GalleryViewer.js
│ │ ├── LayersData.js
│ │ ├── SymbolViewer.js
│ │ ├── VersionViewer.js
│ │ ├── story.js
│ │ ├── viewer-page.js
│ │ └── viewer.js
│ └── Tokens/
│ ├── Demo.sketch
│ └── Lib/
│ ├── UIKit.sketch
│ ├── _pt-assets/
│ │ └── UIKit/
│ │ ├── inspector.json
│ │ ├── vars.json
│ │ └── vars.scss
│ ├── design-tokens.less
│ └── sketch-styles.less
├── examples/
│ ├── CloseOverlay.sketch
│ ├── Favicon/
│ │ └── Favicon.sketch
│ ├── FixedLayers/
│ │ └── FixedLayers.sketch
│ ├── Link-ExternalArtboard.sketch
│ ├── Link-ModalArtboard/
│ │ ├── Link-ModalArtboard-vars.json
│ │ ├── Link-ModalArtboard-viewer.css
│ │ └── Link-ModalArtboard.sketch
│ ├── Link-NestedSymbols.sketch
│ ├── Link-NestedSymbols2.sketch
│ ├── Linked-Symbol-WithEmptyLink.sketch
│ ├── Links-NotOverided.sketch
│ ├── Links.sketch
│ ├── Links2.sketch
│ ├── OverlayOnMouseOver/
│ │ ├── test-library.sketch
│ │ ├── test.sketch
│ │ ├── test2.sketch
│ │ └── test3.sketch
│ ├── Overlays/
│ │ └── Overlays.sketch
│ ├── PageTransition.sketch
│ ├── PinnedTop.sketch
│ ├── Resized-FloatPanel.sketch
│ ├── SortOrder/
│ │ └── SortOrder.sketch
│ ├── Text Search/
│ │ └── Test.sketch
│ ├── VScrollbar/
│ │ ├── Link-ModalArtboard.sketch
│ │ └── VScroll.sketch
│ └── test.sketch
├── server_tools/
│ ├── announce.php
│ ├── config.json
│ ├── folder_info.php
│ ├── journal.php
│ ├── lib/
│ │ └── FolderInfo.php
│ └── version_info.php
└── tests/
├── 12.2.0/
│ └── test.sketch
├── 12.3.1/
│ └── LinkToOverlay.sketch
├── 12.4.0/
│ └── test.sketch
├── 12.4.2/
│ └── Test.sketch
├── 13.0.1/
│ ├── GCUX-7525.sketch
│ ├── GCUX-7530.sketch
│ └── GCUX-7531.sketch
├── 14.11.0/
│ └── test.sketch
├── 14.2.0/
│ └── Test.sketch
├── 14.3.0/
│ └── Test.sketch
├── 14.7.3/
│ └── Test.sketch
├── 14.8.3/
│ └── DemoApp.sketch
├── 15.0.0/
│ └── Test.sketch
├── 15.2.0/
│ └── Test.sketch
├── 15.3.0/
│ └── Test.sketch
├── 16.1.0/
│ ├── Lib.sketch
│ └── Test.sketch
└── New/
└── Test.sketch
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
.DS_Store
PuzzlePublisher.sketchplugin/Contents/Sketch/.vscode/sftp.json
================================================
FILE: CHANGELOG.md
================================================
# Change Log
See discussion on https://github.com/ingrammicro/puzzle-publisher/discussions site
## Version 17.9.0 (25 Sep 2022)
Addded publishing by HTTPS
## Version 17.8.3 (18 Aug 2022)
Check local mockup existing before publishing
## Version 17.8.1 (18 Aug 2022)
Show comment for symbol instance
Workaround for Sketch bug (thanks to cargeo@ for report)
## Version 17.8.0 (4 Aug 2022)
Switch to prev page work in loop
## Version 17.7.1 (3 Aug 2022)
Scrolling in overlay fixed
## Version 17.7.0 (13 Jul 2022)
Symbol instance also can be scrollable (without a parent group)
## Version 17.6.5 (13 Jul 2022)
Show overlays inside scrollable container
## Version 17.6.4 (7 Jul 2022)
To find a layer with shadow for overlay a layer with bigger height will be used
## Version 17.6.3 (28 Jun 2022)
Improved scollable containers
## Version 17.6.1 (03 Jun 2022)
show color: instead of background-color: for icons
## Version 17.6.0 (25 May 2022)
Fixed hotpots
## Version 17.5.1 (21 May 2022)
Fixed hotpots
## Version 17.5.0 (19 May 2022)
New layer settings: Vertical scrollbar
Example: https://github.com/ingrammicro/puzzle-publisher/tree/master/examples/VScrollbar
## Version 17.4.2 (16 May 2022)
Fixed bottom-pined panel
## Version 17.4.1 (11 May 2022)
Fixed overlay inside a fixed panel issue resolved
Wrong overlay transition behaviour fixed
## Version 17.4.0 (25 Apr 2022)
Custom artboard size function fixed
## Version 17.3.3 (15 Apr 2022)
Improved Handoff
## Version 17.3.2 (15 Apr 2022)
Integration with Miro works again
## Version 17.3.1 (6 Apr 2022)
Cmd+click opens external URLs in the new tab/window
## Version 17.3.0 (5 Apr 2022)
Viewer file structure changed
## Version 17.2.3 (30 Mar 2022)
Back links behaviour fixed
## Version 17.2.2 (30 Mar 2022)
Handoff shows background color token if its value is "none"
## Version 17.2.0 (28 Mar 2022)
Experimental Widgets Viewer has new mode - grouping by widgets
## Version 17.1.0 (14 Mar 2022)
Improved PP plugin to show icon colors (finally!)
## Version 17.0.5 (11 Mar 2022)
Selective export fixed
## Version 17.0.4 (01 Mar 2022)
Bugfixes
## Version 17.0.3 (22 Feb 2022)
HOTFIX FOR 17.0.0 - Symbol internal links are broken if Element Inspector is disabled
## Version 17.0.2 (22 Feb 2022)
HOTFIX FOR 17.0.0 - Support Sketch.app installed into non-Applications folder
## Version 17.0.1 (15 Feb 2022)
HOTFIX FOR 17.0.0 - Fixed browser page title
## Version 17.0.0 (14 Feb 2022)
Changed method of symbol master information processing to make it compatible with the latest Sketch.app
## Version 16.17.1 (1 Feb 2022)
Hotfix - return overlay shadows back
## Version 16.17.0 (1 Feb 2022)
Added fixed layer shadow mode to Configure layer
## Version 16.16.1 (1 Feb 2022)
Hotfix
## Version 16.16.0 (31 Jan 2022)
Added ability to enable "render fixed layers as regular" per artboard
## Version 16.15.0 (19 Jan 2022)
Added ability to highlight EXPERIMENTAL widgets
## Version 16.14.4 (18 Jan 2022)
Fixed support for custom JS code (thanks @cargeo for bug report)
## Version 16.14.3 (11 Jan 2022)
Updated menu icons
## Version 16.14.2 (28 Dec 2021)
Improved icon visualization in Handoff
## Version 16.14.1 (23 Dec 2021)
Auto-scale switch fixes
## Version 16.14.0 (16 Dec 2021)
Added full-screen mode
Improved links to experimental widgets
## Version 16.13.0 (6 Dec 2021)
Esc closed menu
New "Full page image" function added to menu
## Version 16.12.1 (24 Nov 2021)
Cleaned Viewer UI
## Version 16.12.0 (18 Nov 2021)
Search continues to next pages
## Version 16.11.4 (17 Nov 2021)
Fixed wrong commit
## Version 16.11.3 (08 Nov 2021)
Several Element Inspector improvements and fixes
## Version 16.11.2 (27 Oct 2021)
Fixed Artboards Sort Order
## Version 16.11.1 (27 Oct 2021)
Minor improvements in Element Inspector
## Version 16.11.0 (24 Oct 2021)
Use 'n' key to hide/show navigation tools
## Version 16.9.2 (26 Sep 2021)
Element Inspector now shows valid value for style defined as "@token1 + @token2"
## Version 16.9.0 (21 Sep 2021)
Show links to external documentation (for usage with Puzzle Tokens)
## Version 16.8.1 (20 Sep 2021)
Fixed layout issues in Chrome and FF
## Version 16.8.0 (21 Aug 2021)
Exporting with Element Inspector enabled is fast again! So, "Enable Element Inspector" checkbox moved back to Configure Export dialo
## Version 16.7.4 (19 Aug 2021)
'S' button should return to a page marked as "Start Page"
## Version 16.7.0 (12 Aug 2021)
Replaced "Use slow but..." checkbox
Moved "Enable Element Inspector" from Configure Export to Export dialog
## Version 16.6.3 (29 Jul 2021)
Fix auto-transition for the last page
Support for undocumented font weight = 14
## Version 16.6.2 (29 Jul 2021)
Added "Use slow but stable symbols detection" checkbox to Export dialog. The fast method depends of unstable Sketch behaviour. The slow method works always stable.
## Version 16.6.1 (28 Jul 2021)
Show icon names correctly in Element Inspector
## Version 16.6.0 (26 Jul 2021)
Fixed Element Inspector behaviour
## Version 16.5.6 (15 Jul 2021)
Fixed Element Inspector
## Version 16.5.5 (14 Jul 2021)
Hotfix
## Version 16.5.4 (14 Jul 2021)
- Rolled back icon name support to fix other tokens
## Version 16.5.3 (10 Jul 2021)
Improving Changes Inspector
## Version 16.5.2 (07 Jul 2021)
Improving Changes Inspector
## Version 16.5.1 (29 Jun 2021)
Corrected "Show symbols" toggler behaviour
Show icon names (Part II)
## Version 16.5.0 (29 Jun 2021)
Use full images in Gallery
Changes Inspector improved
Show icon names
## Version 16.4.3 (26 Jun 2021)
Fixed internal error related to color variables detection
## Version 16.4.1 (12 May 2021)
Text search inside Gallery now finds text layers also
## Version 16.3.1 (30 Apr 2021)
Improved scrolling in Text Search
## Version 16.3.0 (29 Apr 2021)
Added text search (Cmd+F,Cmd+G)
## Version 16.2.4 (23 Apr 2021)
Don't export masked layers without Export Preset configured
## Version 16.2.3 (20 Apr 2021)
Workaround for export crash
## Version 16.2.2 (16 Apr 2021)
Improved Element Inspector
## Version 16.2.0 (5 Apr 2021)
Added comment counters to Gallery
Added page labels to Gallery Map view
## Version 16.1.2 (19 Mar 2021)
Comments Viewer improvements
## Version 16.1.1 (15 Mar 2021)
Fixed Version Viewer
## Version 16.1.0 (12 Mar 2021)
Added ability to ignore links to library internal artboards (see Configure Exporting - Artboards)
## Version 16.0.3 (12 Mar 2021)
Improved comments
## Version 16.0.2 (05 Mar 2021)
Fixed mockup version up/down browsing
## Version 16.0.1 (04 Mar 2021)
Improved comments
## Version 16.0.0 (25 Feb 2021)
Added comments to published mockups
## Version 15.3.0 (30 Dec 2020)
Element Inspector allows to review overlapped layers by multiply clicking
## Version 15.2.7 (28 Dec 2020)
Fixed modal poisitioning on large displays
## Version 15.2.5 (21 Dec 2020)
Escaped "," in image file names to be compatible with Miro
Other fixed for publishing to Miro
## Version 15.2.4 (06 Dec 2020)
Corrected font size for Linux developers
## Version 15.2.0 (04 Dec 2020)
Fixed browser page background color
Added option to see font size adjusted for Linux developers
## Version 15.1.4 (03 Dec 2020)
Fixed Miro issues
## Version 15.1.3 (25 Nov 2020)
Added support for @XSpacer@ and @YSpacer@ layer name magic keys
## Version 15.1.3 (25 Nov 2020)
Fixed issues with Gallery Viewer and Embedded Mode
## Version 15.1.1 (25 Nov 2020)
Redesigned map view by @zubr133
Added page titles to map view
## Version 15.1.0 (19 Nov 2020)
Impoved map view:
1) Added page interactions
2) Added own URLs to gallery and map views
3) Other improvements
## Version 15.0.0 (14 Nov 2020)
Added map view to All Screens page
Code refactoring
## Version 14.11.1 (9 Nov 2020)
Fix: URLs are lowercased again
## Version 14.11.0 (9 Nov 2020)
Fixed "Open HTML in browser" checkbox behaviour
Suport @Redirect@ for modals too
## Version 14.10.4 (2 Nov 2020)
Fixed crash
## Version 14.10.3 (30 Oct 2020)
Fixed support for Shape shadows in Inspector
Fixed "Open in new window" icon behaviour (Embedded mode)
Supported tokens for color variables
## Version 14.10.2 (28 Sep 2020)
Fixes overlay multishadows
## Version 14.10.1 (16 Sep 2020)
Fixed navigation menu layout
## Version 14.10.0 (16 Sep 2020)
Now it's possible to inject any custom JS code into Viewer. See Plugin > Configure Export > JS Code option.
As example - you can hide some navigation menu ites using the following code:
$("#menu #zoom").hide();$("#menu #embed").hide();$("#menu #grid").hide();
"View All Screens" mode now handles "s" key correctly
## Version 14.9.1 (19 Sep 2020)
Gallery can be opened on document load using &g=1 search param
## Version 14.8.14 (17 Sep 2020)
Fixed publishing to Miro
## Version 14.8.13 (17 Sep 2020)
Many improvements in image generation
- Generate full images only if Miro settings configured
- Generate preview images by Sketch, not by external "sips" tool
- Use retina images in Gallery on non-retinal diplays too
## Version 14.8.12 (10 Sep 2020)
Fixed direct link to modal called from overlay
## Version 14.8.11 (2 Sep 2020)
Fixed exporting of external "external" artboards
Added success messages to Miro operations
## Version 14.8.10 (2 Sep 2020)
Fixed login to Miro for passwords with special characters
Improved publishing to Miro
## Version 14.8.8 (1 Sep 2020)
Group Miro boards by project
## Version 14.8.7 (31 Aug 2020)
Fixed image paths in Gallery
Improved shared style/symbol information in Element Inspector
## Version 14.8.6 (27 Aug 2020)
Fixed unstable behaviour of Miro publishing
## Version 14.8.5 (26 Aug 2020)
Improved publishing to Miro
## Version 14.8.4 (20 Aug 2020)
"Show All Images" feature now shows full artboard pictures included fixed images
"Publish to Miro" now places artboards correctly to prevent overlaping
## Version 14.8.3 (19 Aug 2020)
New feature: The plugin can publish mockups on Miro whiteboards
## Version 14.7.3 (10 Aug 2020)
Link inside a modal to the same modal closes it (similer to overlays)
## Version 14.7.2 (4 Aug 2020)
Fixed Document Settings modal (height increased to show all fields)
## Version 14.7.1 (24 July 2020)
Improved External URL dialog
## Version 14.7.0 (24 July 2020)
Added support for relative URLs
## Version 14.6.1 (25 June 2020)
Improvement for Element Inspector: click outside of any element unselect current element
## Version 14.6.0 (19 June 2020)
Added optional Secret Key pair settings to Configure Publishing dialod and server_tools/config.json
## Version 14.5.0 (19 June 2020)
Element Inspector improved
- build layer tree using valid z-index
- skip text layers with empty (or whitespace only) content
- suppor page navigation (left,right keys)
## Version 14.4.1 (10 June 2020)
Added support for fixed layers to Element Inspector
Added ability to disable a library sync for document during automation
## Version 14.4.0 (8 June 2020)
Added support for Image layers to Element Inspector
## Version 14.3.0 (8 June 2020)
Added margins to Element Inspector
## Version 14.2.0 (4 June 2020)
New features:
- Element Inspector shows FA icon details
## Version 14.1.2 (2 June 2020)
Fixed internal error on Sketch startup
## Version 14.1.1 (2 June 2020)
Improved async mode for sending statistics
## Version 14.1.0 (1 June 2020)
Added ability to enable debug logging
PP now sends anonymous usage data (using Google Analytics). You can disable it in Configure Plugin.
Workaround two Sketch issues
## Version 14.0.2 (28 May 2020)
- Improvements and fixes for Element Inspector
## Version 14.0.1 (27 May 2020)
- Totally reworked Element Inspector to show all text/shape layer styles
- Updated file protocol between Puzzle Publisher and Puzzle Tokens
## Version 13.1.2 (29 Apr 2020)
- Fixed error in Sketch 65
## Version 13.1.1 (22 Mar 2020)
- Improved Version Viewer
- Disable show/hide animations for modal (due to other open issue)
## Version 13.1.0 (17 Mar 2020)
Synced with PT 8.2.0 changes
## Version 13.0.5 (14 Mar 2020)
Fixed issues:
- Browser back button doesn't work in PP 13.0.1
- Please fix overlay position when you open overlay from another overlay (GCUX-7530)
- Fix link to close overlay (GCUX-7525)
- Cur trailed "/" in Remote Folder URL
## Version 13.0.1 (14 Mar 2020)
Viewer moved from URL format
https://site.com/dd/index.html?embed#home/o/10
to
https://site.com/dd/index.html?home&o=10&e=1
We need this change because URL with # doesn't work correctly on Apache sites with enabled Azure AD integration.
Attention! The new viewer also supports old URLs.
## Version 12.7.0 (6 Mar 2020)
New cool design for sidebar (thanks to @zubr133 )
Fixed issue: Element Inspector doesn't show token values for the document which is a library itself
## Version 12.6.0 (30 Mar 2020)
- Added support for Google Tag Manager codes (GTM* format)
## Version 12.5.2 (5 Mar 2020)
Fixed case "two links open the same overlay"
Disabled links highlighting for close overlay click event
## Version 12.5.1 (4 Mar 2020)
Fixed nested overlay behaviour
## Version 12.5.0 (2 Mar 2020)
Added custom SSH port settings to Configure Publishing dialog
## Version 12.4.2 (26 Feb 2020)
Respin for 12.4.0
## Version 12.4.0 (22 Feb 2020)
Added new "Up from top center of hotspot" overlay position
## Version 12.3.1 (7 Feb 2020)
- Now redirect overlay has its own URL to open
## Version 12.3.0 (6 Feb 2020)
- Added ability to export into JPG files (see Configure Export)
- Replaced Show Last Info menu item by Show Change Log
- Accelerated export data for Element Inspector
## Version 12.2.0 (4 Feb 2020)
- Added Redirect Overlays (see [details](https://spectrum.chat/puzzle-publisher/general/12-2-0-released~77f1e2a4-e3df-4667-b0bb-9067efce29ec))
## Version 12.1.2 (20 Jan 2020)
- Show a version of published mockups in navigation bar
- Allow browser to handle its own keyboard shortcuts (Cmd+L on mac)
## Version 12.1.1 (10 Jan 2020)
- Hotfix for 12.1.0 (Element Inspector can not be closed using "m" key)
## Version 12.1.0 (09 Jan 2020)
- Improved search in Gallery
## Version 12.0.4 (26 Dec 2019)
- Hide fixed panes under modal shadow
## Version 12.0.3 (25 Dec 2019)
- Element Inspector shows LESS token values
## Version 12.0.2 (24 Dec 2019)
- Element Inspector works for overlays too
## Version 12.0.1 (17 Dec 2019)
- Fix overflow on center layout (by @form-follows-function)
## Version 12.0.0 (13 Dec 2019)
1) Added artboard transition animations.
You can select an animation it in Configure Artboard > Transitions.
Overlays use FADE animation by default.
Animation for standalone pages and modals is not stable for now. Will be improved.
2) You can send custom CSS styles to Viewer placing "<SOME LIB>-viewer.css" file together with any enabled library
## Version 11.6.1 (5 Dec 2019)
- Fixed Element Inspector (Issue #11)
- Fixed overlays in modal
- Removed modal scroller (Issue #12)
## Version 11.6.0 (3 Dec 2019)
- Added "Replace the previous overlay if called from overlay" option to Artboard Overlay setttings
## Version 11.5.4 (28 Nov 2019)
- Improved hotspot highlighting key logic to allow users to make screenshots on macOS without highlighted hotposts
- Element Insprector now shows style library names
## Version 11.5.3 (13 Nov 2019)
- fixed crash on broken symbols
## Version 11.5.2 (5 Nov 2019)
- test Sketch update system
## Version 11.5.1 (5 Nov 2019)
- test Sketch update system
## Version 11.5.0 (5 Nov 2019)
- Improved Configure Artboard dialog (added tabs and images)
## Version 11.4.1 (30 Oct 2019)
- Several fixed and improvements for Version Viewer
## Version 11.4.0 (29 Oct 2019)
- Completed image difff viewer (added Shift+Left and Shift+Right to switch diff mode from keyboard)
- Click inside an overlay outside of any hotspots should not close it
## Version 11.3.0 (28 Oct 2019)
- Render fixed layers as standlanone layer, not as a part of artboard image (to support non-rectangle layers )
- Improved image diffs
- Fixed: Empty Sketch document opening in background when run via Sketchtool (by Arek Talun)
- Improved token inspector
## Version 11.2.0 (24 Oct 2019)
- Added Search in Gallery
- Show changed screen differences
## Version 11.0.4 (23 Oct 2019)
- Fixed zoom disabling for wide artboards (PART II)
## Version 11.0.3 (22 Oct 2019)
- Fixed zoom disabling for wide artboards
- Fixed *artboard exclusion
- Link in Telegram post opens a page with Version Info mode enabled by default
- Added context option currentPath to provide destination path for prototype
## Version 11.0.2 (4 Oct 2019)
- Fixed undo of changes which a user did beforee exporting
- Added "Up-to-down" artboard sorting
## Version 11.0.1 (2 Oct 2019)
- Added additional checks to skip wrong page objects
- Fixed JSON generator
## Version 11.0.0 (2 Oct 2019)
- Totally changed a way to apply symbol overrides in order to make it compatible with Smart Layouts
## Version 10.3.3 (26 Sep 2019)
- Fixed rendering of artboard with fixed layers (on some installations Sketch was too lazy) (part II)
## Version 10.3.2 (24 Sep 2019)
- Fixed "Hotspot top center" and "Hotspot top right corner" overlay aligmnent modes
- Fixed rendering of artboard with fixed layers (on some installations Sketch was too lazy)
## Version 10.3.1 (23 Sep 2019)
- Hotspot in fixed panel aligned to bottom opens overlay also aligned to bottom
## Version 10.3.0 (16 Sep 2019)
- Redesigned Gallery
## Version 10.2.2 (14 Sep 2019)
- Add additional check for valid libraries
- Disabled unstable image compression
## Version 10.2.1 (13 Sep 2019)
- Icon updated
## Version 10.2.0 (06 Sep 2019)
- Added Version Viewer (requires [server tools|https://github.com/ingrammicro/puzzle-publisher/tree/master/server_tools]) installed on your WWW server)
## Version 10.1.0 (03 Sep 2019)
- Added Announce Changes feature for mockups published on SFTP (requires [server tools|https://github.com/ingrammicro/puzzle-publisher/tree/master/server_tools]) installed on your WWW server.
## Version 10.0.2 (02 Sep 2019)
- Fixed "Export selected page artboards" feature
## Version 10.0.1 (28 Aug 2019)
- Fixed mouse-over artboards for "page scrolled down" case
- Added Up/Down Version feature for mockups published on SFTP (requires [server support](https://raw.githubusercontent.com/ingrammicro/puzzle-publisher/master/server_tools/folder_info.php))
## Version 10.0.0 (20 Aug 2019)
- Moved from https://github.com/MaxBazarov/exporter/
================================================
FILE: Hints.md
================================================
# Puzzle Publisher plugin Hints
## [Hint 1](#hint1): Use post-processing to inject your own information in generated HTML
The main index.html contains a special placeholder **\<!\-\-VERSION\-\-\>**.
<ul id="nav-title">
<li><div class="nav-title-label">Screen title <!--VERSION--></div><div class="title">Title</div></li>
</ul>
You can replace it with some your own information, for example — you can show prototype version here.
The following command uses "sed" tool.
sed -i '' "s/<!--VERSION-->/(v123)/g" "index.html"
In the same way you can add inject your own CSS file by locating to <!--HEAD_INJECT--> code.
## [Hint 2](#hint2): How to set external link for overrided symbol hotspot
Sometimes you need to set an external URL for hotspot target. You can't use "Set External Link for layer" command in this case because it's not possible to select some of symbol childs.
But you can follow the another way.
- Create small empty artboard
- Use "Set External Link for layer" command for this artboard
- Select this artobard as a overrided target for your hotsport
- Run Export to HTML to see a result
[Illustration](https://github.com/ingrammicro/puzzle-publisher/raw/master/tests/Pictures/Link-ExternalArtboard.png), [Example file](https://github.com/ingrammicro/puzzle-publisher/raw/master/tests/Link-ExternalArtboard.sketch)
## [Hint 3](#hint3): How to set a start/home page for a prototype
Select "Prototyping > Use Artboard as Start Point" menu item to mark/unmark the selected artboard as home.
## [Hint 4](#hint4): How to export Sketch document to HTML using command line
Run the following command (don't forget to inject a path to your file into a "--context" JSON file)
/Applications/Sketch.app/Contents/Resources/sketchtool/bin/sketchtool --without-activating=YES --new-instance=No run ~/Library/Application\ Support/com.bohemiancoding.sketch3/Plugins/PuzzlePublisher.sketchplugin "cmdRun" --context='{"file":"/Users/baza/GitHub/puzzle-publisher/tests/Links2.sketch","commands":"sync,export,publish,save"}'
## [Hint 5](#hint5): How to see the plugin log
tail -f ~/Library/Logs/com.bohemiancoding.sketch3/Plugin\ Log.log
## [Hint 6](#hint6): How to change a browser page background for all documents
Add "@MainBackground@" magic string to any libary symbol layer name to use its background color as a default color for browser pages
## [Hint 7](#hint7): How to treat symbols as artboards
See discission [here](https://github.com/ingrammicro/puzzle-publisher/discussions/34)
================================================
FILE: Ideas.md
================================================
### Add page transition effects
### Auto transition to other artboard after delay
New artboard setting:
**Transition to artboard**
[ To Artboard Selector]
### Open HTML in special window with webpage inside (not in a standalone browser)
### Add check for Sketch-compatible version
### Mass-operation for artboards and layers
Example: Setup Auto-transition for many artboard at once
================================================
FILE: LICENSE
================================================
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
{one line to give the program's name and a brief idea of what it does.}
Copyright (C) {year} {name of author}
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
{project} Copyright (C) {year} {fullname}
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
================================================
FILE: PuzzlePublisher.sketchplugin/Contents/Sketch/cmd-actions.js
================================================
@import "lib/ga.js";
@import "lib/utils.js";
@import "constants.js";
function onStartup(context) {
const Settings = require('sketch/settings')
const installedBefore = Settings.settingForKey(SettingKeys.PLUGIN_INSTALLED)
if (!installedBefore) {
track(TRACK_INSTALLED)
Settings.setSettingForKey(SettingKeys.PLUGIN_INSTALLED, 1)
} else {
track(TRACK_STARTED)
}
}
================================================
FILE: PuzzlePublisher.sketchplugin/Contents/Sketch/cmdline-functions.js
================================================
@import "exporter/exporter-run.js"
// osascript -e 'quit app "Sketch"'
const example = `
/Applications/Sketch.app/Contents/Resources/sketchtool/bin/sketchtool --without-activating=YES --new-instance=No run ~/Library/Application\ Support/com.bohemiancoding.sketch3/Plugins/PuzzlePublisher.sketchplugin "cmdRun" --context='{"file":"/Users/baza/GitHub/puzzle-publisher/tests/Links2.sketch","commands":"sync,export,publish,save,close"}'
`
function syncDocumentStyles(styles)
{
log(" SYNCING " + styles.length + " STYLES ...")
for (var style of styles)
{
if (null == style.getLibrary()) continue // we need only library-based style
if (!style.syncWithLibrary())
{
log(" Failed to sync symbol " + style.name)
}
}
}
function syncDocument(document)
{
const jSymbols = document.getSymbols()
if (Settings.documentSettingForKey(document, SettingKeys.DOC_SKIP_AUTOSYNC) == 1)
{
log(" SKIP SYNCINC")
return
}
log(" SYNCING " + jSymbols.length + " SYMBOLS ...")
for (var master of jSymbols)
{
if (null == master.getLibrary()) continue // we need only library-based master
if (!master.syncWithLibrary())
{
log(" Failed to sync symbol " + master.name)
for (var i of master.getAllInstances())
{
log(" instance: " + i.name)
}
}
}
syncDocumentStyles(document.sharedTextStyles)
syncDocumentStyles(document.sharedLayerStyles)
}
function exportDocument(context, runOptions)
{
log(" EXPORTING...")
runExporter(context, runOptions)
}
function publishDocument(context, document)
{
log(" PUBLISHING...")
context.fromCmd = true
const publisher = new Publisher(context, document.sketchObject);
publisher.authorName = "[BOT]";
publisher.message = "--TELE";
publisher.publish();
}
function showError(error)
{
log(error + "\n")
log("Command line example:")
log(example + "\n")
}
function saveDocument(document)
{
log(" SAVING DOCUMENT...")
document.save(err =>
{
if (err)
{
log(" Failed to save a document. Error: " + err)
}
})
}
function closeDocument(document)
{
log(" CLOSING DOCUMENT...")
document.close()
}
var cmdRun = function (context)
{
let Document = require('sketch/dom').Document
// Parse command line arguments
let path = context.file + ""
if ('' == path)
{
return showError("context.file is not specified")
}
log("PROCESS " + path)
let argCommands = context.commands + ""
if ('' == argCommands)
{
return showError("context.commands is not specified")
}
const commandsList = argCommands.split(',')
const allCommands = ['save', 'sync', 'export', 'publish', 'close']
const cmds = {}
for (var cmd of allCommands)
{
cmds[cmd] = commandsList.includes(cmd)
}
// Open Sketch document
Document.open(path, (err, document) =>
{
if (err || !document)
{
log("ERROR: Can't open " + path)
return
}
if (cmds.sync) syncDocument(document)
if (cmds.export)
{
const runOptions = {
cmd: "exportHTML",
mode: context.mode,
fromCmd: true,
nDoc: document.sketchObject,
currentPage: context.currentPageID ? document.getLayerWithID(context.currentPageID) : undefined,
customArtboardHeight: context.customArtboardHeight,
customArtboardWidth: context.customArtboardWidth,
}
//
if (Constants.EXPORT_MODE_CURRENT_PAGE == context.mode)
{
} else if (Constants.EXPORT_MODE_SELECTED_ARTBOARDS == context.mode)
{
runOptions.selectedArtboards = context.selectedArtboardIDS.split(",").map(id => document.getLayerWithID(id))
}
//
exportDocument(context, runOptions)
}
if (cmds.save) saveDocument(document)
if (cmds.publish) publishDocument(context, document)
if (cmds.save) saveDocument(document)
if (cmds.close) closeDocument(document)
})
};
================================================
FILE: PuzzlePublisher.sketchplugin/Contents/Sketch/constants.js
================================================
const Constants = {
TAB_SIZE: 2,
HOTSPOT_PADDING: 0,
LOGGING: false,
SERVER_ANNOUNCE_SCRIPT: "announce.php",
IMAGES_DIRECTORY: "images/",
PREVIEWS_DIRECTORY: "previews/",
FULLIMAGE_DIRECTORY: "full/",
DEST_PROTO_DIRECTORY: "data/",
DEST_RESOURCES_DIRECTORY: "resources/",
PLUGIN_IDENTIFIER: "com.cloudblue.sketch.exporter",
TEMP_PAGE_PREFIX: "(temp)",
SORT_RULE_X: 0,
SORT_RULE_SKETCH: 1,
SORT_RULE_REVERSIVE_SKETCH: 2,
SORT_RULE_Y: 3,
SORT_RULE_OPTIONS: ["Left-to-right", "Sketch default", "Reversive Sketch default", "Up-to-down"],
FONT_SIZE_FORMAT_SKETCH: 0,
FONTSIZE_FORMAT_OPTIONS: ["Sketch sizes", "Linux-specific sizes"],
POSITION_DEFAULT: 0,
POSITION_TOP: 1,
POSITION_CENTER: 2,
EXPORT_MODE_SELECTED_ARTBOARDS: 0,
EXPORT_MODE_CURRENT_PAGE: 1,
DEF_BACK_COLOR: "#646464",
//
SITE_CHANGELOG_URL: "https://github.com/ingrammicro/puzzle-publisher/blob/master/CHANGELOG.md",
//
ASSETS_FOLDER_PREFIX: "_pt-assets",
SYMBOLTOKENFILE_POSTFIX: "inspector.json",
CSSFILE_POSTFIX: "viewer.css",
VARSFILE_POSTFIX: "vars.json",
///
INT_LAYER_NAME_BACKCOLOR: "@MainBackground@",
INT_LAYER_NAME_SITEICON: "@SiteIcon@",
INT_LAYER_NAME_SPACER_PART: "Spacer@",
INT_LAYER_NAME_SPACER: "@Spacer@",
INT_LAYER_NAME_XSPACER: "@XSpacer@",
INT_LAYER_NAME_YSPACER: "@YSpacer@",
INT_LAYER_NAME_REDIRECT: "@Redirect@",
//
ARTBOARD_TYPE_REGULAR: 0,
ARTBOARD_TYPE_MODAL: 1,
ARTBOARD_TYPE_EXTERNAL_URL: 2,
ARTBOARD_TYPE_OVERLAY: 3,
//
ARTBOARD_TRANS_ANIM_NONE: 0,
ARTBOARD_TRANS_ANIM_SLIDEIN_UP: 1,
ARTBOARD_TRANS_ANIM_SLIDEIN_LEFT: 2,
ARTBOARD_TRANS_ANIM_FADE: 3,
ARTBOARD_TRANS_ANIM_SLIDEIN_RIGHT: 4,
ARTBOARD_TRANS_ANIM_SLIDEIN_DOWN: 5,
//
ARTBOARD_OVERLAY_BY_EVENT_CLICK: 0,
ARTBOARD_OVERLAY_BY_EVENT_MOUSEOVER: 1,
//
ARTBOARD_OVERLAY_PIN_HOTSPOT: 0,
ARTBOARD_OVERLAY_PIN_PAGE: 1,
//
ARTBOARD_OVERLAY_PIN_HOTSPOT_UNDER_LEFT: 0,
ARTBOARD_OVERLAY_PIN_HOTSPOT_UNDER_CENTER: 1,
ARTBOARD_OVERLAY_PIN_HOTSPOT_UNDER_RIGHT: 2,
ARTBOARD_OVERLAY_PIN_HOTSPOT_TOP_LEFT: 3,
ARTBOARD_OVERLAY_PIN_HOTSPOT_TOP_CENTER: 4,
ARTBOARD_OVERLAY_PIN_HOTSPOT_TOP_RIGHT: 5,
ARTBOARD_OVERLAY_PIN_HOTSPOT_BOTTOM_RIGHT: 6,
ARTBOARD_OVERLAY_PIN_HOTSPOT_UP_CENTER: 7,
//
ARTBOARD_OVERLAY_PIN_PAGE_TOP_LEFT: 0,
ARTBOARD_OVERLAY_PIN_PAGE_TOP_CENTER: 1,
ARTBOARD_OVERLAY_PIN_PAGE_TOP_RIGHT: 2,
ARTBOARD_OVERLAY_PIN_PAGE_BOTTOM_LEFT: 3,
ARTBOARD_OVERLAY_PIN_PAGE_BOTTOM_CENTER: 4,
ARTBOARD_OVERLAY_PIN_PAGE_BOTTOM_RIGHT: 5,
ARTBOARD_OVERLAY_PIN_PAGE_CENTER: 6,
//
//
OLD_ARTBOARD_OVERLAY_ALIGN_HOTSPOT_LEFT: 0,
OLD_ARTBOARD_OVERLAY_ALIGN_HOTSPOT_CENTER: 1,
OLD_ARTBOARD_OVERLAY_ALIGN_HOTSPOT_RIGHT: 2,
OLD_ARTBOARD_OVERLAY_ALIGN_TOP_LEFT: 3,
OLD_ARTBOARD_OVERLAY_ALIGN_TOP_CENTER: 4,
OLD_ARTBOARD_OVERLAY_ALIGN_TOP_RIGHT: 5,
OLD_ARTBOARD_OVERLAY_ALIGN_CENTER: 6,
OLD_ARTBOARD_OVERLAY_ALIGN_BOTTOM_LEFT: 7,
OLD_ARTBOARD_OVERLAY_ALIGN_BOTTOM_CENTER: 8,
OLD_ARTBOARD_OVERLAY_ALIGN_BOTTOM_RIGHT: 9,
OLD_ARTBOARD_OVERLAY_ALIGN_HOTSPOT_TOP_LEFT: 10,
OLD_ARTBOARD_OVERLAY_ALIGN_HOTSPOT_TOP_CENTER: 11,
OLD_ARTBOARD_OVERLAY_ALIGN_HOTSPOT_TOP_RIGHT: 12,
OLD_ARTBOARD_OVERLAY_ALIGN_HOTSPOT_TOP_RIGHT_ALIGN_RIGHT: 13,
LAYER_OVERLAY_DEFAULT: 0,
LAYER_OVERLAY_TRANSP_TOP: 1,
LAYER_OVERLAY_TRANSP_LEFT: 2,
LAYER_OVERLAY_VSCROLL: 3,
LAYER_VSCROLL_NONE: 0,
LAYER_VSCROLL_DEFAULT: 1,
LAYER_VSCROLL_ALWAYS: 2,
LAYER_VSCROLL_NEVER: 3,
//
GA_ID: "UA-84277242-5",
//
CURL_PATH: "/usr/bin/curl"
};
const TRACK_INSTALLED = "installed"
const TRACK_STARTED = "started"
const TRACK_EXPORT_DIALOG_SHOWN = "export-dialog-shown"
const TRACK_EXPORT_DIALOG_CLOSED = "export-dialog-closed" // cmd:ok,cancel
const TRACK_EXPORT_COMPLETED = "export-completed"
const TRACK_PUBLISH_DIALOG_SHOWN = "publish-dialog-shown"
const TRACK_PUBLISH_DIALOG_CLOSED = "publish-dialog-closed" // cmd:ok,cancel
const TRACK_PUBLISH_COMPLETED = "publish-completed"
const TRACK_PUBLISH_MIRO_DIALOG_SHOWN = "publish-miro-dialog-shown"
const TRACK_PUBLISH_MIRO_DIALOG_CLOSED = "publish-miro-dialog-closed" // cmd:ok,cancel
const TRACK_PUBLISH_MIRO_COMPLETED = "publish-miro-completed"
const PublishKeys = {
SHOW_OUTPUT: false,
TMP_FILE: "publish.sh",
RESOURCES_FOLDER: "scripts",
}
const SettingKeys = {
PLUGIN_INFO_11: "pluginShown11",
PLUGIN_POSITION: "positon",
PLUGIN_FILETYPE: "pluginFileType",
PLUGIN_DONT_OPEN_BROWSER: "dontOpenBrowser",
PLUGIN_COMPRESS: "pluginCompress",
PLUGIN_DONT_RETINA_IMAGES: "dontRetinaImages",
PLUGIN_DISABLE_ZOOM: "pluginDisableZoom",
PLUGIN_COMMENTS_URL: "commentsURL",
PLUGIN_GOOGLE_CODE: "googleCode",
PLUGIN_EXPORT_JS_CODE: "pluginExportJSCode",
PLUGIN_SERVERTOOLS_PATH: "pluginServerToolsPath",
PLUGIN_AUTHOR_NAME: "pluginAuthorName",
PLUGIN_AUTHOR_EMAIL: "pluginAuthorEmail",
PLUGIN_EXPORT_MODE: "exportMode",
PLUGIN_HIDE_NAV: "hideNavigation",
PLUGIN_SORT_RULE: "pluginSortRule",
PLUGIN_FONTSIZE_FORMAT: "PLUGIN_FONTSIZE_FORMAT",
PLUGIN_DISABLE_HOTSPOTS: "pluginDisableHotspots",
PLUGIN_ASK_CUSTOM_SIZE: "PLUGIN_ASK_CUSTOM_SIZE",
PLUGIN_EXPORT_CUSTOM_WIDTH: "PLUGIN_EXPORT_CUSTOM_WIDTH2",
PLUGIN_EXPORT_CUSTOM_HEIGHT: "PLUGIN_EXPORT_CUSTOM_HEIGHT2",
PLUGIN_EXPORT_DISABLE_LIB_ARTBOARDS: "PLUGIN_EXPORT_DISABLE_LIB_ARTBOARDS",
PLUGIN_EXPORT_DISABLE_INSPECTOR: "PLUGIN_EXPORT_DISABLE_INSPECTOR",
PLUGIN_PUBLISH_LOGIN: "publishLogin",
PLUGIN_PUBLISH_SITEROOT: "publishSiteRoot",
PLUGIN_PUBLISH_SECRET: "PLUGIN_PUBLISH_SECRET",
PLUGIN_PUBLISH_SSH_PORT: "publishSSHPort",
PLUGIN_PUBLISH_LAST_MSG: "PLUGIN_PIBLISH_LAST_MSG",
PLUGIN_PUBLISH_CURL_PATH: "PLUGIN_PUBLISH_CURL_PATH",
PLUGIN_EXPORTING_URL: "pluginExportingURL",
PLUGIN_SHARE_IFRAME_SIZE: "pluginShareiFrameSize",
PLUGIN_PUBLISH_MIRO_ENABLED: "PLUGIN_PUBLISH_MIRO_ENABLED",
PLUGIN_INSTALLED: "pluginInstalled",
PLUGIN_GA_DISABLED: "pluginGADisabled",
PLUGIN_LOGDEBUG_ENABLED: "pluginLogDebugEnabled",
PLUGIN_USER_ID:"PLUGIN_USER_ID",
ARTBOARD_TYPE: "artboardType",
ARTBOARD_DISABLE_FIXED: "ARTBOARD_DISABLE_FIXED",
LEGACY_ARTBOARD_MODAL: "artboardOverlay", //legacy, replaced by ARTBOARD_TYPE
LEGACY_ARTBOARD_MODAL_SHADOW: "artboardOverlayShadow", // replaced by ARTBOARD_SHADOW, Outdated on 14 Frev 2018
OLD_ARTBOARD_OVERLAY_ALIGN: "artboardOverlayPosition",
ARTBOARD_SHADOW: "artboardShadow",
ARTBOARD_DISABLE_AUTOSCROLL: "artboardDisableAutoScroll",
ARTBOARD_TRANS_TO_NEXT_SECS: "artboardTransNextSecs",
ARTBOARD_TRANS_ANIM_TYPE: "artboardTransAnimType",
ARTBOARD_OVERLAY_BY_EVENT: "artboardOverlayByEvent",
ARTBOARD_OVERLAY_PIN: "artboardOverlayPin",
ARTBOARD_OVERLAY_PIN_HOTSPOT: "artboardOverlayPinHotspot",
ARTBOARD_OVERLAY_PIN_PAGE: "artboardOverlayPinPage",
ARTBOARD_OVERLAY_OVERFIXED: "artboardOverFixed",
ARTBOARD_OVERLAY_ALSOFIXED: "artboardAlsoFixed",
ARTBOARD_OVERLAY_CLOSE_PREVOVERLAY: "artboardClosePrevOverlay",
DOC_EXPORTING_URL: "docExportingURL", // legacy, replaced by PLUGIN_EXPORTING_URL
DOC_PUBLISH_MIRO_BOARD: "DOC_PUBLISH_MIRO_BOARD",
DOC_PUBLISH_MIRO_BOARDID: "DOC_PUBLISH_MIRO_BOARDID",
DOC_PUBLISH_COMPRESS: "docPublishCompress",
DOC_DISABLE_FIXED_LAYERS: "docDisablFixedLayers",
DOC_PUBLISH_VERSION: "mockupsVersion",
DOC_PUBLISH_REMOTE_FOLDER: "remoteFolder",
DOC_CUSTOM_HIDE_NAV: "docCustomHideNavigation",
DOC_CUSTOM_SORT_RULE: "docCustomSortRule", // How to sort artboards
DOC_CUSTOM_FONTSIZE_FORMAT: "DOC_CUSTOM_FONTSIZE_FORMAT", // How to show font size in Element Inspector
DOC_BACK_COLOR: "docBackColor",
DOC_SKIP_AUTOSYNC: "DOC_SKIP_AUTOSYNC",
DOC_OWNER_NAME: "DOC_OWNER_NAME",
DOC_OWNER_EMAIL: "DOC_OWNER_EMAIL",
LAYER_ANNOTATIONS: "layerAnnotations",
LAYER_OVERLAY_TYPE: "layerOverlayType",
LAYER_EXTERNAL_LINK: "externalLink",
LAYER_EXTERNAL_LINK_BLANKWIN: "layerNewWindow",
LAYER_COMMENT: 'layerComment',
LAYER_FIXED_SHADOW_TYPE: "LAYER_FIXED_SHADOW_TYPE",
LAYER_VSCROLL_TYPE: "LAYER_VSCROLL_TYPE"
};
================================================
FILE: PuzzlePublisher.sketchplugin/Contents/Sketch/exporter/PZArtboard.js
================================================
@import("constants.js")
@import("lib/utils.js")
@import("exporter/PZLayer.js")
@import("exporter/PZDoc.js")
Sketch = require('sketch/dom')
class PZArtboard extends PZLayer
{
constructor(slayer)
{
if (DEBUG) exporter.logMsg("PZArtboard.create name=" + slayer.name)
// init Artboard own things !!! before object construction !!!
let artboardType = exporter.Settings.layerSettingForKey(slayer, SettingKeys.ARTBOARD_TYPE)
if (undefined == artboardType || '' == artboardType)
{
if (exporter.Settings.layerSettingForKey(slayer, SettingKeys.LEGACY_ARTBOARD_MODAL) == 1)
{
artboardType = Constants.ARTBOARD_TYPE_MODAL // use legacy setting
} else
artboardType = Constants.ARTBOARD_TYPE_REGULAR // set default 0 value
}
let externalArtboardURL =
exporter.Settings.layerSettingForKey(slayer, SettingKeys.LAYER_EXTERNAL_LINK)
if (externalArtboardURL != undefined && ('' == externalArtboardURL || 'http://' == externalArtboardURL))
externalArtboardURL = undefined
// Resize before exporting
const needResize = exporter.customArtboardFrame && Constants.ARTBOARD_TYPE_REGULAR == artboardType && undefined == externalArtboardURL
if (needResize)
{
if (exporter.customArtboardFrame.width > 0)
slayer.frame.width = exporter.customArtboardFrame.width
if (exporter.customArtboardFrame.height > 0)
slayer.frame.height = exporter.customArtboardFrame.height
}
super(slayer, undefined)
this.overlayLayers = []
this.fixedLayers = [] // list of layers which are configured as fixed
this.nextLinkIndex = 0 // we need it to generate uniq id of the every link
this.imageLayers = [] // list of all Image childs
this.shadowLayers = undefined // list of layer with shaows (needs to show overlay shadow)
// check if the page name is unique in document
if (this.name in pzDoc.artboardsDict)
{
// we need to find a new name
for (let i = 1; i < 1000; i++)
{
const newName = this.name + "(" + i + ")"
if (!(newName in pzDoc.artboardsDict))
{
// found new unique name!
this.name = newName
break
}
}
}
// init Artboard own things
this.artboardType = artboardType
this.isModal = Constants.ARTBOARD_TYPE_MODAL == this.artboardType
this.externalArtboardURL = externalArtboardURL
if (this.isModal || Constants.ARTBOARD_TYPE_OVERLAY == this.artboardType)
{
this.showShadow = exporter.Settings.layerSettingForKey(this.slayer, SettingKeys.ARTBOARD_SHADOW)
if (undefined != this.showShadow)
this.showShadow = this.showShadow == 1
else
{
const legacyShadow = exporter.Settings.layerSettingForKey(this.slayer, SettingKeys.LEGACY_ARTBOARD_MODAL_SHADOW)
if (undefined != legacyShadow && Constants.ARTBOARD_TYPE_MODAL == this.artboardType)
{
this.showShadow = legacyShadow
} else
{
this.showShadow = true
}
}
}
const disableFixed = exporter.Settings.layerSettingForKey(this.slayer, SettingKeys.ARTBOARD_DISABLE_FIXED)
this.disableFixedLayers = (disableFixed != undefined && disableFixed > 0) ? disableFixed == 1 : exporter.disableFixedLayers
this.overlayOverFixed = exporter.Settings.layerSettingForKey(this.slayer, SettingKeys.ARTBOARD_OVERLAY_OVERFIXED) == 1
{
var overlayAlsoFixed = exporter.Settings.layerSettingForKey(this.slayer, SettingKeys.ARTBOARD_OVERLAY_ALSOFIXED)
this.overlayAlsoFixed = overlayAlsoFixed != undefined ? overlayAlsoFixed : true
}
this.overlayClosePrevOverlay = exporter.Settings.layerSettingForKey(this.slayer, SettingKeys.ARTBOARD_OVERLAY_CLOSE_PREVOVERLAY) == 1
this.disableAutoScroll =
exporter.Settings.layerSettingForKey(this.slayer, SettingKeys.ARTBOARD_DISABLE_AUTOSCROLL)
this.transNextSecs =
exporter.Settings.layerSettingForKey(this.slayer, SettingKeys.ARTBOARD_TRANS_TO_NEXT_SECS)
if (undefined != this.transNextSecs && '' == this.transNextSecs)
this.transNextSecs = undefined
this.transAnimType = exporter.Settings.layerSettingForKey(this.slayer, SettingKeys.ARTBOARD_TRANS_ANIM_TYPE)
if (undefined == this.transAnimType)
this.transAnimType = Constants.ARTBOARD_TYPE_OVERLAY == this.artboardType ?
Constants.ARTBOARD_TRANS_ANIM_FADE :
Constants.ARTBOARD_TRANS_ANIM_NONE
if (Constants.ARTBOARD_TRANS_ANIM_NONE != this.transAnimType)
{
exporter.enableTransitionAnimation = true
}
this.overlayByEvent = exporter.Settings.layerSettingForKey(this.slayer, SettingKeys.ARTBOARD_OVERLAY_BY_EVENT)
if (this.overlayByEvent == undefined || this.overlayByEvent == "") this.overlayByEvent = 0
this.oldOverlayAlign = exporter.Settings.layerSettingForKey(this.slayer, SettingKeys.OLD_ARTBOARD_OVERLAY_ALIGN)
if (this.oldOverlayAlign == undefined || this.oldOverlayAlign == "") this.oldOverlayAlign = 0
this.overlayPin = exporter.Settings.layerSettingForKey(this.slayer, SettingKeys.ARTBOARD_OVERLAY_PIN)
if (this.overlayPin == undefined)
{
const newValues = Utils.upgradeArtboardOverlayPosition(this.oldOverlayAlign)
this.overlayPin = newValues.pinTo
this.overlayPinHotspot = newValues.hotspotTo
this.overlayPinPage = newValues.pageTo
} else
{
this.overlayPinHotspot = exporter.Settings.layerSettingForKey(this.slayer, SettingKeys.ARTBOARD_OVERLAY_PIN_HOTSPOT)
this.overlayPinPage = exporter.Settings.layerSettingForKey(this.slayer, SettingKeys.ARTBOARD_OVERLAY_PIN_PAGE)
}
}
collectLayers(space)
{
//if(DEBUG) exporter.logMsg(space+"PZArtboard.collectLayers() name="+this.name)
this.childs = this.collectAChilds(this.slayer.layers, space + " ")
}
export()
{
this._exportImages()
this._findFixedPanelHotspots()
//this._exportOverlayLayers()
this._pushIntoStoryData(this.index)
}
//------------------- FIND HOTSPOTS WHICH LOCATE OVER FIXED HOTPOSTS ----------------------------
//------------------- AND MOVE THEM INTO FIXED LAYER SPECIAL HOTSPOTS ---------------------------
_findFixedPanelHotspots()
{
function isChild(parent,child){
if(!child.parent) return false
if(child.parent==parent) return true
return isChild(parent,child.parent)
}
for (var l of this.fixedLayers)
{
for (let hIndex = 0; hIndex < this.hotspots.length; hIndex++)
{
// move hotspot from artboard hotspots to fixed layer hotspots
let hotspot = this.hotspots[hIndex]
let frame = l.frame
if(l.isVertScroll){
if(!isChild(l,hotspot.owner)) continue
//const maskedLayer = l.isVertScroll ? l.findMaskLayer():null
//frame = maskedLayer ? maskedLayer.frame : l.frame
}else{
}
if (hotspot.r.insideRectangle(frame))
{
if (!hotspot.fixedAncestorID || hotspot.fixedAncestorID == l.objectID)
{
this.hotspots.splice(hIndex--, 1)
hotspot.r.x -= l.frame.x
hotspot.r.y -= l.frame.y
l.hotspots.push(hotspot)
}
}
}
}
}
//------------------ GENERATE STORY.JS FILE ------------------
_pushIntoStoryData(pageIndex)
{
const mainName = this.name
if (DEBUG) exporter.logMsg("process main artboard " + mainName);
pzDoc.totalImages++
let data = {}
data['id'] = this.objectID
data['groupID'] = String(this.slayer.parent.id)
data['index'] = pageIndex
data['image'] = Utils.toFilename(mainName + '.' + exporter.fileType, false)
if (exporter.retinaImages)
data['image2x'] = Utils.toFilename(mainName + '@2x.' + exporter.fileType, false)
data['width'] = this.frame.width
data['height'] = this.frame.height
data['x'] = this.frame.x
data['y'] = this.frame.y
data['title'] = mainName
if (this.transNextSecs != undefined)
{
data['transNextMsecs'] = parseFloat(this.transNextSecs) * 1000
}
data['transAnimType'] = this.transAnimType
if (this.disableAutoScroll)
{
data['disableAutoScroll'] = true
}
{
var layoutGrid = this.nlayer.layout() // class: MSLayoutGrid
if (!layoutGrid) layoutGrid = MSDefaultLayoutGrid.defaultLayout();
if (layoutGrid)
{
var grid = {
offset: layoutGrid.horizontalOffset(),
totalWidth: layoutGrid.totalWidth(),
numberOfColumns: layoutGrid.numberOfColumns(),
columnWidth: layoutGrid.columnWidth(),
gutterWidth: layoutGrid.gutterWidth()
}
data['layout'] = grid
}
}
if (this.isModal)
{
data['type'] = 'modal'
data['isModal'] = true
data['showShadow'] = this.showShadow ? 1 : 0
} else if (this.externalArtboardURL != undefined && this.externalArtboardURL != '')
{
data['type'] = 'external'
} else if (Constants.ARTBOARD_TYPE_OVERLAY == this.artboardType)
{
data['type'] = 'overlay'
// try to find a shadow
if (this.showShadow)
{
const shadowInfo = this._getShadowLayerShadowInfo()
if (shadowInfo)
{
data['overlayShadow'] = shadowInfo.style
data['overlayShadowX'] = shadowInfo.x
}
} else if ((Constants.ARTBOARD_OVERLAY_PIN_HOTSPOT == this.overlayPin) && (Constants.ARTBOARD_OVERLAY_PIN_HOTSPOT_TOP_LEFT == this.overlayPinHotspot))
{
const shadowInfo = this._findLayersShadowInfo()
if (shadowInfo)
{
data['overlayShadowX'] = shadowInfo.x
}
}
data['overlayByEvent'] = this.overlayByEvent
data['overlayPin'] = this.overlayPin
data['overlayPinHotspot'] = this.overlayPinHotspot
data['overlayPinPage'] = this.overlayPinPage
data['overlayOverFixed'] = !!this.overlayOverFixed
data['overlayAlsoFixed'] = !!this.overlayAlsoFixed
data['overlayClosePrevOverlay'] = !!this.overlayClosePrevOverlay
} else
{
data['type'] = 'regular'
}
// add fixed layers
data['fixedPanels'] = this._getFixedLayersForJSON()
// add hotspots
data['links'] = this._buildHotspots(this.hotspots)
if (this.overlayRedirectTargetPage != undefined)
data['overlayRedirectTargetPage'] = this.overlayRedirectTargetPage
let js = pageIndex ? ',' : '';
js += "$.extend(new ViewerPage()," + JSON.stringify(data, null, ' ') + ")\n"
exporter.storyData.pages.push(data)
}
_getShadowLayerShadowInfo()
{
if (!this.shadowLayers) return undefined
// sort layers to find largest
const resorted = this.shadowLayers.sort((l1,l2)=>l1.frame.height<l2.frame.height)
//
return resorted[0].getShadowInfo()
}
_findLayersShadowInfo(layers = undefined, checkKeepFixedShadow = false)
{
if (layers === undefined) layers = this.childs
//
let shadowInfo = undefined
for (const l of layers)
{
if (checkKeepFixedShadow && l.keepFixedShadow) continue
shadowInfo = l.getShadowInfo()
if (shadowInfo && shadowInfo.style.fills!==undefined && shadowInfo.style.fills.length)
{
break
}
shadowInfo = this._findLayersShadowInfo(l.childs, checkKeepFixedShadow)
if (shadowInfo) break
}
return shadowInfo
}
clearRefsBeforeJSON()
{
super.clearRefsBeforeJSON()
this.overlayLayers = undefined
this.fixedLayers = undefined
this.imageLayers = undefined
}
addShadowLayer(layer){
if(this.shadowLayers===undefined) this.shadowLayers = []
this.shadowLayers.push(layer)
}
addLayerAsExportableImage(layer)
{
layer.imageIndex = this.imageLayers.length
this.imageLayers.push(layer)
if (DEBUG) exporter.logMsg("Add image layer: " + layer.name)
}
_getFixedLayersForJSON()
{
let recs = []
if (this.fixedLayers.length)
{
const mainName = this.name
const foundPanels = []
for (var l of this.fixedLayers)
{
let type = l.fixedType
if (type == "")
{
exporter.logError("pushFixedLayersIntoJSStory: can't understand fixed panel type for artboard '" + this.name
+ "' layer='" + l.name + "' layer.frame=" + l.frame + " this.frame=" + this.frame)
continue
}
pzDoc.totalImages++
if (!l.isFloat && foundPanels[type])
{
exporter.logError("pushFixedLayersIntoJSStory: found more than one panel with type '" + type + "' for artboard '"
+ this.name + "' layer='" + l.name + "' layer.frame=" + l.frame + " this.frame=" + this.frame)
const existedPanelLayer = foundPanels[type]
exporter.logError("pushFixedLayersIntoJSStory: already exists panel layer='" + existedPanelLayer.name
+ "' layer.frame=" + existedPanelLayer.frame)
continue
}
foundPanels[type] = l
const fileNamePostfix = !(l.isFloat || l.isVertScroll) ? "" : ('-' + l.fixedIndex)
const rec = {
constrains: l.constrains,
x: l.frame.x,
y: l.frame.y,
width: l.frame.width,
height: l.frame.height,
type: type,
index: l.fixedIndex,
isFloat: l.isFloat,
isVertScroll: l.isVertScroll,
divID: l.layerDivID != undefined ? l.layerDivID : "",
links: this._buildHotspots(l.hotspots, true),
image: Utils.quoteString(Utils.toFilename(mainName, false) + fileNamePostfix + '.' + exporter.fileType)
}
if (l.isVertScroll)
{
const maskLayer = l.findMaskLayer()
rec.mskH = maskLayer.frame.height - (l.frame.y - maskLayer.frame.y)
rec.vst = l.vScrollType
}
if (exporter.retinaImages)
rec.image2x = Utils.quoteString(Utils.toFilename(mainName, false) + fileNamePostfix + '@2x.' + exporter.fileType, false)
// setup shadow
const shadowInfo = this._findLayersShadowInfo([l], true)
if (shadowInfo)
{
rec.shadow = shadowInfo.style
rec.shadowX = shadowInfo.x
}
recs.push(rec)
}
}
return recs
}
_buildHotspots(srcHotspots, isParentFixed = false)
{
let newHotspots = []
for (var hotspot of srcHotspots)
{
const newHotspot = {
rect: hotspot.r,
isParentFixed: isParentFixed,
}
if (hotspot.linkType == 'back')
{
newHotspot.action = 'back'
} else if (hotspot.linkType == 'artboard' && pzDoc.artboardsDict[hotspot.artboardID] != undefined
&& pzDoc.artboardIDsDict[hotspot.artboardID].externalArtboardURL != undefined
)
{
newHotspot.url = pzDoc.artboardIDsDict[hotspot.artboardID].externalArtboardURL
} else if (hotspot.linkType == 'artboard')
{
const targetPage = pzDoc.artboardIDsDict[hotspot.artboardID]
if (targetPage == undefined)
{
if (DEBUG) exporter.logMsg("undefined artboard: '" + hotspot.artboardName + '"');
continue
}
const targetPageIndex = targetPage.index;
newHotspot.page = targetPageIndex
} else if (hotspot.linkType == 'href')
{
newHotspot.url = hotspot.href
} else if (hotspot.target != undefined)
{
newHotspot.target = hotspot.target
} else
{
if (DEBUG) exporter.logMsg("_pushHotspotIntoJSStory: Uknown hotspot link type: '" + hotspot.linkType + "'")
}
if (hotspot.target != undefined)
{
newHotspot.target = hotspot.target
}
if (hotspot.overlayRedirect && newHotspot.page != undefined)
{
this.overlayRedirectTargetPage = newHotspot.page
}
newHotspot.index = this.nextLinkIndex++
newHotspots.push(newHotspot)
}
return newHotspots
}
//------------------ GENERATE IMAGES ------------------
_getImageName(scale, injectScaleToName = true, panelPostix = "")
{
const suffix = injectScaleToName && scale == 2 ? "@2x" : "";
return Utils.toFilename(this.name, false) + panelPostix + suffix + "." + exporter.fileType;
}
// exportType: full, layer, preview, artboard
_exportImage(exportType, nlayer = null, panelPostix = "", forFixedLayer = false)
{
nlayer = nlayer || this.nlayer
if (DEBUG) exporter.logMsg(" exportImage() for " + nlayer.name())
let scales = null
let imageBasePath = exporter.imagesPath
let injectScaleToName = true
if ('artboard' == exportType || 'layer' == exportType)
{
scales = exporter.retinaImages ? [1,2] : [1]
} else if ('full' == exportType)
{
scales = [2]
imageBasePath = exporter.fullImagesPath
injectScaleToName = false
} else if ('preview' == exportType)
{
scales = [522 / nlayer.frame().width()]
imageBasePath = exporter.previewsImagePath
injectScaleToName = false
}
for (let scale of scales)
{
const imageName = this._getImageName(scale, injectScaleToName, panelPostix)
const imagePath = imageBasePath + imageName
let slice = null
slice = MSExportRequest.exportRequestsFromExportableLayer(nlayer).firstObject();
slice.scale = scale;
slice.saveForWeb = false;
slice.format = exporter.fileType;
const bounds = nlayer.absoluteRect();
if (forFixedLayer) slice.setRect(bounds.rect())
exporter.ndoc.saveArtboardOrSlice_toFile(slice, imagePath);
}
}
// new experimental code to export images
// we don't use it because it doesn't allow to set a file name
_exportImage2(scales, slayer)
{
if (DEBUG) exporter.logMsg("exportImage()");
const imagePath = exporter.imagesPath // + this._getImageName(scales)
exporter.logMsg('_exportImage2 name=' + slayer.name)
const options = {
scales: scales,
output: exporter.imagesPath,
overwriting: true,
'save-for-web': true,
formats: exporter.fileType
}
Sketch.export(slayer, options)
}
_exportImages()
{
if(this.artboardType===Constants.ARTBOARD_TYPE_EXTERNAL_URL) return
//this._getAllLayersMatchingPredicate(Sketch.getSelectedDocument().sketchObject)
if (DEBUG) exporter.logMsg("PZArtboard._exportImages: running... " + this.name)
let scales = exporter.retinaImages ? [1, 2] : [1]
// export fixed panels to their own image files
this._exportFixedLayersToImages(scales)
// hide fixed panels to generate a main page content without fixed panels
// and their artefacts (shadows)
this._hideFixedLayers(true)
this._exportImage("artboard")
// export images for Element Inspector
if (exporter.enabledJSON)
{
this._exportImageLayers()
}
// show fixed panels back
// ! temporary disabled because an exported image still shows hidden layers
this._hideFixedLayers(false)
if (exporter.exportFullImages && (this.artboardType===Constants.ARTBOARD_TYPE_REGULAR || this.artboardType===Constants.ARTBOARD_TYPE_OVERLAY))
{
// export full image
if (DEBUG) exporter.logMsg("PZArtboard._exportImages: export full image")
this._exportImage("full")
}
// export preview images (to use by Gallery and Inspector Viewer)
this._exportImage("preview")
if (DEBUG) exporter.logMsg("PZArtboard._exportImages: done!")
}
_exportOverlayLayers()
{
if (DEBUG) exporter.logMsg('_exportOverlayLayers: running')
let scales = exporter.retinaImages ? [1, 2] : [1]
for (const layer of this.overlayLayers)
{
// log('_exportOverlayLayers: '+layer.name)
// need
const artboard = this._findArtboardByName(layer.name + "@")
if (!artboard) continue
//
this._exportImage("layer", artboard.sketchObject, "-" + layer.name)
//
}
if (DEBUG) exporter.logMsg('_exportOverlayLayers: done!')
}
_exportImageLayers()
{
if (DEBUG) exporter.logMsg('_exportImageLayers: running')
for (var layer of this.imageLayers)
{
const path = exporter._outputPath + "/" + layer._buildImageURL()
if (DEBUG) exporter.logMsg(path)
if ("Image" == layer.slayer.type)
{
// The folowing code source — https://stackoverflow.com/a/17510651/9384835
let image = layer.slayer.image.nsimage
let cgRef = [image CGImageForProposedRect: nil context: nil hints: nil]
let newRep = [[NSBitmapImageRep alloc] initWithCGImage: cgRef]
[newRep setSize: [image size]];
let pngData = [newRep representationUsingType: NSPNGFileType properties: nil];
[pngData writeToFile: path atomically: true];
[newRep autorelease];
} else if ("Group" == layer.slayer.type)
{
if (DEBUG) exporter.logMsg("Export group")
const slice = MSExportRequest.exportRequestsFromExportableLayer(layer.nlayer).firstObject();
slice.scale = 2
slice.saveForWeb = false
slice.format = exporter.fileType
exporter.ndoc.saveArtboardOrSlice_toFile(slice, path)
}
}
if (DEBUG) exporter.logMsg('_exportImageLayers: done')
}
_exportFixedLayersToImages(scales)
{
for (var layer of this.fixedLayers)
{
layer.calculateFixedType()
// temporary disable fixed panel shadows
let orgShadows = undefined
let shadowInfo = this._findLayersShadowInfo([layer], true)
if (shadowInfo)
{
orgShadows = shadowInfo.layer.slayer.style.shadows
shadowInfo.layer.slayer.style.shadows = []
}
let orgHeight = undefined, maskLayer = undefined, orgResizesContent = undefined
if (layer.overlayType === Constants.LAYER_OVERLAY_VSCROLL)
{
const layerIndex = layer.parent.childs.indexOf(layer)
maskLayer = layer.parent.childs[layerIndex - 1]
maskLayer.nlayer.hasClippingMask = false
//
// check artboard height
if ((layer.frame.y + layer.frame.height) > this.frame.height)
{
orgHeight = this.frame.height
orgResizesContent = this.nlayer.resizesContent
this.nlayer.resizesContent = false
this.slayer.frame.height = layer.frame.y + layer.frame.height + 10
}
}
// for div and float fixed layer we need to generate its own image files
if (layer.isFloat || layer.isVertScroll)
{
//this._exportImage2('1, 2',layer.parent.slayer)
const l = layer.parent.isSymbolInstance ? layer : layer
this._exportImage("layer", l.nlayer, "-" + layer.fixedIndex, true)
}
// restore original artboard height
if (orgHeight !== undefined)
{
this.slayer.frame.height = orgHeight
this.nlayer.resizesContent = orgResizesContent
}
// restore original fixed panel shadows
if (shadowInfo)
{
shadowInfo.layer.slayer.style.shadows = orgShadows
}
if (maskLayer) maskLayer.nlayer.hasClippingMask = true
}
}
_hideFixedLayers(hide)
{
const show = !hide
for (var layer of this.fixedLayers)
{
// we need to hide/show only div and float panels
if (undefined == layer.slayer.style) continue
if (layer.isFloat || layer.isVertScroll)
{
layer.slayer.hidden = hide
}
// temporary remove fixed panel shadows
if (hide)
{
layer.fixedShadows = layer.slayer.style.shadows
layer.slayer.style.shadows = []
}
// restore original fixed panel shadows
if (show)
{
layer.slayer.style.shadows = layer.fixedShadows
}
// Commented to make it worked in Sketch 65
//Sketch.getSelectedDocument().sketchObject.documentData().invalidateAffectedSymbolInstancesWithDiff(layer.objectID)
}
}
}
================================================
FILE: PuzzlePublisher.sketchplugin/Contents/Sketch/exporter/PZDoc.js
================================================
@import("constants.js")
@import("lib/utils.js")
Sketch = require('sketch/dom')
const replaceValidKeys = [
"x", "y", "w", "h",
"c", // childs
"s", // smName
"l", //styleName
"text", "comment", "sharedLib"]
// smName: symbol master Name
function replacer(key, value)
{
// Pass known keys and array indexes
if (value != undefined && (replaceValidKeys.indexOf(key) >= 0 || !isNaN(key)))
{
return value
}
return undefined
}
var pzDoc = null
class PZDoc
{
constructor()
{
pzDoc = this
var Document = require('sketch/dom').Document
this.sDoc = Document.getSelectedDocument()
this.mPages = []
this.mAllLayers = []
this.mLinkedLayers = []
this.usedLibs = {}
this.swatchesMap = undefined
this.sSymbols = {}
this.artboardCount = 0
this.startArtboardIndex = 0
this.mAllArtboards = []
this.artboardsDict = {}
this.artboardIDsDict = {}
this.jsLibs = undefined
}
collectData()
{
// init required data
this._buildSymbolDict()
// build local pages
const mPages = []
if (Constants.EXPORT_MODE_CURRENT_PAGE == exporter.exportOptions.mode)
{
// build only current page
const mPage = new PZPage(Sketch.fromNative(exporter.exportOptions.currentPage))
mPage.collectData()
mPages.push(mPage)
} else if (Constants.EXPORT_MODE_SELECTED_ARTBOARDS == exporter.exportOptions.mode)
{
// build only selected artboards on current page
const mPage = new PZPage(Sketch.fromNative(exporter.exportOptions.currentPage))
mPage.collectData(exporter.exportOptions.selectedArtboards)
mPages.push(mPage)
} else
{
// build all pages and artboards
for (var sPage of this.sDoc.pages)
{
if (DEBUG) exporter.logMsg("PZDoc:collectData() process page=" + sPage.name)
if (exporter.filterAster && sPage.name.indexOf("*") == 0) continue
// create new local Page object
const mPage = new PZPage(sPage)
mPage.collectData()
mPages.push(mPage)
}
}
this.mPages = mPages
///
this._collectLibArtboards()
}
buildLinks()
{
exporter.logMsg('PZDoc.buildLinks: running')
for (var mLayer of this.mLinkedLayers)
{
mLayer.buildLinks(' ');
}
exporter.logMsg('PZDoc.buildLinks: stop')
}
export()
{
exporter.logMsg(" PZDoc:run running...")
/// Export pages
this.totalImages = 0
for (var page of this.mPages)
{
page.export();
}
exporter.logMsg(" PZDoc:run done!")
}
_getLibAssetsPath(lib)
{
return Utils.cutLastPathFolder(lib.sDoc.path) + "/" + Constants.ASSETS_FOLDER_PREFIX + "/" + lib.jsLib.name
}
getSymbolData()
{
// load library inspector file
let inspectors = ""
let vars = ""
const libs = this._getLibraries()
for (const lib of libs)
{
if (!this.usedLibs[lib.jsLib.name]) continue
const libAssetsPath = this._getLibAssetsPath(lib)
//Utils.cutLastPathFolder(lib.sDoc.path) + "/" + lib.jsLib.name
const pathToSymbolTokens = libAssetsPath + "/" + Constants.SYMBOLTOKENFILE_POSTFIX
exporter.logMsg('pathToSymbolTokens = ' + pathToSymbolTokens + " name=" + lib.jsLib.name)
const inspectorData = Utils.readFile(pathToSymbolTokens)
if (inspectors != "") inspectors += ","
inspectors += '"' + Utils.toFilename(lib.jsLib.name, true, false) + '":' + (inspectorData ? inspectorData : "{}")
//
const pathToVars = libAssetsPath + "/" + Constants.VARSFILE_POSTFIX
const varsData = Utils.readFile(pathToVars)
if (vars != "") vars += ","
vars += "'" + Utils.toFilename(lib.jsLib.name, true, false) + "':" + (varsData ? varsData : "{}")
}
return "const SYMBOLS_DICT = {" + inspectors + "};\n" +
"const TOKENS_DICT = {" + vars + "};"
}
getCSSIncludes()
{
const cssIncludes = []
const libs = this._getLibraries()
for (const lib of libs)
{
const libAssetsPath = this._getLibAssetsPath(lib)
//const libPath = Utils.cutLastPathFolder(lib.sDoc.path) + "/" + lib.jsLib.name
// Copy library CSS to Resources folder
{
const pathSrcCSS = libAssetsPath + "/" + Constants.CSSFILE_POSTFIX
const cssFileName = Utils.toFilename(lib.jsLib.name + ".css")
const css = Utils.readFile(pathSrcCSS)
if (undefined != css)
{
const pathResultCSS = exporter.createDestFile(cssFileName, Constants.DEST_RESOURCES_DIRECTORY)
if (!Utils.writeToFile(css, pathResultCSS))
{
exporter.logError("getSymbolData: can't library save CSS to " + pathResultCSS)
}
cssIncludes.push(cssFileName)
}
}
//
}
return cssIncludes
}
getJSON()
{
exporter.logMsg(" getJSON: cleanup before saving...")
this.mAllLayers.forEach(l =>
{
l.clearRefsBeforeJSON()
});
this.mAllArtboards.forEach(l =>
{
l.clearRefsBeforeJSON()
});
exporter.logMsg(" getJSON: running...")
const json = JSON.stringify(this.mAllArtboards)//, replacer, null)
exporter.logMsg(" getJSON: done!")
return json
}
undoChanges()
{
if (!exporter.enabledJSON) Utils.actionWithType(this.sDoc.sketchObject, "MSUndoAction").doPerformAction(nil);
this.jsLibs = []
}
//////////////////////////// PUBLIC HELPERS ///////////////////////
// return index of new artboard
addArtboard(mArtboard)
{
this.artboardsDict[mArtboard.name] = mArtboard
this.artboardIDsDict[mArtboard.objectID] = mArtboard
this.mAllArtboards.push(mArtboard)
if (mArtboard.nlayer.isFlowHome())
{
this.startArtboardIndex = this.artboardCount
}
return this.artboardCount++
}
// return Sketch native object
findArtboardByID(artboardID)
{
let artboard = this.artboardIDsDict[artboardID]
if (artboard) return artboard
return this._findLibraryArtboardByID(artboardID)
}
getLayerWithID(id)
{
return this.sDoc.getLayerWithID(id)
}
getSymbolMasterByID(id)
{
if (!(id in this.sSymbols))
{
exporter.logMsg('getSymbolMasterByID can not find symbol by ID=' + id)
return undefined
}
return this.sSymbols[id]
}
// result: array [{sn: swatch name,ln: library name},..] OR null
getSwatchInfoByID(swatchID)
{
// load all swatched initially
if (undefined == this.swatchesMap)
{
this.swatchesMap = {}
// load library colors
var libs = require('sketch/dom').getLibraries()
libs.filter(l => l.valid && l.enabled).forEach(function (lib)
{
var stylesReferences = null
try
{
stylesReferences = lib.getImportableSwatchReferencesForDocument(this.sDoc)
}
catch (error)
{
stylesReferences = null
}
if (!stylesReferences) return
stylesReferences.forEach(function (s)
{
this.swatchesMap[s.id] = {
sn: s.name,
ln: lib.name
}
}, this)
}, this)
// load local colors
// log(require('sketch').globalAssets.colors)
}
// find
// log("getSwatchInfoByID")
const res = this.swatchesMap[swatchID]
if (!res) return null
// log(this.swatchesMap)
// log(res.ln)
this.usedLibs[res.ln] = true
return res
}
//////////////////////////// PRIVATE ///////////////////////
_collectLibArtboards()
{
for (const mLayer of this.mLinkedLayers)
{
if (mLayer.flow && mLayer.flow.targetID && !(mLayer.flow.targetID in this.artboardIDsDict))
{
const mArtboard = this._findLibraryArtboardByID(artboardID)
}
}
}
_sortArboards()
{
const exportOptions = exporter.exportOptions
for (const mPage of this.mPages)
{
mPage.sortArboards()
}
}
// return Sketch native object
_findLibraryArtboardByID(artboardID)
{
if (exporter.ignoreLibArtboards) return false
if (DEBUG) exporter.logMsg("findLibraryArtboardByID running... artboardID:" + artboardID)
// find Sketch Artboard
var sArtboard = undefined
var lib = undefined
for (lib of this._getLibraries())
{
if (DEBUG) exporter.logMsg("findLibraryArtboardByID getLayerWithID for lib " + lib.jsLib.name)
sArtboard = lib.sDoc.getLayerWithID(artboardID)
if (sArtboard) break
}
// check artboard existing
if (!sArtboard)
{
if (DEBUG) exporter.logMsg("findLibraryArtboardByID FAILED")
return false
}
// Create new page
this._buildSymbolDict(lib.sDoc)
const mPage = new PZPage(null)
mPage.collectData([sArtboard])
this.mPages.push(mPage)
return this.artboardIDsDict[artboardID]
}
_getLibraries()
{
if (undefined != this.jsLibs) return this.jsLibs
exporter.logMsg("_getLibraries: start")
this.jsLibs = []
var libraries = require('sketch/dom').getLibraries()
for (const jsLib of libraries)
{
if (!jsLib.valid || !jsLib.enabled) continue
exporter.logMsg("_getLibraries: try to load document for library " + jsLib.name + "")
const sDoc = jsLib.getDocument()
if (!sDoc)
{
exporter.logMsg("_getLibraries: can't load document for library " + sDoc.path + "")
continue
}
this.jsLibs.push({
jsLib: jsLib,
sDoc: sDoc
})
}
exporter.logMsg("_getLibraries: finish")
return this.jsLibs
}
_buildSymbolDict(sDoc = null)
{
if (!sDoc) sDoc = this.sDoc
for (const sSymbol of sDoc.getSymbols())
{
const sid = sSymbol.symbolId
if (sid in this.sSymbols) continue
this.sSymbols[sid] = sSymbol
}
}
}
================================================
FILE: PuzzlePublisher.sketchplugin/Contents/Sketch/exporter/PZLayer.js
================================================
const { off } = require("process")
@import("constants.js")
@import("lib/utils.js")
@import("exporter/PZDoc.js")
var Sketch = require('sketch/dom')
var Flow = require('sketch/dom').Flow
var Text = require('sketch/dom').Text
var Style = require('sketch/dom').Style
var LAYER_COUNTER = 0
var ResizingConstraint = {
NONE: 0,
RIGHT: 1 << 0,
WIDTH: 1 << 1,
LEFT: 1 << 2,
BOTTOM: 1 << 3,
HEIGHT: 1 << 4,
TOP: 1 << 5
}
const ICON_TAG = " / ic-" // Use this string to find icon symbol
const alignMap2 = {
[Text.Alignment.left]: "left",
[Text.Alignment.center]: "center",
[Text.Alignment.right]: "right",
[Text.Alignment.justify]: "justify"
}
const vertAlignMap2 = {
[Text.VerticalAlignment.top]: "top",
[Text.VerticalAlignment.center]: "middle",
[Text.VerticalAlignment.bottom]: "bottom",
}
const weights = [
{ label: 'thin', sketch: 2, css: 100, title: "Thin" },
{ label: 'extra-light', sketch: 3, css: 200, title: "Extra Light" },
{ label: 'light', sketch: 4, css: 300, title: "Light" },
{ label: 'regular', sketch: 5, css: 400, title: "Regular" },
{ label: 'medium', sketch: 6, css: 500, title: "Medium" },
{ label: 'semi-bold', sketch: 8, css: 600, title: "Semi Bold" },
{ label: 'semibold', sketch: 8, css: 600, title: "Semi Bold#2" },
{ label: 'bold', sketch: 9, css: 700, title: "Bold" },
{ label: 'extra-bold', sketch: 10, css: 800, title: "Extra Bold" },
{ label: 'black', sketch: 11, css: 900, title: "Black" },
{ label: 'black', sketch: 12, css: 900, title: "Black" },
{ label: 'solid', sketch: 14, css: 900, title: "Solid" },
]
class PZLayer
{
// nlayer: ref to native MSLayer Layer
// myParent: ref to parent MyLayer
constructor(sLayer, myParent)
{
this.nlayer = sLayer.sketchObject
this.name = sLayer.name
this.parent = myParent
this.objectID = String(sLayer.id)
this.ii = LAYER_COUNTER++
this.originalID = undefined
this.slayer = sLayer
this.artboard = myParent ? myParent.artboard : this
this.isParentFixed = undefined != myParent && (myParent.isFixed || myParent.isParentFixed)
// define type
this.isArtboard = false
this.isGroup = false
this.isSymbolInstance = false
this.customLink = undefined
this.isLink = false
this.tp = undefined
if ("Group" == sLayer.type || "Artboard" == sLayer.type) this.isGroup = true
let symbolID = null
let targetID = null
//////////////////////////////////////////////////////// RESTORE SYMBOL INFO
// find a symbol and flow information saved by sketchtool during --detach (see export.sh)
if (this.isGroup && exporter.enabledJSON)
{
const info = this.nlayer.userInfo()
if (null != info)
{
const detach = info['com.sketch.detach']
if (detach && detach['symbolMaster']) symbolID = detach['symbolMaster']['symbolID']
}
}
// save found symbol information
const sSymbolMaster = symbolID ? pzDoc.getSymbolMasterByID(symbolID) : undefined
if (sSymbolMaster)
{
// This layer is Symbol instance
const smName = sSymbolMaster.name + ""
if (smName.indexOf(ICON_TAG) > 0)
{
// Found Icon symbol instance
this.tp = "Icon"
function getLayerStyleName(layer)
{
return layer && layer.isGroup && layer.styleName !== "" ? layer.styleName : undefined
}
this.styleName = getLayerStyleName(myParent) || getLayerStyleName(myParent.parent)
this.smName = smName
this.isGroup = false
//
if (exporter.enabledJSON)
{
this.pr = this._buildIconsPropsForJSON()
}
} else
{
// Regular symbol instance
this.isSymbolInstance = true
this.targetId = targetID
// prepare data for Element Inspector
const lib = sSymbolMaster.getLibrary()
this.smName = smName
if (lib)
{
this.sharedLib = lib.name
pzDoc.usedLibs[lib.name] = true
} else
{
}
}
} else
{
// Check layer shared styles
this.smName = undefined
// prepare data for Element Inspector
var sharedStyle = this.slayer.sharedStyle
if (sharedStyle)
{
this.styleName = sharedStyle.name
const lib = sharedStyle.getLibrary()
if (lib)
{
this.sharedLib = lib.name
pzDoc.usedLibs[lib.name] = true
}
}
if ("Text" == sLayer.type)
{
this.text = this.slayer.text + ""
}
this.cv = this._getColorVariable()
}
this.targetId = this.slayer.flow ? this.slayer.flow.targetId : null
//////////////////////////////////////////////////////// Process artboard-special things
if ("Artboard" == sLayer.type) this.isArtboard = true
if (!this.isArtboard)
{
pzDoc.mAllLayers.push(this)
// Check if this layer has a link
if (this.targetId)
{
this.isLink = true
} else
{
const externalLinkHref = exporter.Settings.layerSettingForKey(this.slayer, SettingKeys.LAYER_EXTERNAL_LINK)
if (externalLinkHref != null && externalLinkHref != "" && externalLinkHref != "http://")
{
this.externalLinkHref = externalLinkHref
this.isLink = true
}
}
if (this.isLink)
{
pzDoc.mLinkedLayers.push(this)
}
// Check if this layer shadow will be used as artboard overlay shadow
if (Constants.ARTBOARD_TYPE_OVERLAY == this.artboard.artboardType)
{
if (this.slayer && this.slayer.style
&& (
(this.slayer.style.shadows && this.slayer.style.shadows.length)
||
(this.slayer.style.innerShadows && this.slayer.style.innerShadows.length)
)
)
{
this.artboard.addShadowLayer(this)
}
}
}
//////////////////////////////////////////////////////// COMMENT
var comment = exporter.Settings.layerSettingForKey(this.slayer, SettingKeys.LAYER_COMMENT)
if (undefined != comment && '' != comment)
{
this.comment = comment
}
//////////////////////////////////////////////////////// IMAGE MASKED
if ("Image" == sLayer.type && this.nlayer.isMasked())
{
// sLayer.hidden = true
this.isMasked = true
}
//////////////////////////////////////////////////////// EXPORTABLE LAYER
else if ("Image" == sLayer.type || (("Group" == sLayer.type || "ShapePath" == sLayer.type) && undefined != sLayer.exportFormats && sLayer.exportFormats.length > 0))
{
this.artboard.addLayerAsExportableImage(this)
}
this.childs = []
this.hotspots = []
//////////////////////////////////////////////////////// RECALCULTE COORDS FROM ABS TO LOCAL
this.frame = Utils.copyRectToRectangle(this.nlayer.absoluteRect())
if (!this.isArtboard)
{
this.frame.x -= this.artboard.frame.x
this.frame.y -= this.artboard.frame.y
}
//////////////////////////////////////////////////////// SAVE CONSTRAINS
if (myParent != undefined) this.constrains = this._calculateConstrains()
//////////////////////////////////////////////////////// LAYER IS SCROLLABLE CONTAINER
if (sLayer.type === "Group")
{
const vScrollType = Utils.getLayerSetting(sLayer, SettingKeys.LAYER_VSCROLL_TYPE, Constants.LAYER_VSCROLL_NONE)
if (vScrollType !== Constants.LAYER_VSCROLL_NONE)
{
this.addSelfAsFixedLayerToArtboad(Constants.LAYER_OVERLAY_VSCROLL)
this.vScrollType = vScrollType
}
}
//////////////////////////////////////////////////////// OVERLAY & FIXED
if (!this.isArtboard && !this.artboard.disableFixedLayers && !this.isParentFixed)
{
var overlayType = exporter.Settings.layerSettingForKey(this.slayer, SettingKeys.LAYER_OVERLAY_TYPE)
if (undefined == overlayType || '' == overlayType)
overlayType = Constants.LAYER_OVERLAY_DEFAULT
if (this.nlayer.isFixedToViewport() || overlayType != Constants.LAYER_OVERLAY_DEFAULT)
{
this.addSelfAsFixedLayerToArtboad(overlayType)
}
}
//////////////////////////////////////////////////////// LAYER PROVIDES PAGE BACKGROUND
// check special internal properties
// check: if this layer provides browser window background color
if ("" == exporter.backColor)
{
while (true)
{
if (this.name.indexOf(Constants.INT_LAYER_NAME_BACKCOLOR) < 0) break
let fills = this.slayer.style.fills
if (undefined == fills) break
fills = fills.filter(function (el) { return el.enabled })
if (0 == fills.length) break
exporter.backColor = fills[0].color
break
}
}
//////////////////////////////////////////////////////// LAYER PROVIDES FAVICON
// check: if this layer provides browser favicon
if (this.name.includes(Constants.INT_LAYER_NAME_SITEICON))
{
exporter.siteIconLayer = this
}
//////////////////////////////////////////////////////// LAYER IS SPACER
// check: if this layer should be hiddden during export
if (this.name.includes(Constants.INT_LAYER_NAME_SPACER_PART)
&& (
this.name.includes(Constants.INT_LAYER_NAME_SPACER)
|| this.name.includes(Constants.INT_LAYER_NAME_XSPACER)
|| this.name.includes(Constants.INT_LAYER_NAME_YSPACER)
))
{
this.isSpacer = true
this.slayer.hidden = true
}
//////////////////////////////////////////////////////// OVERLAY REDIRECT
// check: if this layer contains special overlay
if (!this.isArtboard && this.name.indexOf(Constants.INT_LAYER_NAME_REDIRECT) >= 0)
{
this.overlayRedirect = true
}
//////////////////////////////////////////////////////// ICON OR IMAAGE
// checl if the layer is an image symbol name and we don't want to show the child parts
if (this.isSymbolInstance && this.smName.startsWith("images/"))
{
this.isImageSymbol = true
}
}
_calculateConstrains()
{
const resizingConstraint = 63 ^ this.nlayer.resizingConstraint()
const res = {
top: (resizingConstraint & ResizingConstraint.TOP) === ResizingConstraint.TOP,
bottom: (resizingConstraint & ResizingConstraint.BOTTOM) === ResizingConstraint.BOTTOM,
left: (resizingConstraint & ResizingConstraint.LEFT) === ResizingConstraint.LEFT,
right: (resizingConstraint & ResizingConstraint.RIGHT) === ResizingConstraint.RIGHT,
height: (resizingConstraint & ResizingConstraint.HEIGHT) === ResizingConstraint.HEIGHT,
width: (resizingConstraint & ResizingConstraint.WIDTH) === ResizingConstraint.WIDTH
}
return res
}
collectAChilds(sLayers, space)
{
var aLayers = []
if (undefined == sLayers)
{
exporter.logMsg("PZLayer:collectAChilds() empty sLayers. this.name=" + this.name)
}
for (const sl of sLayers.filter(l => !l.hidden || l.sketchObject.hasClippingMask()))
{
//
const al = new PZLayer(sl, this)
if (al.isGroup && !this.isImageSymbol) al.childs = al.collectAChilds(sl.layers, space + " ")
aLayers.push(al)
}
return aLayers
}
addSelfAsFixedLayerToArtboad(overlayType)
{
{
const shadowType = exporter.Settings.layerSettingForKey(this.slayer, SettingKeys.LAYER_FIXED_SHADOW_TYPE)
this.keepFixedShadow = shadowType != undefined && shadowType == 1
}
this.isFixed = true
this.overlayType = overlayType
this.fixedIndex = this.artboard.fixedLayers.length
this.artboard.fixedLayers.push(this)
}
calculateFixedType()
{
let type = "";
if (Constants.LAYER_OVERLAY_VSCROLL == this.overlayType)
{
type = 'vscroll'
} else if (Constants.LAYER_OVERLAY_TRANSP_TOP == this.overlayType)
{
type = "top";
} else if (Constants.LAYER_OVERLAY_TRANSP_LEFT == this.overlayType)
{
type = "left";
} else
type = "float"
this.fixedType = type
this.isFloat = type == 'float'
if (type == 'vscroll') this.isVertScroll = type === 'vscroll'
}
findMaskLayer()
{
const layerIndex = this.parent.childs.indexOf(this)
return this.parent.childs[layerIndex - 1]
}
buildLinks(space)
{
this._processHotspots(space)
}
getShadowInfo()
{
if (this.slayer.style == undefined || (this.slayer.style.shadows == undefined && this.slayer.style.innerShadows == undefined)) return undefined
let shadowInfo = this.getShadowSetInfo(this.slayer.style.shadows, false) || this.getShadowSetInfo(this.slayer.style.innerShadows, true)
return shadowInfo
}
getShadowSetInfo(shadows, inset)
{
if (!shadows) return
let shadowInfo = undefined
for (var shadow of shadows.filter(s => s.enabled))
{
let res = ""
//if (res != "") res += ","
if (inset) res += "inset "
res += shadow.x + "px "
if (null != shadow.y)
{
res += " " + shadow.y + "px"
if (null != shadow.blur)
{
res += " " + shadow.blur + "px"
if (null != shadow.spread)
{
res += " " + shadow.spread
}
}
}
res += " " + shadow.color
if (shadowInfo)
{
shadowInfo.style += ", " + res
} else
{
shadowInfo = {
style: res,
x: shadow.x + shadow.blur ? shadow.blur : 0,
layer: this
}
}
}
return shadowInfo
}
_processHotspots(prefix)
{
const l = this
const hotspots = []
let finalHotspot = {
r: this.frame.copy(),
linkType: 'undefined',
artboardID: null,
target: null,
overlayRedirect: this.overlayRedirect,
ancestorFixed: null,
owner:this
}
let p = this
while (!p.isArtboard)
{
if (p.nlayer.isFixedToViewport())
{
finalHotspot.fixedAncestorID = p.objectID
break
}
p = p.parent
}
while (true)
{
// check link to external URL
if (this.externalLinkHref != null)
{
const externalLink = {
'href': this.externalLinkHref,
'openNewWindow': exporter.Settings.layerSettingForKey(l.slayer, SettingKeys.LAYER_EXTERNAL_LINK_BLANKWIN) == 1
}
if (!this._specifyExternalURLHotspot(prefix + " ", finalHotspot, externalLink)) return
break
}
// check native link
if (null != l.targetId)
{
if (!this._specifyHotspot(prefix + " ", l, finalHotspot)) return
break
}
// No any link on layer
return
}
hotspots.push(finalHotspot);
// finalization
Array.prototype.push.apply(this.artboard.hotspots, hotspots);
if (DEBUG) exporter.logMsg(prefix + "_processLayerLinks")
}
_specifyHotspot(prefix, l, finalHotspot)
{
const targetArtboardID = l.targetId;
if (targetArtboardID == 'back')
{
// hande Back action
finalHotspot.linkType = "back";
if (DEBUG) exporter.logMsg(prefix + "hotspot: back")
} else if (targetArtboardID != null && targetArtboardID != "" && targetArtboardID != "null")
{
// hande direct link
let targetArtboard = pzDoc.findArtboardByID(targetArtboardID)
if (!targetArtboard)
{
exporter.logWarning("Broken link to missed artboard on layer '" + l.name + "' on artboard '" + l.artboard.name + "' target=")
return false
}
if (targetArtboard.externalArtboardURL != undefined)
{
const externalLink = {
'href': targetArtboard.externalArtboardURL,
'openNewWindow': exporter.Settings.layerSettingForKey(targetArtboard.slayer, SettingKeys.LAYER_EXTERNAL_LINK_BLANKWIN) == 1
}
finalHotspot.artboardID = targetArtboard.objectID
this._specifyExternalURLHotspot(prefix + " ", finalHotspot, externalLink)
} else
{
finalHotspot.linkType = "artboard";
finalHotspot.artboardID = targetArtboardID;
finalHotspot.href = Utils.toFilename(targetArtboard.name) + ".html";
}
} else
{
return false
}
return true
}
_specifyExternalURLHotspot(prefix, finalHotspot, externalLink)
{
if (DEBUG) exporter.logMsg(prefix + "_specifyExternalURLHotspothotspot: href")
// found external link
var href = externalLink.href
/*const regExp = new RegExp("^http(s?)://");
if (!regExp.test(href.toLowerCase())) {
href = "http://" + href;
}*/
const target = externalLink.openNewWindow ? "_blank" : null;
finalHotspot.linkType = "href"
finalHotspot.href = href
finalHotspot.target = target
return true
}
clearRefsBeforeJSON()
{
// need to cleanup temp object to allow dump it into JSON
// but keep nlayer because Exporter.exportImage() needs it
//
///
this.x = this.frame.x
this.y = this.frame.y
this.w = this.frame.width
this.h = this.frame.height
this.s = this.smName
this.l = this.styleName
this.b = this.sharedLib
if (this.keepFixedShadow) this.ks = true
if (this.childs.length) this.c = this.childs
if (!this.tp) this.tp = this.isSymbolInstance ? "SI" : this.slayer.type
if (!this.isSymbolInstance) this.n = this.name
if (this.slayer.hidden) this.hd = true
//
if (this.tp == "Icon")
{
// this.pr is already enabled
} else if ("Text" == this.slayer.type)
{
this.pr = this._buildTextPropsForJSON()
} else if ("ShapePath" == this.slayer.type || "Shape" == this.slayer.type)
{
this.pr = this._buildShapePropsForJSON()
this.tp = "ShapePath"
} else if (this.isImageSymbol)
{
this.tp = "ImageSymbol"
this.isImageSymbol = undefined
} else if ("Image" == this.slayer.type)
{
if (this.isMasked)
{
this.hd = true
this.isMasked = undefined
} else
{
this.iu = this._buildImageURL()
}
} else if (this.isVertScroll !== undefined)
{
//this.tp = "Image"
this.iu = this._buildImageURL()
this.vst = this.vScrollType
this.vScrollType = undefined
} else if (undefined != this.imageIndex)
{
this.tp = "Image"
this.iu = this._buildImageURL()
}
//
this.name = undefined
this.frame = undefined
this.width = undefined
this.height = undefined
this.constrains = undefined
this.smName = undefined
this.styleName = undefined
this.sharedLib = undefined
this.text = undefined
this.keepFixedShadow = undefined
this.childs = undefined
//
this.tempOverrides = undefined
this.slayer = undefined
//l.nlayer = undefined
this.customLink = undefined
this.nlayer = undefined
this.parent = undefined
this.artboard = undefined
this.objectID = undefined
this.isParentFixed = undefined
this.isArtboard = undefined
this.isGroup = undefined
this.isSymbolInstance = undefined
this.isLink = undefined
this.hotspots = undefined
this.targetId = undefined
this.imageIndex = undefined
this.icpn = undefined
this.icpi = undefined
}
_buildImageURL()
{
return Constants.IMAGES_DIRECTORY + Utils.toFilename(this.artboard.name, false) + "--" + this.imageIndex + "." + exporter.fileType;
}
_buildTextPropsForJSON()
{
this.tx = this.text
//
const pte = exporter.getTokensExporter()
return pte._getTextStylePropsAsText(this.slayer.style)
}
_buildShapePropsForJSON()
{
const pte = exporter.getTokensExporter()
return pte._getLayerStylePropsAsText(null, this.slayer, this.slayer.style)
}
_buildIconsPropsForJSON()
{
const pte = exporter.getTokensExporter()
if (this.parent && this.parent.slayer && this.parent.slayer.style)
return pte._getLayerStylePropsAsText(null, this.slayer, this.parent.slayer.style)
else
return undefined
}
_getColorVariable()
{
const style = this.slayer.style
if (!style || !style.sketchObject.primitiveTextStyle()) return undefined
// Try to find that color variables was used
var attributes = style.sketchObject.primitiveTextStyle().attributes()
if (!attributes || !attributes.MSAttributedStringColorAttribute || !attributes.MSAttributedStringColorAttribute.swatchID) return undefined
var swatchID = attributes.MSAttributedStringColorAttribute.swatchID()
if (!swatchID) return undefined
//
var swatchInfo = pzDoc.getSwatchInfoByID(swatchID)
return swatchInfo
}
_clearColor(color)
{
// drop FF transparency as default
if (color.length == 9 && color.substring(7).toUpperCase() == "FF")
{
color = color.substring(0, 7)
}
return color.toUpperCase()
}
exportSiteIcon()
{
const nlayer = this.nlayer
const layer = this
const imageName = "icon.png"
const imagePath = exporter._outputPath + "/resources/" + imageName;
let slice = null
slice = MSExportRequest.exportRequestsFromExportableLayer(nlayer).firstObject();
slice.scale = 1;
slice.saveForWeb = false;
slice.format = "png";
exporter.ndoc.saveArtboardOrSlice_toFile(slice, imagePath);
/*const options = {
scales: [1],
output: imagePath,
overwriting: true,
'save-for-web': true,
formats: 'png'
}
Sketch.export(this.slayer, options) */
}
}
================================================
FILE: PuzzlePublisher.sketchplugin/Contents/Sketch/exporter/PZPage.js
================================================
@import("constants.js")
@import("lib/utils.js")
Sketch = require('sketch/dom')
var PZPage_touched = false
class PZPage {
// spage: ref to Sketch Page
constructor(sPage) {
this.sPage = sPage
this.mArtboards = []
}
collectData(sArtboards = null) {
if (DEBUG) exporter.logMsg("PZPage.collectData() starting... name=" + (this.sPage ? this.sPage.name : ''))
//
if (!sArtboards) sArtboards = this.sPage.layers
// Run in sync mode, doesn't need Element Inspector data to be exported
if (!exporter.enabledJSON) {
// prepare layers for collecting
exporter.logMsg("PZPage.collectData() preparing...")
for (const sa of sArtboards) {
if ("Artboard" != sa.type) continue
if (exporter.filterAster && sa.name.indexOf("*") == 0) continue
// special trick to add some data change event to Sketch as Undo point
if (!PZPage_touched) {
sa.frame.y += 10
PZPage_touched = true
}
this._scanLayersToDetachSymbols(sa)
}
}
// collect layers
exporter.logMsg("PZPage.collectData() collecting...")
this._collectArtboards(sArtboards)
exporter.logMsg("PZPage.collectData() collected")
}
export() {
for (const a of this.mArtboards) {
a.export()
}
//// export itself
if (this.sPage != null) {
const data = {
'id': String(this.sPage.id),
name: this.sPage.name
}
exporter.storyData.groups.push(data)
}
}
// return index of new artboard
addArtboard(mArtboard) {
this.mArtboards.push(mArtboard)
mArtboard.index = pzDoc.addArtboard(mArtboard)
}
//////////////////////// PRIVATE FUNCTIONS //////////////////////////////////////
_scanLayersToDetachSymbols(sParent) {
if (DEBUG) exporter.logMsg("PZPage._scanLayersToDetachSymbols() runnning...name=" + (this.sPage ? this.sPage.name : ''))
const nParent = sParent.sketchObject
const symbolPredicate = NSPredicate.predicateWithFormat("className == %@", 'MSSymbolInstance');
const symbolInstances = nParent.children().filteredArrayUsingPredicate_(symbolPredicate);
symbolInstances.forEach(function (nl) {
var sl = Sketch.fromNative(nl)
sl = sl.detach({
recursively: true
})
if (DEBUG) exporter.logMsg("PZPage._scanLayersToDetachSymbols() symbol:" + sl.name)
}, this)
if (DEBUG) exporter.logMsg("PZPage._scanLayersToDetachSymbols() completed")
}
_collectArtboards(sArtboards) {
for (var sa of this._sortArtboards(sArtboards)) {
if ("SymbolMaster" == sa.type) continue
if ("Artboard" != sa.type) continue
if (exporter.filterAster && sa.name.indexOf("*") == 0) continue
const ma = new PZArtboard(sa)
ma.collectLayers(' ')
this.addArtboard(ma)
}
}
// Resort artboards using configuration settings
_sortArtboards(sSrcArtboards) {
var sArtboards = sSrcArtboards.slice()
if (Constants.SORT_RULE_X == exporter.sortRule) {
sArtboards.sort((
function (a, b) {
return a.frame.x != b.frame.x ? (a.frame.x - b.frame.x) : (a.frame.y - b.frame.y)
}))
} else if (Constants.SORT_RULE_Y == exporter.sortRule) {
sArtboards.sort((
function (a, b) {
return a.frame.y != b.frame.y ? (a.frame.y - b.frame.y) : (a.frame.x - b.frame.x)
}))
} else if (Constants.SORT_RULE_REVERSIVE_SKETCH == exporter.sortRule) {
sArtboards = sArtboards.reverse()
} else {
}
return sArtboards
}
}
================================================
FILE: PuzzlePublisher.sketchplugin/Contents/Sketch/exporter/exporter-run.js
================================================
@import "constants.js"
@import "exporter/exporter.js"
@import "lib/uidialog.js"
@import "lib/uipanel.js"
@import "lib/utils.js"
const Settings = require('sketch/settings')
const UI = require('sketch/ui')
const Dom = require('sketch/dom')
let exportInfo = {
timeout: undefined,
panel: undefined
}
function closePanel()
{
if (exportInfo.panel != undefined)
{
exportInfo.panel.finish()
exportInfo.panel = undefined
}
if (exportInfo.timeout != undefined)
{
exportInfo.timeout.cancel() // fibers takes care of keeping coscript around
exportInfo.timeout = undefined
}
coscript.setShouldKeepAround(false)
}
function panelSwitchFinished()
{
exportInfo.panel.addButton("cancel", " Ok ", function ()
{
closePanel()
})
}
function openBrowser(currentPath, doc)
{
const docName = doc.sketchObject.cloudName() + ""
const openPath = currentPath + "/" + docName + "/"
const fullPath = "" + openPath + (openPath.endsWith('/') ? '' : '/') + 'index.html'
NSWorkspace.sharedWorkspace().openFile(fullPath);
}
function exportHTML(currentPath, nDoc, exportOptions, context)
{
let fromCmd = ('fromCmd' in exportOptions) && exportOptions.fromCmd
const currentPage = exportOptions.currentPage || nDoc.currentPage()
new Exporter(currentPath, nDoc, currentPage, exportOptions, context);
let exportedOk = false
if (fromCmd)
{
exportedOk = exporter.exportArtboards()
track(TRACK_EXPORT_COMPLETED)
} else
{
let panel = new UIPanel("Exporting to HTML")
exportInfo.panel = panel
panel.addLabel("", "Please wait...")
panel.show()
// export HTML
coscript.setShouldKeepAround(true)
exportInfo.timeout = coscript.scheduleWithInterval_jsFunction(1, function ()
{
// Exporting...
exportedOk = exporter.exportArtboards()
//
//panelSwitchFinished()
closePanel()
track(TRACK_EXPORT_COMPLETED)
// show final message
if (exporter.errors.length > 0)
{
UI.alert('Export failed with errors', exporter.errors.join("\n\n"))
} else if (false && exporter.warnings.length > 0)
{
UI.alert('Export completed with warnings', exporter.warnings.join("\n\n"))
} else
{
UI.message('HTML exported.')
// open HTML in browser
if (!exportOptions.dontOpenBrowser)
{
openBrowser(currentPath, Dom.fromNative(nDoc))
}
}
})
}
}
function saveDocumentAs(document, filePath)
{
if (DEBUG) log(" SAVING DOCUMENT TO " + filePath)
var newFileURL = NSURL.fileURLWithPath(filePath)
document.sketchObject.writeToURL_ofType_forSaveOperation_originalContentsURL_error_(newFileURL, "com.bohemiancoding.sketch.drawing",
NSSaveOperation, nil, nil);
}
function asyncExportHTML(context, doc, exportOptions)
{
// Clone current doc to temp file
const docName = doc.sketchObject.cloudName() + ""
const tempDir = Utils.getPathToTempFolder()
const tempFile = tempDir + "/" + "tmp" + ".sketch"
saveDocumentAs(doc, tempFile)
const fileManager = NSFileManager.defaultManager()
const scriptName = "export.sh"
const scriptPath = context.plugin.url().URLByAppendingPathComponent("Contents")
.URLByAppendingPathComponent("Sketch").URLByAppendingPathComponent("scripts")
.URLByAppendingPathComponent(scriptName)
const newContext = {
file: tempFile,
name: docName,
commands: "export,close",
async: true,
mode: exportOptions.mode,
currentPageID: exportOptions.currentPage != undefined ? (exportOptions.currentPage.id + "") : undefined,
}
if (exportOptions.customArtboardHeight != "" && exportOptions.customArtboardWidth != "")
{
const customArtboardHeight = parseInt(exportOptions.customArtboardHeight)
const customArtboardWidth = parseInt(exportOptions.customArtboardWidth)
if (!isNaN(customArtboardHeight) && !isNaN(customArtboardWidth))
{
newContext.customArtboardHeight = customArtboardHeight
newContext.customArtboardWidth = customArtboardWidth
}
}
// Prepare options to send
if (exportOptions.mode === undefined)
{
} else if (exportOptions.mode === Constants.EXPORT_MODE_SELECTED_ARTBOARDS)
{
const ids = exportOptions["selectedArtboards"].map(a => a.id).join(",")
newContext.selectedArtboardIDS = ids
}
const newContextStr = JSON.stringify(newContext)
// Run other Sketch instance to export
//const result = Utils.runCommand('/bin/bash', [scriptPath, tempDir, tempFile, docName, Utils.escapeDoudleQuote(JSON.stringify(exportOptions))], true)
const result = Utils.runCommand('/bin/bash', [scriptPath, tempDir, tempFile, newContextStr], true)
return result
}
function runExporter(context, exportOptions = null)
{
if (exportOptions === null)
{
exportOptions = {
cmd: 'exportHTML'
}
}
const nDoc = exportOptions.nDoc ? exportOptions.nDoc : context.document
const doc = Dom.fromNative(nDoc)
const Settings = require('sketch/settings')
let fromCmd = ('fromCmd' in exportOptions) && exportOptions.fromCmd
const isCmdExportToHTML = exportOptions['cmd'] == "exportHTML"
var dontOpenBrowser = Settings.settingForKey(SettingKeys.PLUGIN_DONT_OPEN_BROWSER) == 1
var customWidth = Utils.getPluginSetting(SettingKeys.PLUGIN_EXPORT_CUSTOM_WIDTH)
var customHeight =Utils.getPluginSetting(SettingKeys.PLUGIN_EXPORT_CUSTOM_HEIGHT)
var askSize = Settings.settingForKey(SettingKeys.PLUGIN_ASK_CUSTOM_SIZE) == 1
var compress = Settings.settingForKey(SettingKeys.PLUGIN_COMPRESS) == 1
// ask for output path
let currentPath = context.currentPath ? context.currentPath : Settings.settingForKey(SettingKeys.PLUGIN_EXPORTING_URL)
if (currentPath == null)
{
// check legacy settings
currentPath = Settings.documentSettingForKey(doc, SettingKeys.DOC_EXPORTING_URL)
if (currentPath == null)
currentPath = ''
}
if (!fromCmd)
{
UIDialog.setUp(context);
const dialog = new UIDialog("Export to HTML", NSMakeRect(0, 0, 500, 100 + (askSize ? 100 : 0)), "Export")
dialog.removeLeftColumn()
dialog.addPathInput({
id: "path", label: "Destination folder", labelSelect: "Select Folder",
textValue: currentPath,
inlineHint: 'e.g. ~/HTML', width: 450
})
dialog.addCheckbox("open", "Open HTML in browser", !dontOpenBrowser)
if (askSize)
{
dialog.addTextInput("customWidth", "Artboard custom width (px)", customWidth, 'e.g. 1920')
dialog.addTextInput("customHeight", "Artboard custom height (px)", customHeight, 'e.g. 1080')
}
track(TRACK_EXPORT_DIALOG_SHOWN)
while (true)
{
const result = dialog.run()
if (!result)
{
track(TRACK_EXPORT_DIALOG_CLOSED, { "cmd": "cancel" })
return false
}
if (askSize)
{
customWidth = dialog.views['customWidth'].stringValue()
if (customWidth != '')
{
if (isNaN(customWidth)) continue
} else
customWidth = ''
customHeight = dialog.views['customHeight'].stringValue()
if (customHeight != '')
{
if (isNaN(customHeight)) continue
} else
customHeight = ''
}
currentPath = dialog.views['path'].stringValue() + ""
if (currentPath == "") continue
dontOpenBrowser = dialog.views['open'].state() != 1
compress = false //dialog.views['compress'].state() == 1
break
}
dialog.finish()
track(TRACK_EXPORT_DIALOG_CLOSED, { "cmd": "ok" })
Settings.setSettingForKey(SettingKeys.PLUGIN_EXPORTING_URL, currentPath)
Settings.setSettingForKey(SettingKeys.PLUGIN_DONT_OPEN_BROWSER, dontOpenBrowser)
Settings.setSettingForKey(SettingKeys.PLUGIN_COMPRESS, compress)
Settings.setSettingForKey(SettingKeys.PLUGIN_EXPORT_CUSTOM_WIDTH, customWidth)
Settings.setSettingForKey(SettingKeys.PLUGIN_EXPORT_CUSTOM_HEIGHT, customHeight)
exportOptions.dontOpenBrowser = dontOpenBrowser
exportOptions.compress = compress
if(askSize){
if (customHeight !== "") exportOptions.customArtboardHeight = customHeight
if (customWidth !== "") exportOptions.customArtboardWidth = customWidth
}
// Export in background
var enabledJSON = Settings.settingForKey(SettingKeys.PLUGIN_EXPORT_DISABLE_INSPECTOR) != 1
if (enabledJSON)
{
const result = asyncExportHTML(context, doc, exportOptions)
if (result.result == 1)
{
if (!dontOpenBrowser) openBrowser(currentPath, doc)
} else
{
UI.alert('Export failed with errors', result.output)
}
return
}
}
exportHTML(currentPath, nDoc, exportOptions, context)
};
================================================
FILE: PuzzlePublisher.sketchplugin/Contents/Sketch/exporter/exporter.js
================================================
@import("constants.js")
@import("lib/utils.js")
@import("lib/ga.js")
@import("pp-viewer/exporter/exporter-build-html.js")
@import("exporter/PZLayer.js")
@import("exporter/PZArtboard.js")
@import("exporter/PZPage.js")
@import("exporter/PZDoc.js")
@import("exporter/publisher.js") // we need it to run resize.sh script
@import("tokens/DSExporter.js")
var exporter = undefined
class Exporter
{
constructor(selectedPath, ndoc, page, exportOptions, context)
{
this.Settings = require('sketch/settings');
this.Sketch = require('sketch/dom');
this.ndoc = ndoc
this.doc = this.Sketch.fromNative(ndoc)
this.page = page;
this.context = context;
this.customArtboardFrame = undefined
this.enableTransitionAnimation = false
this.siteIconLayer = undefined
this.myLayers = []
this.errors = []
this.warnings = []
if (context['async'])
{
this.docName = context["name"] + ""
} else
{
// workaround for Sketch 52s
this.docName = this._clearCloudName(this.ndoc.cloudName() + "")
let posSketch = this.docName.indexOf(".sketch")
if (posSketch > 0)
{
this.docName = this.docName.slice(0, posSketch)
}
// @workaround for Sketch 52
}
this.initPaths(selectedPath)
this.exportOptions = exportOptions
this._readSettings()
this.filterAster = null == this.exportOptions || !('mode' in this.exportOptions) || Constants.EXPORT_MODE_SELECTED_ARTBOARDS != this.exportOptions.mode
// init global variable
exporter = this
}
_readSettings()
{
if (this.exportOptions.customArtboardWidth > 0 && this.exportOptions.customArtboardHeight > 0)
{
this.customArtboardFrame = new Rectangle(0, 0
, parseInt(this.exportOptions.customArtboardWidth, 10)
, parseInt(this.exportOptions.customArtboardHeight, 10)
)
}
this.retinaImages = this.Settings.settingForKey(SettingKeys.PLUGIN_DONT_RETINA_IMAGES) != 1
this.enabledJSON = this.Settings.settingForKey(SettingKeys.PLUGIN_EXPORT_DISABLE_INSPECTOR) != 1
this.disableFixedLayers = this.customArtboardFrame || this.Settings.documentSettingForKey(this.doc, SettingKeys.DOC_DISABLE_FIXED_LAYERS) == 1
let pluginSortRule = this.Settings.settingForKey(SettingKeys.PLUGIN_SORT_RULE)
if (undefined == pluginSortRule) pluginSortRule = Constants.SORT_RULE_X
const docCustomSortRule = this.Settings.documentSettingForKey(this.doc, SettingKeys.DOC_CUSTOM_SORT_RULE)
this.sortRule = undefined == docCustomSortRule || docCustomSortRule < 0 ? pluginSortRule : docCustomSortRule
let fontSizeFormat = this.Settings.settingForKey(SettingKeys.PLUGIN_FONTSIZE_FORMAT)
const docCustomFontSize = this.Settings.documentSettingForKey(this.doc, SettingKeys.DOC_CUSTOM_FONTSIZE_FORMAT)
if (undefined != docCustomFontSize && docCustomFontSize != 0) fontSizeFormat = docCustomFontSize
this.fontSizeFormat = undefined != fontSizeFormat ? fontSizeFormat - 1 : Constants.FONT_SIZE_FORMAT_SKETCH
let backColor = this.Settings.documentSettingForKey(this.doc, SettingKeys.DOC_BACK_COLOR)
if (undefined == backColor) backColor = ""
this.backColor = backColor
let serverTools = this.Settings.settingForKey(SettingKeys.PLUGIN_SERVERTOOLS_PATH)
if (serverTools == undefined) serverTools = ''
this.serverTools = serverTools
let fileType = Settings.settingForKey(SettingKeys.PLUGIN_FILETYPE)
if (fileType == undefined || fileType == "") fileType = "PNG"
this.fileType = fileType.toLowerCase()
// To know do we need full-size images or not
const miroEnabled = Settings.settingForKey(SettingKeys.PLUGIN_PUBLISH_MIRO_ENABLED) == 1
this.exportFullImages = miroEnabled || true
this.ignoreLibArtboards = this.Settings.settingForKey(SettingKeys.PLUGIN_EXPORT_DISABLE_LIB_ARTBOARDS) == 1
}
getManifest()
{
var manifestPath = this.context.plugin.url().URLByAppendingPathComponent("Contents").URLByAppendingPathComponent("Sketch").URLByAppendingPathComponent("manifest.json").path()
return NSJSONSerialization.JSONObjectWithData_options_error(NSData.dataWithContentsOfFile(manifestPath), 0, nil)
}
logMsg(msg)
{
const d = new Date()
log(d.getHours() + ":" + d.getMinutes() + "." + d.getSeconds() + " " + msg)
}
logWarning(text)
{
this.logMsg("[ WARNING ] " + text)
this.warnings.push(text)
}
logError(error)
{
this.logMsg("[ ERROR ] " + error)
this.errors.push(error)
}
stopWithError(error)
{
const UI = require('sketch/ui')
UI.alert('Error', error)
exit = true
}
_clearCloudName(cloudName)
{
let name = cloudName
let posSketch = name.indexOf(".sketch")
if (posSketch > 0)
{
name = name.slice(0, posSketch)
}
return name
}
getTokensExporter()
{
if (undefined == this.tokensExporter)
{
this.tokensExporter = new DSExporter(this.context)
this.tokensExporter.initForPublisher()
}
return this.tokensExporter
}
prepareFilePath(filePath, fileName)
{
const fileManager = NSFileManager.defaultManager();
const targetPath = filePath + '/' + fileName;
let error = MOPointer.alloc().init();
if (!fileManager.fileExistsAtPath(filePath))
{
if (!fileManager.createDirectoryAtPath_withIntermediateDirectories_attributes_error(filePath, true, null, error))
{
this.logError("prepareFilePath(): Can't create directory '" + filePath + "'. Error: " + error.value().localizedDescription());
return undefined
}
}
if (fileManager.fileExistsAtPath(targetPath))
{
if (!fileManager.removeItemAtPath_error(targetPath, error))
{
this.logError("prepareFilePath(): Can't remove old directory '" + targetPath + "'. Error: " + error.value().localizedDescription());
return undefined
}
}
return targetPath
}
copyStatic(resFolder)
{
const fileManager = NSFileManager.defaultManager();
const targetPath = this.prepareFilePath(this._outputPath, resFolder);
if (undefined == targetPath) return false
const sourcePath = this.context.plugin.url().URLByAppendingPathComponent("Contents").URLByAppendingPathComponent("Sketch")
.URLByAppendingPathComponent("pp-viewer").URLByAppendingPathComponent("viewer").URLByAppendingPathComponent(resFolder)
//const sourcePath = this.context.plugin.url().URLByAppendingPathComponent("Contents").URLByAppendingPathComponent("Sketch").URLByAppendingPathComponent(resFolder).path();
let error = MOPointer.alloc().init();
if (!fileManager.copyItemAtPath_toPath_error(sourcePath, targetPath, error))
{
this.logMsg(error.value().localizedDescription());
return this.logError("copyStatic(): Can't copy '" + sourcePath + "' to directory '" + targetPath + "'. Error: " + error.value().localizedDescription());
}
return true
}
startStoryData()
{
const disableHotspots = this.Settings.settingForKey(SettingKeys.PLUGIN_DISABLE_HOTSPOTS) == 1
var ownerName = Utils.getDocSetting(this.ndoc, SettingKeys.DOC_OWNER_NAME)
if ('' == ownerName) ownerName = Utils.getPluginSetting(SettingKeys.PLUGIN_AUTHOR_NAME)
var ownerEmail = Utils.getDocSetting(this.ndoc, SettingKeys.DOC_OWNER_EMAIL)
if ('' == ownerEmail) ownerEmail = Utils.getPluginSetting(SettingKeys.PLUGIN_AUTHOR_EMAIL)
this.storyData = {
docName: Utils.toFilename(this.docName),
docPath: "P_P_P",
docVersion: ExporterConstants.DOCUMENT_VERSION_PLACEHOLDER,
ownerName: ownerName,
ownerEmail: ownerEmail,
authorName: ExporterConstants.DOCUMENT_AUTHOR_NAME_PLACEHOLDER,
authorEmail: ExporterConstants.DOCUMENT_AUTHOR_EMAIL_PLACEHOLDER,
commentsURL: ExporterConstants.DOCUMENT_COMMENTS_URL_PLACEHOLDER,
hasRetina: this.retinaImages,
serverToolsPath: this.serverTools,
fontSizeFormat: this.fontSizeFormat,
fileType: this.fileType,
disableHotspots: disableHotspots,
zoomEnabled: this.Settings.settingForKey(SettingKeys.PLUGIN_DISABLE_ZOOM) != 1,
title: this.docName,
layersExist: this.enabledJSON,
centerContent: false, // because too many issues
highlightLinks: false,
pages: [],
groups: []
}
//
}
createMainHTML()
{
const pluginVer = exporter.getManifest().version
const buildOptions = {
docName: this.docName,
serverTools: this.serverTools,
backColor: this.backColor,
enableExpViewer: this.storyData.experimentalExisting,
centerContent: false, // because too many issues
loadLayers: this.enabledJSON,
cssFileNames: this.enabledJSON ? this.mDoc.getCSSIncludes() : undefined,
enableAnimations: this.enableTransitionAnimation,
generatorText: "Generated by Puzzle Publisher " + pluginVer + " plugin for Sketch.app - https://github.com/ingrammicro/puzzle-publisher"
}
const docHideNav = this.Settings.documentSettingForKey(this.doc, SettingKeys.DOC_CUSTOM_HIDE_NAV)
buildOptions.hideNav = docHideNav == undefined || docHideNav == 0 ? this.Settings.settingForKey(SettingKeys.PLUGIN_HIDE_NAV) == 1 : docHideNav == 2
let googleCode = this.Settings.settingForKey(SettingKeys.PLUGIN_GOOGLE_CODE)
if (googleCode == undefined) googleCode = ''
buildOptions.googleCode = googleCode
let jsCode = this.Settings.settingForKey(SettingKeys.PLUGIN_EXPORT_JS_CODE)
if (jsCode == undefined) jsCode = ''
buildOptions.jsCode = jsCode
if ("" == buildOptions.backColor) buildOptions.backColor = Constants.DEF_BACK_COLOR
const s = buildMainHTML(buildOptions);
const filePath = this.prepareFilePath(this._outputPath, 'index.html');
if (undefined == filePath) return false
Utils.writeToFile(s, filePath);
return true
}
compressImages()
{
if (!this.exportOptions.compress) return true
this.logMsg(" compressImages: running...")
const pub = new Publisher(this.context, this.ndoc);
pub.copyScript("compress2.sh")
var url = pub.context.plugin.urlForResourceNamed('advpng').path()
const res = pub.runScriptWithArgs("compress2.sh", [this.imagesPath, url])
if (!res.result)
{
this.logMsg(" compressImages: failed!")
} else
this.logMsg(" compressImages: done!")
pub.showOutput(res)
}
buildPreviews()
{
this.logMsg(" buildPreviews: running...")
// WE NEED THE FOLLOWING DUMMY CODE TO GET UNDO CHANGES ( see PZDoc.undoChanges() )
const pub = new Publisher(this.context, this.ndoc);
let args = ["-Z", "300", "fileName", "--out", this.imagesPath + "previews/"]
let res = pub.runToolWithArgs("/usr/bin/sips", args)
this.logMsg(" buildPreviews: done!!!!!")
}
createDestFile(fileName, folder = Constants.DEST_PROTO_DIRECTORY)
{
return this.prepareFilePath(this._outputPath + "/" + folder, fileName);
}
// result: true OR false
finishSaveStoryData()
{
const iFrameSizeSrc = this.Settings.settingForKey(SettingKeys.PLUGIN_SHARE_IFRAME_SIZE)
let iFrameSize = undefined
if (iFrameSizeSrc != undefined && iFrameSizeSrc != '')
{
const size = iFrameSizeSrc.split(':')
if (2 == size.length)
{
iFrameSize = {
width: size[0],
height: size[1]
}
}
}
this.storyData['startPageIndex'] = this.mDoc.startArtboardIndex
this.storyData['totalImages'] = this.mDoc.totalImages
if (undefined != iFrameSize)
{
this.storyData['iFrameSizeWidth'] = iFrameSize.width
this.storyData['iFrameSizeHeight'] = iFrameSize.height
}
if ("" != this.backColor)
{
this.storyData['backColor'] = this.backColor
}
// Convert data to JSON
let jsStory = "var story = " + JSON.stringify(this.storyData, null, ' ')
// And save it
const pathStoryJS = this.createDestFile('story.js')
if (undefined == pathStoryJS) return false
Utils.writeToFile(jsStory, pathStoryJS)
return true
}
exportArtboards()
{
this.logMsg("exportArtboards: running...")
// Prepare folders
this.prepareOutputFolder()
// Copy static files
if (!this.copyStatic("resources")) return false
if (!this.copyStatic("js")) return false
this.mDoc = new PZDoc()
try
{
// Collect layers information
this.mDoc.collectData()
this.mDoc.buildLinks()
// Build Story.js with hotspots
this.startStoryData();
// Export every artboard into image
this.mDoc.export()
// Dump document layers to JSON file
this.saveToJSON()
// Build main HTML file
if (!this.createMainHTML()) return false
if (!this.finishSaveStoryData()) return false
// Compress Images
this.compressImages()
// Build image small previews for Gallery
this.buildPreviews()
// Save site icon
if (this.siteIconLayer != undefined)
{
this.siteIconLayer.exportSiteIcon()
}
}
catch (error)
{
this.logError(error)
}
finally
{
if (!exporter.context['async'])
{
if (DEBUG || true) exporter.logMsg("exportArtboards: undo changes")
this.mDoc.undoChanges()
}
}
this.logMsg("exportArtboards: done!")
return true
}
saveToJSON()
{
if (!this.enabledJSON) return true
const symbolData = this.mDoc.getSymbolData()
const json = this.mDoc.getJSON()
this.storyData.experimentalExisting = json.includes("EXPERIMENTAL")
const pathJSFile = this.createDestFile('handoff.js')
if (!Utils.writeToFile(symbolData + "var layersData = " + json, pathJSFile)) return false
}
initPaths(selectedPath)
{
this._outputPath = selectedPath + "/" + this.docName
this.imagesPath = this._outputPath + "/" + Constants.IMAGES_DIRECTORY;
this.fullImagesPath = this.imagesPath + Constants.FULLIMAGE_DIRECTORY
this.previewsImagePath = this.imagesPath + Constants.PREVIEWS_DIRECTORY
}
prepareOutputFolder()
{
Utils.deleteFile(this._outputPath)
function createSubfolder(path)
{
let error = MOPointer.alloc().init();
const fileManager = NSFileManager.defaultManager();
if (!fileManager.createDirectoryAtPath_withIntermediateDirectories_attributes_error(path, false, null, error))
{
exporter.logMsg(error.value().localizedDescription());
}
}
createSubfolder(this.previewsImagePath)
createSubfolder(this.fullImagesPath)
}
}
================================================
FILE: PuzzlePublisher.sketchplugin/Contents/Sketch/exporter/publisher.js
================================================
@import("constants.js")
@import("lib/utils.js")
@import("lib/ga.js")
@import("lib/uidialog.js")
@import "miro/api.js";
@import "miro/utils.js";
let publisher = null
Api.prototype.artboardsToPNG = function (context, exportAll, scale)
{
return publisher.miroExportInfoList
}
class Publisher
{
constructor(context, doc)
{
this.doc = doc;
this.UI = require('sketch/ui')
this.context = context;
this.Settings = require('sketch/settings');
this.login = ''
this.sshPort = ''
this.siteRoot = ''
this.ver = ''
this.remoteFolder = ''
this.allMockupsdDir = Utils.getPluginSetting(SettingKeys.PLUGIN_EXPORTING_URL, '1')
this.serverToolsPath = Utils.getPluginSetting(SettingKeys.PLUGIN_SERVERTOOLS_PATH)
this.authorName = Utils.getPluginSetting(SettingKeys.PLUGIN_AUTHOR_NAME)
this.authorEmail = Utils.getPluginSetting(SettingKeys.PLUGIN_AUTHOR_EMAIL)
this.commentsURL = Utils.getPluginSetting(SettingKeys.PLUGIN_COMMENTS_URL)
this.curlPath = Utils.getPluginSetting(SettingKeys.PLUGIN_PUBLISH_CURL_PATH)
if(this.curlPath==="") this.curlPath = Constants.CURL_PATH
this.filesChunkLimit = 50
this.docFolder = this.doc.cloudName();
let posSketch = this.docFolder.indexOf(".sketch")
if (posSketch > 0)
{
this.docFolder = this.docFolder.slice(0, posSketch)
}
this.message = Utils.getPluginSetting(SettingKeys.PLUGIN_PUBLISH_LAST_MSG)
publisher = this
this.story = null
this.mockupsPath = this.allMockupsdDir + "/" + this.docFolder
this.fullImagesPath = this.mockupsPath + "/" + Constants.IMAGES_DIRECTORY + Constants.FULLIMAGE_DIRECTORY
this.miroExportInfoList = []
this.miroEnabled = null
this.userID = Utils.getUserID()
}
readOptions()
{
// read current version from document settings
let Settings = this.Settings
this.ver = Settings.documentSettingForKey(this.doc, SettingKeys.DOC_PUBLISH_VERSION)
if (this.ver == undefined || this.ver == null) this.ver = '1'
this.login = Settings.settingForKey(SettingKeys.PLUGIN_PUBLISH_LOGIN)
if (this.login == undefined || this.login == null) this.login = ''
this.sshPort = Settings.settingForKey(SettingKeys.PLUGIN_PUBLISH_SSH_PORT)
if (this.sshPort == undefined || this.sshPort == null || this.sshPort == '') this.sshPort = '22'
this.siteRoot = Settings.settingForKey(SettingKeys.PLUGIN_PUBLISH_SITEROOT)
if (this.siteRoot == undefined || this.siteRoot == null) this.siteRoot = ''
this.secret = Settings.settingForKey(SettingKeys.PLUGIN_PUBLISH_SECRET)
if (this.secret == undefined || this.secret == null) this.secret = ''
this.remoteFolder = Settings.documentSettingForKey(this.doc, SettingKeys.DOC_PUBLISH_REMOTE_FOLDER)
if (this.remoteFolder == undefined || this.remoteFolder == null) this.remoteFolder = ''
this.miroEnabled = null == this.miroEnabled ? Settings.settingForKey(SettingKeys.PLUGIN_PUBLISH_MIRO_ENABLED) == 1 : this.miroEnabled
this.miroBoards = null
if (this.miroEnabled)
{
this.miroBoardName = Utils.getDocSetting(this.doc, SettingKeys.DOC_PUBLISH_MIRO_BOARD)
this.oldMiroBoardName = this.miroBoardName
this.miroBoardID = Utils.getDocSetting(this.doc, SettingKeys.DOC_PUBLISH_MIRO_BOARDID)
if (("" == this.miroBoardID || "" == this.miroBoardName) && (this.miroBoardID + this.miroBoardName) != '')
{
this._initMiro()
this._validateMiroParams()
}
}
this.authorName = Settings.settingForKey(SettingKeys.PLUGIN_AUTHOR_NAME)
if (this.authorName == undefined || this.authorName == '') this.authorName = 'None'
this.authorEmail = Settings.settingForKey(SettingKeys.PLUGIN_AUTHOR_EMAIL)
if (this.authorEmail == undefined || this.authorEmail == '') this.authorEmail = 'None'
this.commentsURL = Settings.settingForKey(SettingKeys.PLUGIN_COMMENTS_URL)
if (this.commentsURL == undefined) this.commentsURL = ''
///
//
return true
}
log(msg)
{
//log(msg)
}
publish()
{
this.readOptions()
if (!this.checkMockupExists(this.allMockupsdDir, this.docFolder))
{
return false
}
// Show this.UI
if (!this.context.fromCmd)
{
while (true)
{
if (!this.askOptions()) return false
if (this.checkOptions()) break
}
}
const version = this.ver
let destFolder = this.remoteFolder
if ('' == destFolder) return true
// drop trailed /
destFolder = destFolder.replace(/(\/)$/, "")
// copy publish script
if (!this.copyScript("publish.sh")) return false
if (!this.copyScript("preparePublish.sh")) return false
//
if (this.miroEnabled && this.miroBoardID != "")
{
this.publishToMiro()
}
this.Settings.setSettingForKey(SettingKeys.PLUGIN_PUBLISH_LAST_MSG, this.message)
// run publish script
this.tempFolder = this.allMockupsdDir+"/_tmp/"+this.docFolder
log("tempFolder="+this.tempFolder)
let commentsID = destFolder
commentsID = Utils.toFilename(commentsID)
let runResult = this.runPreparationScript(version, this.allMockupsdDir, this.docFolder, destFolder, commentsID)
if (!runResult.result){
this.showMessage(runResult)
return false
}
if(this.login!=="")
runResult = this.runPublishScript(version, this.allMockupsdDir, this.docFolder, destFolder, commentsID)
else
runResult = this.publishMockupsByHTTPS(destFolder, commentsID)
track(TRACK_PUBLISH_COMPLETED)
// success
if (runResult.result)
{
const openURL = this.siteRoot + destFolder + (version == "-1" ? "" : ("/" + version)) + "/index.html"
const announceFolder = destFolder + (version == "-1" ? "" : ("/" + version))
// save changed document
log(" SAVING DOCUMENT...")
const Dom = require('sketch/dom')
const jDoc = Dom.fromNative(this.doc)
jDoc.save(err =>
{
if (err)
{
log(" Failed to save a document. Error: " + err)
}
})
// inform server about new version
if (this.message != "--" && this.serverToolsPath != "")
{
try
{
var url = this.siteRoot + this.serverToolsPath + Constants.SERVER_ANNOUNCE_SCRIPT
url += "?author=" + encodeURI(this.authorName).replace(/[#]/g, '')
if ("" != this.authorEmail) url += "&email=" + encodeURI(this.authorEmail).replace(/[#]/g, '')
if ("" != this.secret) url += "&sec=" + encodeURI(this.secret).replace(/[#]/g, '')
url += "&msg=" + encodeURI(this.message).replace(/[#]/g, '')
url += "&ver=" + encodeURI(this.ver).replace(/[#]/g, '')
url += "&dir=" + encodeURI(announceFolder).replace(/[#]/g, '')
if (this.message.includes('--NOTELE'))
{
url += "&NOTELE=1"
}
if (DEBUG)
{
log(url)
}
var nURL = NSURL.URLWithString(url);
var data = NSData.dataWithContentsOfURL(nURL);
//var json = NSJSONSerialization.JSONObjectWithData_options_error(data, 0, nil)
//log(json)
} catch (e)
{
log("Exception: " + e);
}
}
if (!this.context.fromCmd)
{
// open browser
if (this.siteRoot != '')
{
const openResult = Utils.runCommand('/usr/bin/open', [openURL])
log(" OPENING PUBLISHED PAGE...")
if (openResult.result)
{
} else
{
this.UI.alert('Can not open HTML in browser', openResult.output)
}
}
this.Settings.setSettingForKey(SettingKeys.PLUGIN_PUBLISH_LAST_MSG, "")
this.showMessage(runResult)
}
} else
{
this.showMessage(runResult)
return false
}
return true
}
publishToMiro(standalone = false)
{
if (standalone)
{
this.miroEnabled = true
this.readOptions()
}
if (standalone && !this.askMiroOptions()) return false
try
{
log("publishToMiro: start")
// Load story.js file and eval it
const storyPath = this.mockupsPath + "/data/story.js"
let storyJS = Utils.readFile(storyPath)
if (undefined == storyJS)
{
this.UI.alert('Error', "Can't find mockups on path: " + this.mockupsPath)
return false
}
String.prototype.replaceAllMe = function (search, replacement)
{
return this.split(search).join(replacement)
}
storyJS = Utils.readFile(storyPath).replace("var story = {", "this.story = {")
storyJS = storyJS.replaceAllMe("$.extend(new ViewerPage(),", "").replaceAllMe("})", "}")
eval(storyJS)
// Build page list
this.miroExportInfoList = this.getArtboardsListForMiro()
// Publish
log("publishToMiro: start publishing")
const result = api.uploadArtboardsToRTB(this.context, this.miroBoardID, true)
if (result != api.UploadEnum.SUCCESS)
{
throw "Failed to publish"
}
// Show in browser
if (standalone)
{
var fullBoardURL = boardURL + this.miroBoardID;
const openResult = Utils.runCommand('/usr/bin/open', [fullBoardURL])
if (openResult.result)
{
} else
{
this.UI.alert('Can not open HTML in browser', openResult.output)
}
require('sketch/ui').alert('Success', 'Published successfully')
}
}
catch (error)
{
this.UI.alert('Publishing to Miro failed', error)
}
finally
{
log("publishToMiro: done")
}
}
getArtboardsListForMiro()
{
var imagePath = this.fullImagesPath
var exportInfoList = [];
const Dom = require('sketch/dom')
const jDoc = Dom.fromNative(publisher.doc)
let errors = ""
log("Miro: build page list: start")
for (var page of this.story.pages.filter(el => "external" != el.type))
{
const artboard = jDoc.getLayerWithID(page["id"])
if (!artboard)
{
//if ("" != errors) errors += "\n"
//errors += page['title']
continue
}
var exportInfo = { "artboardID": page["id"], "artboard": artboard.sketchObject, "path": imagePath + page['image'] };
exportInfoList.push(exportInfo);
}
log("Miro: build page list: done")
if ("" != errors)
{
this.UI.alert('Can not find by ID the following artboards', errors)
return null
}
return exportInfoList;
}
showMessage(result)
{
if (result.result)
{
this.UI.alert('Success', PublishKeys.SHOW_OUTPUT ? result.output : 'Mockups published!')
} else
{
this.showOutput(result)
}
}
showOutput(result)
{
if (result.result && !PublishKeys.SHOW_OUTPUT) return true
this.UI.alert(result.result ? 'Output' : 'Error', result.output)
}
checkOptions()
{
if (this.ver == '')
{
this.UI.alert('Error', 'Version should be specified')
return false
}
if (this.remoteFolder == '')
{
this.UI.alert('Error', 'Remote site folder should be specified')
return false
}
return true
}
askOptions()
{
let Settings = this.Settings
let askMessage = '' != this.serverToolsPath
let askMiro = this.miroEnabled
if(this.login==="" && this.siteRoot===""){
publisher.UI.alert("Error", "Configure SFTP login ot HTTPS Site URL")
return false
}
// show dialod
const dialog = new UIDialog("Publish HTML", NSMakeRect(0, 0, 400,
180 + (askMessage ? 65 : 0) + (askMiro ? 60 : 0)),
"Publish", "Generated HTML will be uploaded to external site by SFTP.")
dialog.removeLeftColumn()
if (askMessage)
{
dialog.addTextBox("message", "Change Description", this.message, 'Added Remove button', 40)
dialog.addHint("messageHint", "Describe briefly was changed")
}
dialog.addTextInput("version", "Version", this.ver, '1', 50)
dialog.addHint("versionHint", "Exporter will publish two HTML sets - live and <version>")
dialog.addTextInput("remoteFolder", "Remote Site Folder", this.remoteFolder, 'myprojects/project1', 350)
dialog.addHint("remoteFolderHint", "Relative path on server")
if (askMiro)
{
this.addMiroBoardSelector(dialog, 350, " (optional)")
}
track(TRACK_PUBLISH_DIALOG_SHOWN)
while (true)
{
const result = dialog.run()
if (!result)
{
track(TRACK_PUBLISH_DIALOG_CLOSED, { "cmd": "cancel" })
return false
}
// Read data
if (askMiro)
{
this.miroBoardName = dialog.views['miroBoard'].stringValue() + ""
}
this.remoteFolder = dialog.views['remoteFolder'].stringValue() + ""
if (askMessage)
{
this.message = dialog.views['message'].stringValue() + ""
}
let ver = dialog.views['version'].stringValue() + ""
let verInt = parseInt(ver)
this.ver = ver
// check data
if (askMiro)
{
if ("" == this.miroBoardName)
{
// Set empty board
this.miroBoardID = ""
this.miroBoardIndex = -1
} else if (this.oldMiroBoardName != this.miroBoardName)
{
// Change name
// load Miro boards to find the new ID
if (!this._initMiro()) return false
this.miroBoardIndex = this.miroBoards.boards.indexOf(this.miroBoardName)
this.miroBoardID = this.miroBoardIndex >= 0 ? this.miroBoards.indexIdsMap[this.miroBoardIndex] : ""
}
if ("" != this.miroBoardName && "" == this.miroBoardID)
{
this.UI.alert("Error", "No such board in Miro")
continue
}
}
if ('' == this.remoteFolder) continue
if ('' == this.ver) continue
if (askMessage && '' == this.message) continue
dialog.finish()
track(TRACK_PUBLISH_DIALOG_CLOSED, { "cmd": "ok" })
// save new version into document settings
if (askMiro)
{
Settings.setDocumentSettingForKey(this.doc, SettingKeys.DOC_PUBLISH_MIRO_BOARDID, this.miroBoardID)
Settings.setDocumentSettingForKey(this.doc, SettingKeys.DOC_PUBLISH_MIRO_BOARD, this.miroBoardName)
}
Settings.setDocumentSettingForKey(this.doc, SettingKeys.DOC_PUBLISH_REMOTE_FOLDER, this.remoteFolder)
Settings.setDocumentSettingForKey(this.doc, SettingKeys.DOC_PUBLISH_VERSION, (verInt >= 0 ? verInt + 1 : verInt) + "")
return true
}
return false
}
askMiroOptions()
{
if (!this._initMiro() || !this._validateMiroParams()) return false
const dialog = new UIDialog("Select Miro Board ", NSMakeRect(0, 0, 350, 60), "Select", "Previously exported pages will be uploaded to Miro whiteboard as images")
dialog.removeLeftColumn()
dialog.addSelect("miroBoard", "", this.miroBoardIndex, this.miroBoards.boards, 350)
while (true)
{
const result = dialog.run()
if (!result)
{
return false
}
const miroBoardIndex = dialog.views['miroBoard'].indexOfSelectedItem()
if (0 > miroBoardIndex)
{
publisher.UI.alert("Error", "Miro board should be specified")
continue
}
let miroBoardID = this.miroBoards.indexIdsMap[miroBoardIndex]
if ("" == miroBoardID)
{
publisher.UI.alert("Error", "Miro board should be specified")
continue
}
this.miroBoardID = miroBoardID
dialog.finish()
// save
this.Settings.setDocumentSettingForKey(this.doc, SettingKeys.DOC_PUBLISH_MIRO_BOARDID, this.miroBoardID)
if (this.oldMiroBoardName != "") this.Settings.setDocumentSettingForKey(this.doc, SettingKeys.DOC_PUBLISH_MIRO_BOARD, "")
return true
}
return false
}
addMiroBoardSelector(dialog, width = 520, inlineHintPostfix = "")
{
//dialog.addTextInput("miroBoard", "Miro board", this.miroBoard, 'Board name', 350)
const input = dialog.addPathInput({
id: "miroBoard", label: "Miro board", labelSelect: "Select",
textValue: this.miroBoardName,
inlineHint: 'Board name' + inlineHintPostfix, width,
customHandler: function ()
{
if (!publisher._initMiro()) return false
const dialog = new UIDialog("Select Miro Board ", NSMakeRect(0, 0, 350, 60), "Select")
dialog.removeLeftColumn()
const currentBoard = input.stringValue() + ""
let currentBoardIndex = currentBoard != "" ? publisher.miroBoards.boards.indexOf(currentBoard) : 0
if (currentBoardIndex < 0) currentBoardIndex = 0
dialog.addSelect("miroBoard", "", currentBoardIndex, publisher.miroBoards.boards, 350)
while (true)
{
const result = dialog.run()
if (!result)
{
return false
}
const miroBoardIndex = dialog.views['miroBoard'].indexOfSelectedItem()
if (0 > miroBoardIndex)
{
publisher.UI.alert("Error", "Miro board should be specified")
continue
}
input.setStringValue(publisher.miroBoards.boards[miroBoardIndex])
dialog.finish()
// save
return true
}
return false
}
})
}
checkMockupExists(allMockupsdDir, docFolder)
{
const fullPath = allMockupsdDir + "/" + docFolder
if (Utils.isFolderExists(fullPath)) return true
this.UI.alert('Error', `Local HTML is not found on \n${fullPath}\n\nYou need to run Export to HTML before publishing`)
return false
}
runPreparationScript(version, allMockupsdDir, docFolder,remoteFolder,commentsID) {
let args = [version, allMockupsdDir, docFolder, remoteFolder, commentsID]
args.push(this.login)
args.push(this.sshPort)
args.push(this.authorName)
args.push(this.authorEmail)
args.push(this.commentsURL.replace(/(\/)/g, '\\/'))
args.push(this.tempFolder)
//args.push(Constants.MIRROR2)
return this.runScriptWithArgs("preparePublish.sh", args)
}
runPublishScript(version, allMockupsdDir, docFolder, remoteFolder, commentsID) {
let args = [version, allMockupsdDir, docFolder, remoteFolder, commentsID]
args.push(this.login)
args.push(this.sshPort)
args.push(this.authorName)
args.push(this.authorEmail)
args.push(this.commentsURL.replace(/(\/)/g, '\\/'))
args.push(this.tempFolder)
//args.push(Constants.MIRROR2)
return this.runScriptWithArgs("publish.sh", args)
}
publishMockupsByHTTPS(remoteFolder)
{
const fullPath = this.tempFolder
const localImagesPath = fullPath + "/images"
//////////// PUBLISH IMAGES /////////
this.publishedImages = 0
// Publish images im /images folder
let res = this.publishImagesInFolderByHTTPS(localImagesPath, "2x")
if (res && !res.result) return res
// Publish images im /images/full folder
res = this.publishImagesInFolderByHTTPS(localImagesPath + "/full", "full")
if (res && !res.result) return res
// Publish images im /images/full folder
res = this.publishImagesInFolderByHTTPS(localImagesPath + "/previews", "preview")
if (res && !res.result) return res
//////////// PUBLISH OTHJER /////////
res = this.publishFilesByHTTPS(fullPath, ["index.html"])
if (res && !res.result) return res
const folders = ["data", "resources", "js", "js/other"]
folders.forEach(function (folderName)
{
res = this.publishFilesInFolderByHTTPS(fullPath, folderName)
if (res && !res.result) return res
}, this)
// COMPLETE
res = this.publishCompleteByHTTPS(fullPath)
if (res && !res.result) return res
return res
}
publishCompleteByHTTPS(fullPath)
{
let args = ["--no-progress-meter"]
const cmd = "cms"
let url = this.siteRoot + this.serverToolsPath + "/upload.php?cmd=" + cmd
url += `&tid=${encodeURI(this.secret)}`
url += `&uid=${encodeURI(this.userID)}`
url += `&ver=${encodeURI(this.ver)}`
url += `&docid=${encodeURI(this.remoteFolder)}`
args.push(url)
return Utils.runCommand(this.curlPath, args)
}
publishImagesInFolderByHTTPS(localPath, defaultImageType)
{
if (!Utils.isFolderExists(localPath)) return {
result: 0,
output: "No folder on path " + localPath
}
const allImages = Utils.listFiles(localPath)
let result = null
// process images
let fileNames = []
allImages.forEach(function (file)
{
if (result && !result.result) return
const fileName = file + ""
if (!(fileName.endsWith(".png") || fileName.endsWith(".jpg"))) return
//
const imageType = fileName.includes("@2x.") ? "2x" : defaultImageType
//
if (DEBUG) log(`Upload #${this.publishedImages} ${fileName}`)
fileNames.push(fileName)
}, this);
//
return this.publishFilesByHTTPS(localPath, fileNames, defaultImageType)
}
publishFilesInFolderByHTTPS(localPath, folderName)
{
const fullLocalPath = localPath + "/" + folderName
const allFiles = Utils.listFiles(fullLocalPath)
let result = null
// process files
let fileNames = []
allFiles.forEach(function (file)
{
if (result && !result.result) return
const fileName = file + ""
if (fileName === "other") return
//
if (DEBUG) log(`Upload ${fileName}`)
fileNames.push(fileName)
}, this);
//
result = this.publishFilesByHTTPS(fullLocalPath, fileNames, "", folderName)
if (!result.result) log(result.output)
return result
}
publishFilesByHTTPS(filePath, fileNames, imageType = "", dirType = "")
{
let result = null
let chunk = []
fileNames.forEach(function(fileName){
chunk.push(fileName)
if(chunk.length===this.filesChunkLimit){
result = this.publishFilesChunkByHTTPS(filePath, chunk, imageType, dirType)
//
chunk = []
}
},this)
if(chunk.length) result = this.publishFilesChunkByHTTPS(filePath, chunk, imageType, dirType)
return result
}
publishFilesChunkByHTTPS(filePath, fileNames, imageType, dirType)
{
if(DEBUG) log(filePath)
if(DEBUG) log(fileNames)
let isStart = this.publishedImages++==0
const cmd = imageType != "" ? "uploadFrame" : "uploadFile"
let args = ["--no-progress-meter","-X","POST","-H","Content-Type: multipart/form-data"]
let url = this.siteRoot + this.serverToolsPath + "/upload.php?cmd=" + cmd
url += `&tid=${encodeURI(this.secret)}`
url += `&uid=${encodeURI(this.userID)}`
url += `&s=${isStart ? 1 : 0}`
if (imageType != "") url += `&t=${imageType}`
if (dirType != "") url += `&dt=${dirType}`
fileNames.forEach(function(fileName){
const fullPath = filePath + "/" + fileName
if (!Utils.isFolderExists(fullPath)) return {
result: 0,
output: "No file on path " + fullPath
}
//
args.push("-F")
const fileStr = `${fileName}=@${fullPath}`
args.push(fileStr)
if(DEBUG) log(fileStr)
},this)
args.push(url)
return Utils.runCommand(this.curlPath, args)
}
runScriptWithArgs(scriptName, args)
{
const scriptPath = this.allMockupsdDir + "/" + scriptName
args.unshift(scriptPath) // add script itself as a first argument
const res = Utils.runCommand('/bin/bash', args)
// delete script
Utils.deleteFile(scriptPath)
return res
}
runToolInResourcesWithArgs(toolName, args)
{
var url = this.context.plugin.urlForResourceNamed(toolName).path()
//args.unshift(toolName)
//const regex = / /gi;
//const pathTo = this._getFilePathInResourceFolder(toolName).replace(regex,"\\ ")
const res = Utils.runCommand(url, args)
return res
}
runToolWithArgs(toolName, args)
{
const res = Utils.runCommand(toolName, args)
return res
}
copyScript(scriptName)
{
const scriptPath = this.allMockupsdDir + "/" + scriptName
const fileManager = NSFileManager.defaultManager()
const targetPath = scriptPath
// delete old copy
Utils.deleteFile(targetPath)
let sourcePath = this._getFileURLInResourceFolder(scriptName)
let error = MOPointer.alloc().init()
if (!fileManager.copyItemAtPath_toPath_error(sourcePath, targetPath, error))
{
log("copyScript(): Can't copy '" + sourcePath + "' to '" + targetPath + "'. Error: " + error.value().localizedDescription());
this.UI.alert('Can`t copy script', error.value().localizedDescription())
return false
}
return true
}
_initMiro()
{
log("_initMiro")
if (null != this.miroBoards) return true
// Get request
log("_initMiro start")
var response = api.authCheckRequest(this.context);
if (response)
{
if (response.success == 1)
{
} else if (response.error && response.error.code == 401)
{
api.setToken(nil);
log(response.error)
response = null
} else
{
response = null
}
}
if (!response)
{
this.UI.alert("Error", "You need to log into Miro using Miro plugin\n\nhttps://github.com/miroapp/sketch_plugin")
return false
} else
{
this.miroBoards = Utils.getMiroBoardsGroupedByProject()
}
return true
}
_validateMiroParams()
{
if (null == this.miroBoards) return true
// Find board ID for name
if ("" == this.miroBoardID && "" != this.miroBoardName)
{
let index = this.miroBoards.boards.indexOf(this.miroBoardName)
if (index >= 0)
{
let miroBoardID = this.miroBoards.indexIdsMap[index]
if (undefined == miroBoardID) miroBoardID = ""
this.miroBoardID = miroBoardID
}
}
// Find board name for ID
if ("" != this.miroBoardID && "" == this.miroBoardName)
{
let index = this.miroBoards.indexIdsMap.indexOf(this.miroBoardID)
if (index >= 0)
{
let miroBoardName = this.miroBoards.boards[index]
if (undefined == miroBoardName) miroBoardName = ""
this.miroBoardName = miroBoardName
}
}
// Reset if something is wrong
if ("" == this.miroBoardID || "" == this.miroBoardName)
{
this.miroBoardID = ""
this.miroBoardName = ""
}
let currentBoardIndex = this.miroBoardID != "" ? this.miroBoards.indexIdsMap.indexOf(this.miroBoardID) : ""
if (currentBoardIndex < 0) currentBoardIndex = 0
this.miroBoardIndex = currentBoardIndex
return true
}
_getFileURLInResourceFolder(file)
{
return this.context.plugin.url().URLByAppendingPathComponent("Contents").URLByAppendingPathComponent("Sketch").URLByAppendingPathComponent(PublishKeys.RESOURCES_FOLDER).URLByAppendingPathComponent(file)
}
}
================================================
FILE: PuzzlePublisher.sketchplugin/Contents/Sketch/lib/ga.js
================================================
@import "lib/utils.js";
@import "constants.js";
function jsonToQueryString(json) {
return Object.keys(json)
.map(function (key) {
return encodeURIComponent(key) + "=" + encodeURIComponent(json[key]);
})
.join("&");
}
function _completeGA(d, r, e) {
if (DEBUG) log("Completed GA")
}
function track(page, props = undefined) {
coscript.scheduleWithInterval_jsFunction(1, function () {
var trackingId = Constants.GA_ID
var Settings = require("sketch/settings");
var kUUIDKey = "google.analytics.uuid";
var uuid = null
var uuid = NSUserDefaults.standardUserDefaults().objectForKey(kUUIDKey);
if (!uuid) {
uuid = NSUUID.UUID().UUIDString();
NSUserDefaults.standardUserDefaults().setObject_forKey(uuid, kUUIDKey)
}
var variant = ""//MSApplicationMetadata.metadata().variant;
var source =
"Sketch " +
(variant == "NONAPPSTORE" ? "" : variant + " ") +
Settings.version.sketch;
if (Settings.settingForKey(SettingKeys.PLUGIN_GA_DISABLED)) {
// the user didn't enable sharing analytics
return 'the user didn\'t enable sharing analytics';
}
var payload = {
v: 1,
tid: trackingId,
ds: source,
cid: uuid,
t: "pageview",
dp: page
};
if (typeof __command !== "undefined") {
payload.an = __command.pluginBundle().name();
payload.aid = __command.pluginBundle().identifier();
payload.av = __command.pluginBundle().version();
}
if (props) {
Object.keys(props).forEach(function (key) {
payload[key] = props[key];
});
}
var nURL = NSURL.URLWithString(
"https://www.google-analytics.com/collect?payload_data&" +
jsonToQueryString(payload) +
"&z=" +
NSUUID.UUID().UUIDString()
);
if (DEBUG) log("Started GA " + nURL)
//NSData.dataWithContentsOfURL(nURL);
let session = NSURLSession.sharedSession()
//let task = [session dataTaskWithURL: nURL completionHandler: _completeGA]//, _completeGA)
//let task = session.dataTaskWithURL_completionHandler_(nURL, _completeGA)
let task = session.dataTaskWithURL(nURL)
task.resume()
})
}
================================================
FILE: PuzzlePublisher.sketchplugin/Contents/Sketch/lib/uidialog.js
================================================
var UIDialog_iconImage = null
const TAB_HEIGHT = 55
function Class(className, BaseClass, selectorHandlerDict)
{
var uniqueClassName = className + NSUUID.UUID().UUIDString();
var delegateClassDesc = MOClassDescription.allocateDescriptionForClassWithName_superclass_(uniqueClassName, BaseClass);
for (var selectorString in selectorHandlerDict)
{
delegateClassDesc.addInstanceMethodWithSelector_function_(selectorString, selectorHandlerDict[selectorString]);
}
delegateClassDesc.registerClass();
return NSClassFromString(uniqueClassName);
};
class UIAbstractWindow
{
constructor(window, intRect)
{
this.window = window
var container = NSView.alloc().initWithFrame(intRect)
this.container = container
this.topContainer = container
this.views = []
this.leftOffset = 0
this.rect = intRect
this.y = NSHeight(this.rect)
this.leftColumn = true
this.leftColWidth = 120
this.textOffset = 2
}
removeLeftColumn()
{
this.leftColumn = false
this.leftColWidth = 0
this.textOffset = 0
}
initTabs(tabs)
{
const intRect = this.rect
this.tabs = tabs.map(function (tab) { return { label: tab } })
var tabView = NSTabView.alloc().initWithFrame(intRect)
this.tabs.forEach(function (tab, index)
{
let viewController = NSViewController.alloc().init()
viewController.originalSize = intRect
var view = NSView.alloc().initWithFrame(intRect)
view.wantsLayer = false
viewController.view = view
let tabViewIem = NSTabViewItem.allo
gitextract_x9zr0ace/
├── .gitignore
├── CHANGELOG.md
├── Hints.md
├── Ideas.md
├── LICENSE
├── PuzzlePublisher.sketchplugin/
│ └── Contents/
│ ├── Resources/
│ │ └── advpng
│ └── Sketch/
│ ├── cmd-actions.js
│ ├── cmdline-functions.js
│ ├── constants.js
│ ├── exporter/
│ │ ├── PZArtboard.js
│ │ ├── PZDoc.js
│ │ ├── PZLayer.js
│ │ ├── PZPage.js
│ │ ├── exporter-run.js
│ │ ├── exporter.js
│ │ └── publisher.js
│ ├── lib/
│ │ ├── ga.js
│ │ ├── uidialog.js
│ │ ├── uipanel.js
│ │ └── utils.js
│ ├── manifest.json
│ ├── menu-cmd-other.js
│ ├── menu-cmd-publish-miro.js
│ ├── menu-cmd-publish.js
│ ├── menu-conf-artboard.js
│ ├── menu-conf-document.js
│ ├── menu-conf-layer.js
│ ├── menu-conf-plugin-export.js
│ ├── menu-conf-plugin-publishing.js
│ ├── menu-conf-plugin.js
│ ├── menu-edit-annotations.js
│ ├── menu-export-html-adv.js
│ ├── menu-export-html.js
│ ├── menu-external-link.js
│ ├── miro/
│ │ ├── api.js
│ │ └── utils.js
│ ├── pp-viewer/
│ │ ├── exporter/
│ │ │ └── exporter-build-html.js
│ │ └── viewer/
│ │ ├── js/
│ │ │ ├── AbstractViewer.js
│ │ │ ├── CommentsViewer.js
│ │ │ ├── ExpViewer.js
│ │ │ ├── GalleryViewer.js
│ │ │ ├── InfoViewer.js
│ │ │ ├── PresenterViewer.js
│ │ │ ├── SymbolViewer.js
│ │ │ ├── Viewer.js
│ │ │ ├── ViewerPage.js
│ │ │ └── other/
│ │ │ └── jquery.hotkeys.js
│ │ └── resources/
│ │ ├── animations.css
│ │ ├── viewer-center.css
│ │ ├── viewer-top.css
│ │ └── viewer.css
│ ├── scripts/
│ │ ├── export.sh
│ │ ├── preparePublish.sh
│ │ └── publish.sh
│ └── tokens/
│ └── DSExporter.js
├── README.md
├── appcast.xml
├── artworks/
│ └── UI-Images.sketch
├── comments/
│ ├── backend/
│ │ ├── .gitignore
│ │ ├── config/
│ │ │ ├── .htaccess
│ │ │ └── forums.json
│ │ ├── data/
│ │ │ └── .htaccess
│ │ ├── lib/
│ │ │ ├── Comments.js
│ │ │ ├── Forum.php
│ │ │ └── Frontend.php
│ │ └── server.php
│ ├── frontend/
│ │ ├── index.php
│ │ └── test.html
│ └── readme.txt
├── docs/
│ ├── Favicon/
│ │ ├── index.html
│ │ ├── resources/
│ │ │ ├── animations.css
│ │ │ ├── cb-modern-ui.css
│ │ │ ├── jquery.hotkeys.js
│ │ │ ├── ux1-ui.css
│ │ │ ├── viewer-center.css
│ │ │ ├── viewer-fonts.css
│ │ │ ├── viewer-top.css
│ │ │ └── viewer.css
│ │ └── viewer/
│ │ ├── AbstractViewer.js
│ │ ├── CommentsViewer.js
│ │ ├── GalleryViewer.js
│ │ ├── LayersData.js
│ │ ├── SymbolViewer.js
│ │ ├── VersionViewer.js
│ │ ├── story.js
│ │ ├── viewer-page.js
│ │ └── viewer.js
│ ├── FixedLayers/
│ │ ├── index.html
│ │ ├── resources/
│ │ │ ├── animations.css
│ │ │ ├── cb-modern-ui.css
│ │ │ ├── jquery.hotkeys.js
│ │ │ ├── ux1-ui.css
│ │ │ ├── viewer-center.css
│ │ │ ├── viewer-fonts.css
│ │ │ ├── viewer-top.css
│ │ │ └── viewer.css
│ │ └── viewer/
│ │ ├── AbstractViewer.js
│ │ ├── CommentsViewer.js
│ │ ├── GalleryViewer.js
│ │ ├── LayersData.js
│ │ ├── SymbolViewer.js
│ │ ├── VersionViewer.js
│ │ ├── story.js
│ │ ├── viewer-page.js
│ │ └── viewer.js
│ └── Tokens/
│ ├── Demo.sketch
│ └── Lib/
│ ├── UIKit.sketch
│ ├── _pt-assets/
│ │ └── UIKit/
│ │ ├── inspector.json
│ │ ├── vars.json
│ │ └── vars.scss
│ ├── design-tokens.less
│ └── sketch-styles.less
├── examples/
│ ├── CloseOverlay.sketch
│ ├── Favicon/
│ │ └── Favicon.sketch
│ ├── FixedLayers/
│ │ └── FixedLayers.sketch
│ ├── Link-ExternalArtboard.sketch
│ ├── Link-ModalArtboard/
│ │ ├── Link-ModalArtboard-vars.json
│ │ ├── Link-ModalArtboard-viewer.css
│ │ └── Link-ModalArtboard.sketch
│ ├── Link-NestedSymbols.sketch
│ ├── Link-NestedSymbols2.sketch
│ ├── Linked-Symbol-WithEmptyLink.sketch
│ ├── Links-NotOverided.sketch
│ ├── Links.sketch
│ ├── Links2.sketch
│ ├── OverlayOnMouseOver/
│ │ ├── test-library.sketch
│ │ ├── test.sketch
│ │ ├── test2.sketch
│ │ └── test3.sketch
│ ├── Overlays/
│ │ └── Overlays.sketch
│ ├── PageTransition.sketch
│ ├── PinnedTop.sketch
│ ├── Resized-FloatPanel.sketch
│ ├── SortOrder/
│ │ └── SortOrder.sketch
│ ├── Text Search/
│ │ └── Test.sketch
│ ├── VScrollbar/
│ │ ├── Link-ModalArtboard.sketch
│ │ └── VScroll.sketch
│ └── test.sketch
├── server_tools/
│ ├── announce.php
│ ├── config.json
│ ├── folder_info.php
│ ├── journal.php
│ ├── lib/
│ │ └── FolderInfo.php
│ └── version_info.php
└── tests/
├── 12.2.0/
│ └── test.sketch
├── 12.3.1/
│ └── LinkToOverlay.sketch
├── 12.4.0/
│ └── test.sketch
├── 12.4.2/
│ └── Test.sketch
├── 13.0.1/
│ ├── GCUX-7525.sketch
│ ├── GCUX-7530.sketch
│ └── GCUX-7531.sketch
├── 14.11.0/
│ └── test.sketch
├── 14.2.0/
│ └── Test.sketch
├── 14.3.0/
│ └── Test.sketch
├── 14.7.3/
│ └── Test.sketch
├── 14.8.3/
│ └── DemoApp.sketch
├── 15.0.0/
│ └── Test.sketch
├── 15.2.0/
│ └── Test.sketch
├── 15.3.0/
│ └── Test.sketch
├── 16.1.0/
│ ├── Lib.sketch
│ └── Test.sketch
└── New/
└── Test.sketch
SYMBOL INDEX (1099 symbols across 57 files)
FILE: PuzzlePublisher.sketchplugin/Contents/Sketch/cmd-actions.js
function onStartup (line 5) | function onStartup(context) {
FILE: PuzzlePublisher.sketchplugin/Contents/Sketch/cmdline-functions.js
function syncDocumentStyles (line 8) | function syncDocumentStyles(styles)
function syncDocument (line 21) | function syncDocument(document)
function exportDocument (line 47) | function exportDocument(context, runOptions)
function publishDocument (line 53) | function publishDocument(context, document)
function showError (line 63) | function showError(error)
function saveDocument (line 71) | function saveDocument(document)
function closeDocument (line 83) | function closeDocument(document)
FILE: PuzzlePublisher.sketchplugin/Contents/Sketch/constants.js
constant TRACK_INSTALLED (line 108) | const TRACK_INSTALLED = "installed"
constant TRACK_STARTED (line 109) | const TRACK_STARTED = "started"
constant TRACK_EXPORT_DIALOG_SHOWN (line 110) | const TRACK_EXPORT_DIALOG_SHOWN = "export-dialog-shown"
constant TRACK_EXPORT_DIALOG_CLOSED (line 111) | const TRACK_EXPORT_DIALOG_CLOSED = "export-dialog-closed" // cmd:ok,cancel
constant TRACK_EXPORT_COMPLETED (line 112) | const TRACK_EXPORT_COMPLETED = "export-completed"
constant TRACK_PUBLISH_DIALOG_SHOWN (line 113) | const TRACK_PUBLISH_DIALOG_SHOWN = "publish-dialog-shown"
constant TRACK_PUBLISH_DIALOG_CLOSED (line 114) | const TRACK_PUBLISH_DIALOG_CLOSED = "publish-dialog-closed" // cmd:ok,c...
constant TRACK_PUBLISH_COMPLETED (line 115) | const TRACK_PUBLISH_COMPLETED = "publish-completed"
constant TRACK_PUBLISH_MIRO_DIALOG_SHOWN (line 116) | const TRACK_PUBLISH_MIRO_DIALOG_SHOWN = "publish-miro-dialog-shown"
constant TRACK_PUBLISH_MIRO_DIALOG_CLOSED (line 117) | const TRACK_PUBLISH_MIRO_DIALOG_CLOSED = "publish-miro-dialog-closed" /...
constant TRACK_PUBLISH_MIRO_COMPLETED (line 118) | const TRACK_PUBLISH_MIRO_COMPLETED = "publish-miro-completed"
FILE: PuzzlePublisher.sketchplugin/Contents/Sketch/exporter/PZArtboard.js
class PZArtboard (line 10) | class PZArtboard extends PZLayer
method constructor (line 13) | constructor(slayer)
method collectLayers (line 137) | collectLayers(space)
method export (line 143) | export()
method _findFixedPanelHotspots (line 153) | _findFixedPanelHotspots()
method _pushIntoStoryData (line 192) | _pushIntoStoryData(pageIndex)
method _getShadowLayerShadowInfo (line 298) | _getShadowLayerShadowInfo()
method _findLayersShadowInfo (line 307) | _findLayersShadowInfo(layers = undefined, checkKeepFixedShadow = false)
method clearRefsBeforeJSON (line 326) | clearRefsBeforeJSON()
method addShadowLayer (line 335) | addShadowLayer(layer){
method addLayerAsExportableImage (line 340) | addLayerAsExportableImage(layer)
method _getFixedLayersForJSON (line 347) | _getFixedLayersForJSON()
method _buildHotspots (line 421) | _buildHotspots(srcHotspots, isParentFixed = false)
method _getImageName (line 482) | _getImageName(scale, injectScaleToName = true, panelPostix = "")
method _exportImage (line 489) | _exportImage(exportType, nlayer = null, panelPostix = "", forFixedLaye...
method _exportImage2 (line 533) | _exportImage2(scales, slayer)
method _exportImages (line 550) | _exportImages()
method _exportOverlayLayers (line 591) | _exportOverlayLayers()
method _exportImageLayers (line 608) | _exportImageLayers()
method _exportFixedLayersToImages (line 639) | _exportFixedLayersToImages(scales)
method _hideFixedLayers (line 697) | _hideFixedLayers(hide)
FILE: PuzzlePublisher.sketchplugin/Contents/Sketch/exporter/PZDoc.js
function replacer (line 12) | function replacer(key, value)
class PZDoc (line 24) | class PZDoc
method constructor (line 26) | constructor()
method collectData (line 47) | collectData()
method buildLinks (line 91) | buildLinks()
method export (line 102) | export()
method _getLibAssetsPath (line 117) | _getLibAssetsPath(lib)
method getSymbolData (line 122) | getSymbolData()
method getCSSIncludes (line 150) | getCSSIncludes()
method getJSON (line 179) | getJSON()
method undoChanges (line 200) | undoChanges()
method addArtboard (line 215) | addArtboard(mArtboard)
method findArtboardByID (line 232) | findArtboardByID(artboardID)
method getLayerWithID (line 239) | getLayerWithID(id)
method getSymbolMasterByID (line 244) | getSymbolMasterByID(id)
method getSwatchInfoByID (line 255) | getSwatchInfoByID(swatchID)
method _collectLibArtboards (line 299) | _collectLibArtboards()
method _sortArboards (line 310) | _sortArboards()
method _findLibraryArtboardByID (line 322) | _findLibraryArtboardByID(artboardID)
method _getLibraries (line 351) | _getLibraries()
method _buildSymbolDict (line 380) | _buildSymbolDict(sDoc = null)
FILE: PuzzlePublisher.sketchplugin/Contents/Sketch/exporter/PZLayer.js
constant ICON_TAG (line 23) | const ICON_TAG = " / ic-" // Use this string to find icon symbol
class PZLayer (line 52) | class PZLayer
method constructor (line 57) | constructor(sLayer, myParent)
method _calculateConstrains (line 314) | _calculateConstrains()
method collectAChilds (line 328) | collectAChilds(sLayers, space)
method addSelfAsFixedLayerToArtboad (line 347) | addSelfAsFixedLayerToArtboad(overlayType)
method calculateFixedType (line 359) | calculateFixedType()
method findMaskLayer (line 382) | findMaskLayer()
method buildLinks (line 389) | buildLinks(space)
method getShadowInfo (line 394) | getShadowInfo()
method getShadowSetInfo (line 403) | getShadowSetInfo(shadows, inset)
method _processHotspots (line 444) | _processHotspots(prefix)
method _specifyHotspot (line 503) | _specifyHotspot(prefix, l, finalHotspot)
method _specifyExternalURLHotspot (line 545) | _specifyExternalURLHotspot(prefix, finalHotspot, externalLink)
method clearRefsBeforeJSON (line 565) | clearRefsBeforeJSON()
method _buildImageURL (line 652) | _buildImageURL()
method _buildTextPropsForJSON (line 657) | _buildTextPropsForJSON()
method _buildShapePropsForJSON (line 665) | _buildShapePropsForJSON()
method _buildIconsPropsForJSON (line 671) | _buildIconsPropsForJSON()
method _getColorVariable (line 679) | _getColorVariable()
method _clearColor (line 695) | _clearColor(color)
method exportSiteIcon (line 705) | exportSiteIcon()
FILE: PuzzlePublisher.sketchplugin/Contents/Sketch/exporter/PZPage.js
class PZPage (line 7) | class PZPage {
method constructor (line 10) | constructor(sPage) {
method collectData (line 15) | collectData(sArtboards = null) {
method export (line 46) | export() {
method addArtboard (line 62) | addArtboard(mArtboard) {
method _scanLayersToDetachSymbols (line 70) | _scanLayersToDetachSymbols(sParent) {
method _collectArtboards (line 88) | _collectArtboards(sArtboards) {
method _sortArtboards (line 101) | _sortArtboards(sSrcArtboards) {
FILE: PuzzlePublisher.sketchplugin/Contents/Sketch/exporter/exporter-run.js
function closePanel (line 18) | function closePanel()
function panelSwitchFinished (line 33) | function panelSwitchFinished()
function openBrowser (line 41) | function openBrowser(currentPath, doc)
function exportHTML (line 49) | function exportHTML(currentPath, nDoc, exportOptions, context)
function saveDocumentAs (line 102) | function saveDocumentAs(document, filePath)
function asyncExportHTML (line 111) | function asyncExportHTML(context, doc, exportOptions)
function runExporter (line 162) | function runExporter(context, exportOptions = null)
FILE: PuzzlePublisher.sketchplugin/Contents/Sketch/exporter/exporter.js
class Exporter (line 15) | class Exporter
method constructor (line 18) | constructor(selectedPath, ndoc, page, exportOptions, context)
method _readSettings (line 59) | _readSettings()
method getManifest (line 103) | getManifest()
method logMsg (line 111) | logMsg(msg)
method logWarning (line 118) | logWarning(text)
method logError (line 124) | logError(error)
method stopWithError (line 130) | stopWithError(error)
method _clearCloudName (line 137) | _clearCloudName(cloudName)
method getTokensExporter (line 148) | getTokensExporter()
method prepareFilePath (line 158) | prepareFilePath(filePath, fileName)
method copyStatic (line 185) | copyStatic(resFolder)
method startStoryData (line 205) | startStoryData()
method createMainHTML (line 240) | createMainHTML()
method compressImages (line 278) | compressImages()
method buildPreviews (line 296) | buildPreviews()
method createDestFile (line 307) | createDestFile(fileName, folder = Constants.DEST_PROTO_DIRECTORY)
method finishSaveStoryData (line 313) | finishSaveStoryData()
method exportArtboards (line 353) | exportArtboards()
method saveToJSON (line 417) | saveToJSON()
method initPaths (line 432) | initPaths(selectedPath)
method prepareOutputFolder (line 441) | prepareOutputFolder()
FILE: PuzzlePublisher.sketchplugin/Contents/Sketch/exporter/publisher.js
class Publisher (line 16) | class Publisher
method constructor (line 18) | constructor(context, doc)
method readOptions (line 62) | readOptions()
method log (line 112) | log(msg)
method publish (line 117) | publish()
method publishToMiro (line 247) | publishToMiro(standalone = false)
method getArtboardsListForMiro (line 313) | getArtboardsListForMiro()
method showMessage (line 346) | showMessage(result)
method showOutput (line 357) | showOutput(result)
method checkOptions (line 363) | checkOptions()
method askOptions (line 380) | askOptions()
method askMiroOptions (line 487) | askMiroOptions()
method addMiroBoardSelector (line 526) | addMiroBoardSelector(dialog, width = 520, inlineHintPostfix = "")
method checkMockupExists (line 572) | checkMockupExists(allMockupsdDir, docFolder)
method runPreparationScript (line 581) | runPreparationScript(version, allMockupsdDir, docFolder,remoteFolder,c...
method runPublishScript (line 594) | runPublishScript(version, allMockupsdDir, docFolder, remoteFolder, com...
method publishMockupsByHTTPS (line 606) | publishMockupsByHTTPS(remoteFolder)
method publishCompleteByHTTPS (line 642) | publishCompleteByHTTPS(fullPath)
method publishImagesInFolderByHTTPS (line 656) | publishImagesInFolderByHTTPS(localPath, defaultImageType)
method publishFilesInFolderByHTTPS (line 684) | publishFilesInFolderByHTTPS(localPath, folderName)
method publishFilesByHTTPS (line 708) | publishFilesByHTTPS(filePath, fileNames, imageType = "", dirType = "")
method publishFilesChunkByHTTPS (line 724) | publishFilesChunkByHTTPS(filePath, fileNames, imageType, dirType)
method runScriptWithArgs (line 754) | runScriptWithArgs(scriptName, args)
method runToolInResourcesWithArgs (line 766) | runToolInResourcesWithArgs(toolName, args)
method runToolWithArgs (line 776) | runToolWithArgs(toolName, args)
method copyScript (line 783) | copyScript(scriptName)
method _initMiro (line 808) | _initMiro()
method _validateMiroParams (line 841) | _validateMiroParams()
method _getFileURLInResourceFolder (line 881) | _getFileURLInResourceFolder(file)
FILE: PuzzlePublisher.sketchplugin/Contents/Sketch/lib/ga.js
function jsonToQueryString (line 4) | function jsonToQueryString(json) {
function _completeGA (line 12) | function _completeGA(d, r, e) {
function track (line 18) | function track(page, props = undefined) {
FILE: PuzzlePublisher.sketchplugin/Contents/Sketch/lib/uidialog.js
constant TAB_HEIGHT (line 2) | const TAB_HEIGHT = 55
function Class (line 4) | function Class(className, BaseClass, selectorHandlerDict)
class UIAbstractWindow (line 16) | class UIAbstractWindow
method constructor (line 19) | constructor(window, intRect)
method removeLeftColumn (line 38) | removeLeftColumn()
method initTabs (line 45) | initTabs(tabs)
method setTabForViewsCreating (line 82) | setTabForViewsCreating(tabIndex)
method enableTextByID (line 90) | enableTextByID(id, enabled)
method enableHintByID (line 102) | enableHintByID(id, enabled)
method enableControlByID (line 114) | enableControlByID(id, enabled)
method getNewFrame (line 124) | getNewFrame(height = 25, width = -1, yinc = -1)
method addSpace (line 131) | addSpace()
method addLeftLabel (line 136) | addLeftLabel(id, text, height = 40)
method addLabel (line 163) | addLabel(id, text, height = 25)
method addComboBox (line 181) | addComboBox(opt)
method addCheckbox (line 204) | addCheckbox(id, label, checked, height = 18)
method addTextBox (line 219) | addTextBox(id, label, textValue, inlineHint = "", height = 120)
method addTextViewBox (line 239) | addTextViewBox(id, label, textValue, height = 120)
method addTextInput (line 262) | addTextInput(id, label, textValue, inlineHint = "", width = 220, frame...
method addSecureTextInput (line 282) | addSecureTextInput(id, label, textValue, inlineHint = "", width = 220,...
method addPathInput (line 306) | addPathInput(opt)
method addSelect (line 339) | addSelect(id, label, selectItem, options, width = 100)
method addRadioButtons (line 353) | addRadioButtons(id, label, selectItem, options, width = 100)
method startRadioButtions (line 381) | startRadioButtions(idGroup, selectedIndex)
method addRadioButton (line 398) | addRadioButton(id, title, index, frame)
method addButton (line 417) | addButton(id, label, func, width = 100, frame = undefined)
method addHint (line 432) | addHint(id, label, height = 23)
method addDivider (line 450) | addDivider()
method addImage (line 469) | addImage(id, image, frame)
method finish (line 479) | finish()
class UIDialog (line 486) | class UIDialog extends UIAbstractWindow
method setUp (line 489) | static setUp(context)
method constructor (line 494) | constructor(title, rect, okButtonTitle, description = '', cancelButton...
method run (line 515) | run()
FILE: PuzzlePublisher.sketchplugin/Contents/Sketch/lib/uipanel.js
class UIPanel (line 1) | class UIPanel extends UIAbstractWindow {
method constructor (line 3) | constructor(title) {
method show (line 17) | show() {
method finish (line 21) | finish() {
FILE: PuzzlePublisher.sketchplugin/Contents/Sketch/lib/utils.js
class Utils (line 32) | class Utils
method getUserID (line 34) | static getUserID(){
method getDocSetting (line 44) | static getDocSetting(doc, key, defaultValue = '')
method getLayerSetting (line 52) | static getLayerSetting(layer, key, defaultValue = '')
method getPluginSetting (line 60) | static getPluginSetting(key, defaultValue = '')
method upgradeArtboardOverlayPosition (line 69) | static upgradeArtboardOverlayPosition(oldValue)
method askPath (line 141) | static askPath(currentPath = null, buttonName = "Select")
method writeToFile (line 167) | static writeToFile(str, filePath)
method isFolderExists (line 173) | static isFolderExists(path)
method readFile (line 179) | static readFile(path)
method cutLastPathFolder (line 191) | static cutLastPathFolder(path)
method deleteFile (line 197) | static deleteFile(filePath)
method cloneDict (line 211) | static cloneDict(dict)
method escapeFileNameAsArgument (line 216) | static escapeFileNameAsArgument(path)
method escapeSpaces (line 222) | static escapeSpaces(path)
method escapeDoudleQuote (line 227) | static escapeDoudleQuote(path)
method copyRect (line 233) | static copyRect(rect)
method copyRectToRectangle (line 240) | static copyRectToRectangle(rect)
method transformRect (line 246) | static transformRect(rect, cw, ch)
method quoteString (line 254) | static quoteString(str)
method getPathToTempFolder (line 259) | static getPathToTempFolder()
method toFilename (line 265) | static toFilename(name, dasherize = true, lowercase = true)
method isSymbolsPage (line 278) | static isSymbolsPage(page)
method listFiles (line 283) | static listFiles(path)
method removeFilesWithExtension (line 296) | static removeFilesWithExtension(path, extension, extension2 = null)
method runCommand (line 314) | static runCommand(command, args, sync = true)
method actionWithType (line 336) | static actionWithType(nDoc, type)
method getMiroBoardsGroupedByProject (line 352) | static getMiroBoardsGroupedByProject(context)
method testMiro (line 404) | static testMiro(context, email, password, board)
FILE: PuzzlePublisher.sketchplugin/Contents/Sketch/menu-conf-artboard.js
function enableTypeRelated (line 7) | function enableTypeRelated() {
function handleOverlayPin (line 31) | function handleOverlayPin() {
FILE: PuzzlePublisher.sketchplugin/Contents/Sketch/menu-conf-plugin-export.js
constant FILE_TYPES (line 5) | const FILE_TYPES = ["PNG", "JPG"]
FILE: PuzzlePublisher.sketchplugin/Contents/Sketch/menu-export-html-adv.js
function askMode (line 7) | function askMode(context) {
FILE: PuzzlePublisher.sketchplugin/Contents/Sketch/miro/api.js
function dealWithErrors (line 34) | function dealWithErrors(context, message) {
function removeForbiddenCharacters (line 47) | function removeForbiddenCharacters(string) {
function encodeHtmlSpecialCharacters (line 51) | function encodeHtmlSpecialCharacters(string) {
function Api (line 59) | function Api() {
FILE: PuzzlePublisher.sketchplugin/Contents/Sketch/miro/utils.js
function getMessagesByError (line 45) | function getMessagesByError(error) {
function makeSubclass (line 76) | function makeSubclass(className, BaseClass, selectorHandlerDict) {
function logMsg (line 89) | function logMsg(msg) {
function logErr (line 93) | function logErr(err) {
FILE: PuzzlePublisher.sketchplugin/Contents/Sketch/pp-viewer/exporter/exporter-build-html.js
function buildMainHTML_NavigationIcons (line 9) | function buildMainHTML_NavigationIcons(options)
function buildMainHTML (line 99) | function buildMainHTML(options)
FILE: PuzzlePublisher.sketchplugin/Contents/Sketch/pp-viewer/viewer/js/AbstractViewer.js
class AbstractViewer (line 2) | class AbstractViewer {
method constructor (line 3) | constructor(divID) {
method initialize (line 22) | initialize(force = false) {
method customTextSearchPrevented (line 40) | customTextSearchPrevented() {
method pageChanged (line 44) | pageChanged() {
method viewerResized (line 49) | viewerResized() {
method hide (line 53) | hide() {
method show (line 57) | show() {
method toggle (line 61) | toggle() {
method handleKeyDown (line 65) | handleKeyDown(jevent) {
method handleKeyDownWhileInactive (line 69) | handleKeyDownWhileInactive(jevent) {
method onContentClick (line 72) | onContentClick() {
method isVisible (line 77) | isVisible() {
method toggle (line 81) | toggle() {
method _showSelf (line 85) | _showSelf() {
method _hideSelf (line 89) | _hideSelf() {
FILE: PuzzlePublisher.sketchplugin/Contents/Sketch/pp-viewer/viewer/js/CommentsViewer.js
class CommentsViewer (line 4) | class CommentsViewer extends AbstractViewer {
method constructor (line 5) | constructor() {
method initialize (line 16) | initialize(force = false) {
method _hideSelf (line 27) | _hideSelf() {
method handleKeyDownWhileInactive (line 35) | handleKeyDownWhileInactive(jevent) {
method pageChanged (line 49) | pageChanged() {
method handleKeyDown (line 60) | handleKeyDown(jevent) {
method askCommentCounters (line 79) | askCommentCounters(handler) {
method _showCommentCounter (line 84) | _showCommentCounter() {
method sendRequest (line 116) | sendRequest(formData, cmd, handler) {
method updateCommentCounter (line 124) | updateCommentCounter(total) {
method _showComments (line 134) | _showComments() {
method _showSelf (line 162) | _showSelf() {
method _showLoadingMessage (line 173) | _showLoadingMessage() {
FILE: PuzzlePublisher.sketchplugin/Contents/Sketch/pp-viewer/viewer/js/ExpViewer.js
constant EXP_SCOPE_PAGE (line 1) | const EXP_SCOPE_PAGE = "page"
constant EXP_SCOPE_PROJECT (line 2) | const EXP_SCOPE_PROJECT = "project"
constant EXP_MODE_PAGES (line 4) | const EXP_MODE_PAGES = "pages"
constant EXP_MODE_WIDGETS (line 5) | const EXP_MODE_WIDGETS = "widgets"
constant EXP_FILTER_EXP (line 7) | const EXP_FILTER_EXP = "exp"
constant EXP_FILTER_ALL (line 8) | const EXP_FILTER_ALL = "all"
class ExpViewer (line 10) | class ExpViewer extends AbstractViewer {
method constructor (line 12) | constructor() {
method initialize (line 23) | initialize(force = false) {
method goPage (line 29) | goPage(index) {
method _buildContent (line 36) | _buildContent() {
method _buildContentPages (line 44) | _buildContentPages() {
method _getContentForPage (line 62) | _getContentForPage(page) {
method _findExpLayers (line 101) | _findExpLayers(layers, foundExpLayers, layersExt = null, topPage = nul...
method _buildContentWidgets (line 120) | _buildContentWidgets() {
method _getWidgetsSorted (line 135) | _getWidgetsSorted() {
method _getContentForWidget (line 143) | _getContentForWidget(widgetSet, widgetsExt, widgetName) {
method goWidget (line 172) | goWidget(widgetName) {
method _toogleClass (line 178) | _toogleClass(obj, className) {
method setScope (line 183) | setScope(scope) {
method setMode (line 187) | setMode(mode) {
method setFilter (line 191) | setFilter(filter) {
method _hideSelf (line 197) | _hideSelf() {
method _showSelf (line 205) | _showSelf() {
method pageChanged (line 213) | pageChanged() {
method highlightWidget (line 219) | highlightWidget(widgetName) {
FILE: PuzzlePublisher.sketchplugin/Contents/Sketch/pp-viewer/viewer/js/GalleryViewer.js
constant GALLERY_TOP_MARGIN (line 1) | const GALLERY_TOP_MARGIN = 80
constant GALLERY_LEFTRIGH_MARGIN (line 2) | const GALLERY_LEFTRIGH_MARGIN = 40
class GalleryViewerMapLink (line 5) | class GalleryViewerMapLink {
method constructor (line 6) | constructor(index, link, spage, dpage) {
method buildCode (line 19) | buildCode(zoom, visible) {
class GalleryViewer (line 73) | class GalleryViewer extends AbstractViewer {
method constructor (line 74) | constructor() {
method _initPages (line 99) | _initPages() {
method initialize (line 103) | initialize(force = false, skipZoomUpdate = false) {
method _updateCommentCounters (line 149) | _updateCommentCounters(pagesInfo) {
method handleKeyDown (line 170) | handleKeyDown(jevent) {
method mapZoomChanged (line 192) | mapZoomChanged(zoomValue) {
method viewerResized (line 198) | viewerResized() {
method handleKeyDownWhileInactive (line 203) | handleKeyDownWhileInactive(jevent) {
method handleURLParam (line 218) | handleURLParam(paramValue) {
method enableMapMode (line 227) | enableMapMode(enabled) {
method showMapLinks (line 236) | showMapLinks(visible) {
method resetMapZoom (line 243) | resetMapZoom() {
method _showSelf (line 248) | _showSelf() {
method _hideSelf (line 269) | _hideSelf() {
method loadPages (line 277) | loadPages() {
method loadPagesAbs (line 284) | loadPagesAbs() {
method addMapPageGroupTitle (line 353) | addMapPageGroupTitle(group) {
method selectPage (line 367) | selectPage(index) {
method mouseEnterPage (line 372) | mouseEnterPage(index) {
method loadOnePage (line 382) | loadOnePage(page) {
method loadOnePageAbs (line 437) | loadOnePageAbs(page, pageLeft, pageTop) {
method _valueToStyle (line 501) | _valueToStyle(styleName, v, absDelta = 0) {
method _showHideMapLinks (line 505) | _showHideMapLinks(show) {
method _buildMapLinks (line 512) | _buildMapLinks(finalWidth, finalHeight) {
method onSearchInputChange (line 551) | onSearchInputChange() {
method _findTextShowElement (line 600) | _findTextShowElement(page, l, div) {
FILE: PuzzlePublisher.sketchplugin/Contents/Sketch/pp-viewer/viewer/js/InfoViewer.js
function getVersionInfoRequest (line 1) | function getVersionInfoRequest() {
class infoViewer (line 17) | class infoViewer extends AbstractViewer {
method constructor (line 18) | constructor() {
method initialize (line 29) | initialize(force = false) {
method goToVersion (line 40) | goToVersion(recIndex) {
method goToScreen (line 47) | goToScreen(recIndex, screenIndex, pageIndex) {
method switchMode (line 53) | switchMode(delta) {
method _hideSelf (line 74) | _hideSelf() {
method pageChanged (line 84) | pageChanged() {
method handleKeyDownWhileInactive (line 98) | handleKeyDownWhileInactive(jevent) {
method handleKeyDown (line 116) | handleKeyDown(jevent) {
method showRecDetails (line 135) | showRecDetails(index, forNew) {
method _restoreNewImages (line 154) | _restoreNewImages() {
method _showScreens (line 162) | _showScreens(rec, recIndex, showNew) {
method _showCurrentPageDiffs (line 193) | _showCurrentPageDiffs() {
method _showStatic (line 222) | _showStatic() {
method _loadData (line 235) | _loadData(data) {
method _askServerTools (line 266) | _askServerTools() {
method _showSelf (line 274) | _showSelf() {
method _showLoadingMessage (line 281) | _showLoadingMessage() {
FILE: PuzzlePublisher.sketchplugin/Contents/Sketch/pp-viewer/viewer/js/PresenterViewer.js
constant STORAGE_TRANSPERIOD (line 2) | const STORAGE_TRANSPERIOD = "presenterViewer.transPeriod"
function doPresenterViewerNext (line 4) | function doPresenterViewerNext() {
class PresenterViewer (line 14) | class PresenterViewer extends AbstractViewer {
method constructor (line 15) | constructor() {
method initialize (line 24) | initialize(force = false) {
method _hideSelf (line 38) | _hideSelf() {
method _showSelf (line 43) | _showSelf() {
method handleKeyDownWhileInactive (line 59) | handleKeyDownWhileInactive(jevent) {
method handleKeyDown (line 74) | handleKeyDown(jevent) {
method play (line 91) | play() {
method gotoPageWithDelay (line 96) | gotoPageWithDelay(pageIndex) {
method stop (line 105) | stop(exitFullScreen = true) {
FILE: PuzzlePublisher.sketchplugin/Contents/Sketch/pp-viewer/viewer/js/SymbolViewer.js
constant ELEMENTINSPECTOR_LINUX_FONT_SIZES (line 1) | const ELEMENTINSPECTOR_LINUX_FONT_SIZES = {
constant ICON_TAG (line 18) | const ICON_TAG = " / ic-" // Use this string to find icon symbol
constant ICON_TAG2 (line 19) | const ICON_TAG2 = "ic-" // Use this string to find icon symbol
constant SUPPORT_TYPES (line 20) | const SUPPORT_TYPES = ["Text", "ShapePath", "Image", "ImageSymbol"]
class SymbolViewer (line 22) | class SymbolViewer extends AbstractViewer {
method constructor (line 23) | constructor() {
method initialize (line 37) | initialize (force = false) {
method _setSymCheck (line 65) | _setSymCheck (showSymbols) {
method pageChanged (line 73) | pageChanged () {
method _selectLib (line 77) | _selectLib (libName) {
method _reShowContent (line 82) | _reShowContent () {
method toggle (line 103) | toggle () {
method hide (line 107) | hide () {
method showFromExpViewer (line 116) | showFromExpViewer (highlightWidgetName = null) {
method _hideSelf (line 122) | _hideSelf () {
method onContentClick (line 139) | onContentClick () {
method handleKeyDown (line 144) | handleKeyDown (jevent) {
method handleKeyDownWhileInactive (line 159) | handleKeyDownWhileInactive (jevent) {
method _showSelf (line 173) | _showSelf () {
method _showEmptyContent (line 194) | _showEmptyContent () {
method _buildElementLinks (line 204) | _buildElementLinks () {
method _buildElementLinksForPage (line 212) | _buildElementLinksForPage (page) {
method _processSymbolList (line 238) | _processSymbolList (layers, isParentSymbol = false) {
method _processLayerList (line 259) | _processLayerList (layers, sSI = null) {
method _showElement (line 273) | _showElement (l, siLayer = null) {
method _mergeTokens (line 417) | _mergeTokens (list1, list2) {
method _showLayerTextContent (line 430) | _showLayerTextContent (layer, decRes) {
method _showLayerSymbol (line 458) | _showLayerSymbol (layer, symName, siLayer) {
method _showExtDocRef (line 479) | _showExtDocRef (layer, symName, siLayer) {
method _showLayerComment (line 519) | _showLayerComment (layer,siLayer) {
method _showLayerImage (line 532) | _showLayerImage (layer) {
method _showLayerStyle (line 546) | _showLayerStyle (layer, siLayer) {
method _showLayerDimensions (line 564) | _showLayerDimensions (layer) {
method setSelected (line 593) | setSelected (event = null, layer = null, a = null, force = false) {
method findOtherSelection (line 663) | findOtherSelection (click, layers, foundLayers) {
method mouseEnterLayerDiv (line 680) | mouseEnterLayerDiv (div) {
method _drawLeftHMargin (line 705) | _drawLeftHMargin (currentPanel, layer, slayer) {
method _drawRightHMargin (line 733) | _drawRightHMargin (currentPanel, layer, slayer) {
method _drawTopVMargin (line 765) | _drawTopVMargin (currentPanel, layer, slayer) {
method _drawBottomVMargin (line 792) | _drawBottomVMargin (currentPanel, layer, slayer) {
method _findLayersCenterX (line 824) | _findLayersCenterX (l, sl) {
method _findLayersCenterY (line 830) | _findLayersCenterY (l, sl) {
method _drawMarginLine (line 836) | _drawMarginLine (currentPanel, x, y, width, height, className) {
method _drawMarginValue (line 843) | _drawMarginValue (currentPanel, x, y, value) {
method _decorateCSS (line 858) | _decorateCSS (layer, tokens, siLayer) {
method _decorateCSSOneStyle (line 893) | _decorateCSSOneStyle (tokens, layer, siLayer, styleName, styleValue) {
method _decorateCSSLostTokens (line 921) | _decorateCSSLostTokens (tokens, styles, layer, siLayer) {
method _decorateCSSOtherTokens (line 935) | _decorateCSSOtherTokens (tokens, layer, siLayer) {
method _decorateSwatchToken (line 945) | _decorateSwatchToken (tokens, styleValue) {
method _decorateStyleToken (line 951) | _decorateStyleToken (style, tokens, siLayer, styleValue) {
method _formatStyleValue (line 974) | _formatStyleValue (style = "font-size", styleValue = "13px") {
method _showTextPropery (line 986) | _showTextPropery (propName, propValue, postfix = "") {
method _findTokenValueByName (line 992) | _findTokenValueByName (tokenName, libName, styleValue = null) {
method _findSymbolAndLibBySymbolName (line 1016) | _findSymbolAndLibBySymbolName (symName) {
method _findStyleAndLibByStyleName (line 1029) | _findStyleAndLibByStyleName (styleName) {
method _findSwatchTokens (line 1046) | _findSwatchTokens (cv) {
FILE: PuzzlePublisher.sketchplugin/Contents/Sketch/pp-viewer/viewer/js/Viewer.js
function getQuery (line 4) | function getQuery(uri, q) {
function showError (line 8) | function showError(error) {
function showMessage (line 12) | function showMessage(message) {
function checkFolderInfoRequest (line 16) | function checkFolderInfoRequest(resp) {
function handleDecreaseVersion (line 27) | function handleDecreaseVersion() {
function handleIncreaseVersion (line 34) | function handleIncreaseVersion() {
function doTransNext (line 45) | function doTransNext() {
function pagerMarkImageAsLoaded (line 76) | function pagerMarkImageAsLoaded() {
function preloadAllPageImages (line 83) | async function preloadAllPageImages() {
function reloadAllPageImages (line 95) | function reloadAllPageImages() {
function doBlinkHotspots (line 107) | function doBlinkHotspots() {
function splitStylesStr (line 113) | function splitStylesStr(str) {
class Viewer (line 119) | class Viewer {
method constructor (line 120) | constructor(story, files) {
method initialize (line 165) | initialize() {
method initAnimations (line 195) | initAnimations() {
method initializeLast (line 207) | initializeLast() {
method initParseGetParams (line 237) | initParseGetParams() {
method initializeHighDensitySupport (line 254) | initializeHighDensitySupport() {
method isHighDensityDisplay (line 264) | isHighDensityDisplay() {
method buildUserStory (line 267) | buildUserStory() {
method handleKeyDown (line 294) | handleKeyDown(jevent) {
method showTextSearch (line 356) | showTextSearch() {
method blinkHotspots (line 365) | blinkHotspots() {
method setMouseMoveHandler (line 371) | setMouseMoveHandler(obj) {
method onMouseMove (line 375) | onMouseMove(x, y) {
method onContentClick (line 380) | onContentClick() {
method onModalClick (line 391) | onModalClick() {
method showMenu (line 396) | showMenu() {
method hideMenu (line 401) | hideMenu() {
method _setupFolderinfoRequest (line 407) | _setupFolderinfoRequest(func) {
method decreaseVersion (line 415) | decreaseVersion() {
method increaseVersion (line 419) | increaseVersion() {
method showChild (line 423) | showChild(child) {
method _showSidebar (line 437) | _showSidebar() {
method _hideSidebar (line 443) | _hideSidebar() {
method hideChild (line 449) | hideChild() {
method share (line 459) | share() {
method openFulImage (line 484) | openFulImage() {
method toggleZoom (line 494) | toggleZoom(newState = undefined, updateToogler = true) {
method openNewWindow (line 500) | openNewWindow() {
method zoomContent (line 506) | zoomContent() {
method getPageHashes (line 587) | getPageHashes() {
method getModalFirstParentPageIndex (line 598) | getModalFirstParentPageIndex(modalIndex) {
method getPageIndex (line 618) | getPageIndex(page, defIndex = 0) {
method goBack (line 634) | goBack() {
method closeModal (line 644) | closeModal() {
method goToPage (line 647) | goToPage(page, searchText) {
method goTo (line 657) | goTo(page, refreshURL = true, link = undefined, incBackStack = true) {
method _setupTransNext (line 744) | _setupTransNext(msecs) {
method _resetTransQueue (line 758) | _resetTransQueue() {
method refresh_update_navbar (line 763) | refresh_update_navbar(page) {
method refresh_update_links_toggler (line 789) | refresh_update_links_toggler(page) {
method refresh_hide_last_image (line 792) | refresh_hide_last_image(page) {
method refresh_adjust_content_layer (line 815) | refresh_adjust_content_layer(page) {
method refresh_switch_modal_layer (line 829) | refresh_switch_modal_layer(page) {
method _getSearchPath (line 846) | _getSearchPath(page = null, extURL = null) {
method _getPageFullURL (line 853) | _getPageFullURL(page = null, extURL = null) {
method _calcCurrentPageURL (line 858) | _calcCurrentPageURL(page = null, extURL = null) {
method refresh_url (line 867) | refresh_url(page, extURL = "", pushHistory = true) {
method _parseLocationSearch (line 891) | _parseLocationSearch() {
method handleNewLocation (line 914) | handleNewLocation(initial) {
method clear_context_hide_all_images (line 962) | clear_context_hide_all_images() {
method clear_context (line 989) | clear_context() {
method refresh (line 999) | refresh() {
method onKeyEscape (line 1004) | onKeyEscape() {
method next (line 1026) | next() {
method previous (line 1032) | previous() {
method getFirstUserPage (line 1042) | getFirstUserPage() {
method getNextUserPage (line 1046) | getNextUserPage(page = null) {
method getNextVisPage (line 1053) | getNextVisPage(page, loopSearch = true) {
method getPreviousUserPage (line 1059) | getPreviousUserPage(page) {
method toggleLinks (line 1064) | toggleLinks(newState = undefined, updateToogler = true) {
method toogleLayout (line 1069) | toogleLayout(newState = undefined, updateToogler = true) {
method toogleFullScreen (line 1080) | toogleFullScreen(newState = undefined, updateToogler = true) {
method toogleUI (line 1087) | toogleUI(newState = undefined, updateToogler = true) {
method _updateLinksState (line 1092) | _updateLinksState(showLinks = undefined, div = undefined) {
method showHints (line 1107) | showHints() {
method handleStateChanges (line 1113) | handleStateChanges(e) {
method _enableFullScreen (line 1128) | _enableFullScreen() {
method _disableFullScreen (line 1150) | _disableFullScreen() {
function addRemoveClass (line 1165) | function addRemoveClass(mode, el, cls) {
function handleStateChanges (line 1186) | function handleStateChanges(e) {
FILE: PuzzlePublisher.sketchplugin/Contents/Sketch/pp-viewer/viewer/js/ViewerPage.js
constant EVENT_HOVER (line 34) | const EVENT_HOVER = 1
constant TRANS_ANIM_NONE (line 35) | const TRANS_ANIM_NONE = 0
constant TRANS_ANIMATIONS (line 36) | let TRANS_ANIMATIONS = [
function inViewport (line 47) | function inViewport($el)
function handleAnimationEndOnHide (line 56) | function handleAnimationEndOnHide(el)
function handleAnimationEndOnShow (line 67) | function handleAnimationEndOnShow(el)
class ViewerPage (line 77) | class ViewerPage
method constructor (line 80) | constructor()
method showHideGalleryLinks (line 104) | showHideGalleryLinks(show = null)
method _showHideGalleryLinkSet (line 111) | _showHideGalleryLinkSet(links, forceShow = null)
method getHash (line 125) | getHash()
method hide (line 131) | hide(hideChilds = false, disableAnim = false)
method hideCurrentOverlays (line 175) | hideCurrentOverlays()
method hideChildOverlays (line 185) | hideChildOverlays()
method hideOtherParentOverlays (line 195) | hideOtherParentOverlays()
method show (line 206) | show(disableAnim = false)
method showOverlayOverParent (line 233) | showOverlayOverParent()
method findTextNext (line 259) | findTextNext()
method findText (line 267) | findText(text, interactive = true)
method findNextPageWithText (line 333) | findNextPageWithText(text)
method findTextLayersByText (line 358) | findTextLayersByText(text, foundLayers, layers = null)
method _findTextShowElement (line 377) | _findTextShowElement(l, isFocused = false)
method _scrollTo (line 399) | _scrollTo(x, y)
method hideFoundTextResults (line 412) | hideFoundTextResults()
method stopTextSearch (line 418) | stopTextSearch()
method updatePosition (line 425) | updatePosition()
method showOverlayByLinkIndex (line 464) | showOverlayByLinkIndex(linkIndex)
method onMouseMove (line 499) | onMouseMove(x, y)
method onMouseMoveOverlay (line 510) | onMouseMoveOverlay(x, y)
method showAsOverlayInCurrentPage (line 569) | showAsOverlayInCurrentPage(orgPage, link, posX, posY, linkParentFixed,...
method loadImages (line 706) | loadImages(force = false)
method showLayout (line 857) | showLayout()
method _addLayoutLines (line 866) | _addLayoutLines(imageDiv)
method _getSrcPageAndLink (line 898) | _getSrcPageAndLink()
method _getLinkByTargetPage (line 914) | _getLinkByTargetPage(page, links, targetPageIndex)
method _getLinkByIndex (line 926) | _getLinkByIndex(index)
method _getLinkByIndexInLinks (line 938) | _getLinkByIndexInLinks(index, links)
method _loadSingleImage (line 948) | _loadSingleImage(sizeSrc, idPrefix)
method _createLinks (line 968) | _createLinks(panel)
function handleLinkEvent (line 1052) | function handleLinkEvent(event)
FILE: PuzzlePublisher.sketchplugin/Contents/Sketch/pp-viewer/viewer/js/other/jquery.hotkeys.js
function keyHandler (line 130) | function keyHandler(handleObj) {
FILE: PuzzlePublisher.sketchplugin/Contents/Sketch/tokens/DSExporter.js
constant BLENDING_MODE_SKETCH_TO_CSS (line 29) | const BLENDING_MODE_SKETCH_TO_CSS = {
class DSExporter (line 64) | class DSExporter {
method constructor (line 65) | constructor(context) {
method init (line 99) | init() {
method initForPublisher (line 124) | initForPublisher() {
method logMsg (line 134) | logMsg(msg) {
method logError (line 143) | logError(error) {
method stopWithError (line 148) | stopWithError(error) {
method run (line 155) | run() {
method _openFinder (line 177) | _openFinder() {
method _clearCloudName (line 181) | _clearCloudName(cloudName) {
method _showMessages (line 192) | _showMessages() {
method _showErrors (line 199) | _showErrors() {
method _showDialog (line 208) | _showDialog() {
method _export (line 279) | _export() {
method _getStylesAsText (line 292) | _getStylesAsText() {
method _getStyleInfoAsText (line 341) | _getStyleInfoAsText(sStyle) {
method _getTokensAsText (line 352) | _getTokensAsText() {
method getAbstractTokensAsText (line 362) | getAbstractTokensAsText(opt) {
method _getAbstractToken (line 376) | _getAbstractToken(opt, value) {
method _getColorToken (line 389) | _getColorToken(color) {
method _getFontSizeToken (line 397) | _getFontSizeToken(fontSize) {
method _getFontFamilyToken (line 401) | _getFontFamilyToken(fontFamily) {
method _getFontWeightToken (line 405) | _getFontWeightToken(fontWeight) {
method _getTextStylePropsAsText (line 411) | _getTextStylePropsAsText(sStyle, spaces = "") {
method _getLayerStylePropsAsText (line 455) | _getLayerStylePropsAsText(sSharedStyle, layer, sStyle, spaces = "") {
method _getLayerBorderByIndexAsText (line 504) | _getLayerBorderByIndexAsText(sStyle, index, spaces) {
method _getLayerFillByIndexAsText (line 559) | _getLayerFillByIndexAsText(sStyle, index, spaces) {
method _getLayerShadowsText (line 600) | _getLayerShadowsText(sStyle, spaces) {
method _getLayerTypeShadowsText (line 613) | _getLayerTypeShadowsText(sStyle, shadows, inset, spaces) {
method _calcGradientDeg (line 641) | _calcGradientDeg(g) {
method _parseStyleName (line 657) | _parseStyleName(nameSrc, isText) {
FILE: comments/backend/lib/Comments.js
function commentReplaceEnds (line 1) | function commentReplaceEnds(value) {
class CommentsAbstractForm (line 5) | class CommentsAbstractForm {
method constructor (line 6) | constructor(formName) {
method _tuneInput (line 11) | _tuneInput(inputName, type = "input") {
method _setInputValue (line 33) | _setInputValue(inputName, value) {
method showError (line 38) | showError(errorText) {
method show (line 41) | show() {
method hide (line 47) | hide() {
method showViewer (line 52) | showViewer() {
method hideViewer (line 54) | hideViewer() {
method buildHTML (line 57) | buildHTML() {
method getDataFromForm (line 60) | getDataFromForm() {
method putDataInForm (line 63) | putDataInForm() { }
method checkData (line 64) | checkData() { }
method handleEnterKey (line 65) | handleEnterKey() {
method clear (line 68) | clear() {
method submit (line 71) | submit() { }
class CommentsLoginForm (line 74) | class CommentsLoginForm extends CommentsAbstractForm {
method constructor (line 75) | constructor() {
method putDataInForm (line 79) | putDataInForm() {
method checkData (line 83) | checkData() {
method getDataFromForm (line 90) | getDataFromForm() {
method buildHTML (line 93) | buildHTML() {
method submit (line 109) | submit() {
method clear (line 134) | clear() {
class CommentsAuthForm (line 141) | class CommentsAuthForm extends CommentsAbstractForm {
method constructor (line 142) | constructor() {
method putDataInForm (line 148) | putDataInForm() {
method checkData (line 158) | checkData() {
method getDataFromForm (line 169) | getDataFromForm() {
method buildHTML (line 173) | buildHTML() {
method cancel (line 197) | cancel() {
method submit (line 202) | submit() {
method clear (line 238) | clear() {
class CommentsNewCommentForm (line 247) | class CommentsNewCommentForm extends CommentsAbstractForm {
method constructor (line 248) | constructor() {
method putDataInForm (line 255) | putDataInForm() {
method checkData (line 259) | checkData() {
method getDataFromForm (line 266) | getDataFromForm() {
method getHTML (line 269) | getHTML() {
method buildHTML (line 300) | buildHTML() {
method startMarkerMove (line 306) | startMarkerMove() {
method stopMarkerMove (line 325) | stopMarkerMove() {
method onMouseMove (line 334) | onMouseMove(x, y) {
method saveMarker (line 338) | saveMarker() {
method dropMarker (line 349) | dropMarker() {
method submit (line 358) | submit() {
method clear (line 388) | clear() {
method hide (line 394) | hide() {
method hideViewer (line 398) | hideViewer() {
class CommentsEditCommentForm (line 403) | class CommentsEditCommentForm extends CommentsAbstractForm {
method constructor (line 404) | constructor(commentID) {
method build (line 413) | build(commentID) {
method putDataInForm (line 428) | putDataInForm() {
method checkData (line 432) | checkData() {
method cancel (line 439) | cancel() {
method getDataFromForm (line 443) | getDataFromForm() {
method getHTML (line 446) | getHTML() {
method buildHTML (line 479) | buildHTML() {
method startMarkerMove (line 486) | startMarkerMove() {
method stopMarkerMove (line 505) | stopMarkerMove() {
method onMouseMove (line 514) | onMouseMove(x, y) {
method saveMarker (line 518) | saveMarker() {
method dropMarker (line 529) | dropMarker() {
method submit (line 538) | submit() {
method clear (line 568) | clear() {
method hide (line 574) | hide() {
method hideViewer (line 578) | hideViewer() {
class Comments (line 583) | class Comments {
method constructor (line 584) | constructor(forumID, url, sid, user) {
method clearSession (line 615) | clearSession() {
method saveSessionInBrowser (line 627) | saveSessionInBrowser() {
method processRequestResult (line 631) | processRequestResult(result) {
method sendCommand (line 645) | sendCommand(cmd, formData, handler) {
method logout (line 657) | logout() {
method reloadComments (line 667) | reloadComments() {
method removeComment (line 684) | removeComment(commentID) {
method editComment (line 705) | editComment(commentID) {
method getCommentByID (line 713) | getCommentByID(commentID) {
method build (line 718) | build(commentList) {
method _buildComments (line 731) | _buildComments(commentList) {
method _buildMarkers (line 780) | _buildMarkers() {
method _buildScene (line 793) | _buildScene() {
method addCircleToScene (line 807) | addCircleToScene(id, x, y, number = "") {
method removeCircleOnScene (line 828) | removeCircleOnScene(id) {
method _dropScene (line 831) | _dropScene() {
method _clearScene (line 834) | _clearScene() {
method showViewer (line 838) | showViewer() {
method hideViewer (line 846) | hideViewer() {
FILE: comments/backend/lib/Forum.php
function getallheaders (line 110) | function getallheaders() {
class Page (line 121) | class Page
method build (line 128) | public static function build($pubID){
method setError (line 135) | protected function setError($errorCode){
method addComment (line 140) | public function addComment(){
method removeComment (line 172) | public function removeComment(){
method updateComment (line 204) | public function updateComment(){
method notify (line 242) | private function notify($comment){
method getInfo (line 288) | public function getInfo(){
method getExtendedComments (line 303) | public function getExtendedComments(){
method init (line 342) | protected function init(){
method load (line 351) | protected function load(){
method create (line 368) | protected function create(){
method save (line 391) | protected function save(){
method getJSONPath (line 395) | private function getJSONPath(){
method getUserVisitsBasePath (line 400) | private function getUserVisitsBasePath(){
method getUserVisitsPath (line 404) | private function getUserVisitsPath($uid){
method loadJSON (line 408) | protected function loadJSON(){
method saveToJSON (line 427) | protected function saveToJSON(){
method getUserVisited (line 444) | protected function getUserVisited($uid){
class Project (line 491) | class Project
method build (line 497) | public static function build($pubID){
method setError (line 504) | protected function setError($errorCode){
method init (line 509) | protected function init(){
method load (line 518) | protected function load(){
method create (line 535) | protected function create(){
method getProjectInfo (line 545) | public function getProjectInfo(){
method addPageIDs (line 570) | public function addPageIDs($pagePubID,$pageIntID){
method save (line 581) | protected function save(){
method getJSONPath (line 585) | private function getJSONPath(){
method loadJSON (line 589) | protected function loadJSON(){
method saveToJSON (line 608) | protected function saveToJSON(){
class Forum (line 634) | class Forum
method build (line 648) | public static function build($forumID){
method forumID (line 655) | public function forumID(){
method generatePageIntIDForPubID (line 659) | public function generatePageIntIDForPubID($pagePubID){
method findPageIntIDByPubID (line 668) | public function findPageIntIDByPubID($pubID){
method generateProjectIntIDForPubID (line 675) | public function generateProjectIntIDForPubID($pubID){
method findProjectIntIDByPubID (line 684) | public function findProjectIntIDByPubID($pubID){
method logout (line 692) | public function logout(){
method login (line 703) | public function login(){
method auth (line 725) | public function auth(){
method _createAuthCode (line 764) | private function _createAuthCode($uid){
method _checkAuthCodesFolder (line 770) | private function _checkAuthCodesFolder(){
method _getAuthEmail (line 778) | private function _getAuthEmail($code){
method _saveAuthCode (line 792) | protected function _saveAuthCode($code,$email){
method _dropAuthCode (line 800) | protected function _dropAuthCode($code){
method _createSession (line 809) | private function _createSession($uid){
method _checkSessionsFolder (line 827) | private function _checkSessionsFolder(){
method _getSessionUID (line 836) | private function _getSessionUID($sid){
method _saveSession (line 850) | protected function _saveSession($sid,$uid){
method _dropSession (line 859) | protected function _dropSession($sid){
method _loginSendCode (line 868) | private function _loginSendCode($email,$code){
method _getPagePubIDByReferer (line 886) | private function _getPagePubIDByReferer(){
method buildPage (line 894) | public function buildPage(){
method buildProject (line 899) | public function buildProject(){
method getProjectInfo (line 906) | public function getProjectInfo(){
method sendEmail (line 923) | public function sendEmail($email){
method _sendEmail_uidsTo (line 976) | private function _sendEmail_uidsTo($uids){
method getUserByEmail (line 999) | public function getUserByEmail($email,$name){
method getUserByUID (line 1017) | public function getUserByUID($uid){
method findUserByEmail (line 1025) | private function findUserByEmail(&$usersInfo,$email){
method init (line 1032) | protected function init(){
method _findUserByODC (line 1120) | private function _findUserByODC($name,$email){
method getBasePage (line 1125) | public function getBasePage(){
method getBaseProject (line 1128) | public function getBaseProject(){
method loadUsersInfo (line 1140) | public function loadUsersInfo(){
method setError (line 1165) | protected function setError($errorCode){
method loadServerConfig (line 1172) | protected function loadServerConfig(){
method saveUsersInfo (line 1191) | protected function saveUsersInfo($usersInfo){
method loadForumConfig (line 1207) | protected function loadForumConfig(){
method saveForumConfig (line 1226) | protected function saveForumConfig(){
FILE: comments/backend/lib/Frontend.php
class Frontend (line 3) | class Frontend
method buildFullHTML (line 5) | public static function buildFullHTML(&$forum,&$page,&$commentsInfo){
method buildCommentListHTML (line 30) | public static function buildCommentListHTML(&$page,&$commentsInfo){
FILE: comments/backend/server.php
function exitError (line 8) | function exitError($errorCode)
function exitSuccess (line 19) | function exitSuccess($message,$data=null)
function http_get_param (line 34) | function http_get_param($name)
function http_post_param (line 40) | function http_post_param($name)
FILE: docs/Favicon/resources/jquery.hotkeys.js
function keyHandler (line 130) | function keyHandler(handleObj) {
FILE: docs/Favicon/viewer/AbstractViewer.js
class AbstractViewer (line 2) | class AbstractViewer {
method constructor (line 3) | constructor() {
method pageChanged (line 20) | pageChanged() {
method viewerResized (line 25) | viewerResized() {
method hide (line 29) | hide() {
method show (line 33) | show() {
method toggle (line 37) | toggle() {
method handleKeyDown (line 41) | handleKeyDown(jevent) {
method handleKeyDownWhileInactive (line 45) | handleKeyDownWhileInactive(jevent) {
method onContentClick (line 48) | onContentClick() {
method isVisible (line 54) | isVisible() {
method toggle (line 58) | toggle() {
method _showSelf (line 62) | _showSelf() {
method _hideSelf (line 66) | _hideSelf() {
FILE: docs/Favicon/viewer/CommentsViewer.js
class CommentsViewer (line 4) | class CommentsViewer extends AbstractViewer {
method constructor (line 5) | constructor() {
method initialize (line 15) | initialize(force = false) {
method _hideSelf (line 27) | _hideSelf() {
method handleKeyDownWhileInactive (line 35) | handleKeyDownWhileInactive(jevent) {
method pageChanged (line 49) | pageChanged() {
method handleKeyDown (line 60) | handleKeyDown(jevent) {
method _showCommentCounter (line 79) | _showCommentCounter() {
method updateCommentCounter (line 99) | updateCommentCounter(total) {
method _showComments (line 109) | _showComments() {
method _showSelf (line 137) | _showSelf() {
method _showLoadingMessage (line 148) | _showLoadingMessage() {
FILE: docs/Favicon/viewer/GalleryViewer.js
constant GALLERY_TOP_MARGIN (line 1) | const GALLERY_TOP_MARGIN = 80
constant GALLERY_LEFTRIGH_MARGIN (line 2) | const GALLERY_LEFTRIGH_MARGIN = 40
class GalleryViewerMapLink (line 5) | class GalleryViewerMapLink {
method constructor (line 6) | constructor(index, link, spage, dpage) {
method buildCode (line 18) | buildCode(zoom, visible) {
class GalleryViewer (line 72) | class GalleryViewer extends AbstractViewer {
method constructor (line 73) | constructor() {
method initialize (line 97) | initialize(force = false, skipZoomUpdate = false) {
method handleKeyDown (line 128) | handleKeyDown(jevent) {
method mapZoomChanged (line 150) | mapZoomChanged(zoomValue) {
method viewerResized (line 156) | viewerResized() {
method handleKeyDownWhileInactive (line 161) | handleKeyDownWhileInactive(jevent) {
method handleURLParam (line 176) | handleURLParam(paramValue) {
method enableMapMode (line 185) | enableMapMode(enabled) {
method showMapLinks (line 193) | showMapLinks(visible) {
method resetMapZoom (line 200) | resetMapZoom() {
method _showSelf (line 205) | _showSelf() {
method _hideSelf (line 223) | _hideSelf() {
method loadPages (line 231) | loadPages() {
method loadPagesAbs (line 238) | loadPagesAbs() {
method addMapPageGroupTitle (line 307) | addMapPageGroupTitle(group) {
method selectPage (line 321) | selectPage(index) {
method mouseEnterPage (line 326) | mouseEnterPage(index) {
method loadOnePage (line 336) | loadOnePage(page) {
method loadOnePageAbs (line 379) | loadOnePageAbs(page, pageLeft, pageTop) {
method _valueToStyle (line 423) | _valueToStyle(styleName, v, absDelta = 0) {
method _showHideMapLinks (line 427) | _showHideMapLinks(show) {
method _buildMapLinks (line 434) | _buildMapLinks(finalWidth, finalHeight) {
function searchScreen (line 475) | function searchScreen() {
FILE: docs/Favicon/viewer/LayersData.js
constant SYMBOLS_DICT (line 1) | const SYMBOLS_DICT = {};
constant TOKENS_DICT (line 2) | const TOKENS_DICT = {};
FILE: docs/Favicon/viewer/SymbolViewer.js
constant ELEMENTINSPECTOR_LINUX_FONT_SIZES (line 1) | const ELEMENTINSPECTOR_LINUX_FONT_SIZES = {
constant SUPPORT_TYPES (line 19) | const SUPPORT_TYPES = ["Text", "ShapePath", "Image"]
class SymbolViewer (line 21) | class SymbolViewer extends AbstractViewer {
method constructor (line 22) | constructor() {
method initialize (line 31) | initialize(force = false) {
method _setSymCheck (line 61) | _setSymCheck(showSymbols) {
method pageChanged (line 69) | pageChanged() {
method _selectLib (line 73) | _selectLib(libName) {
method _reShowContent (line 78) | _reShowContent() {
method toggle (line 95) | toggle() {
method _hideSelf (line 101) | _hideSelf() {
method onContentClick (line 118) | onContentClick() {
method handleKeyDown (line 123) | handleKeyDown(jevent) {
method handleKeyDownWhileInactive (line 138) | handleKeyDownWhileInactive(jevent) {
method _showSelf (line 152) | _showSelf() {
method _showEmptyContent (line 173) | _showEmptyContent() {
method _buildSymbolLinks (line 178) | _buildSymbolLinks() {
method _showPage (line 186) | _showPage(page) {
method _create (line 207) | _create() {
method _processSymbolList (line 216) | _processSymbolList(layers, isParentSymbol = false) {
method _processLayerList (line 243) | _processLayerList(layers, sSI = null) {
method _showElement (line 253) | _showElement(l, siLayer = null) {
method setSelected (line 455) | setSelected(event = null, layer = null, a = null, force = false) {
method findOtherSelection (line 525) | findOtherSelection(click, layers, foundLayers) {
method mouseEnterLayerDiv (line 541) | mouseEnterLayerDiv(div) {
method _drawLeftHMargin (line 566) | _drawLeftHMargin(currentPanel, layer, slayer) {
method _drawRightHMargin (line 594) | _drawRightHMargin(currentPanel, layer, slayer) {
method _drawTopVMargin (line 626) | _drawTopVMargin(currentPanel, layer, slayer) {
method _drawBottomVMargin (line 653) | _drawBottomVMargin(currentPanel, layer, slayer) {
method _findLayersCenterX (line 685) | _findLayersCenterX(l, sl) {
method _findLayersCenterY (line 691) | _findLayersCenterY(l, sl) {
method _drawMarginLine (line 697) | _drawMarginLine(currentPanel, x, y, width, height, className) {
method _drawMarginValue (line 704) | _drawMarginValue(currentPanel, x, y, value) {
method _decorateCSS (line 719) | _decorateCSS(layer, tokens, siLayer) {
method _decorateSwatchToken (line 762) | _decorateSwatchToken(tokens, styleValue) {
method _decorateStyleToken (line 768) | _decorateStyleToken(style, tokens, siLayer, styleValue) {
method _formatStyleValue (line 783) | _formatStyleValue(style = "font-size", styleValue = "13px") {
method _showTextPropery (line 795) | _showTextPropery(propName, propValue, postfix = "") {
method _findTokenValueByName (line 801) | _findTokenValueByName(tokenName, libName, styleValue = null) {
method _findSymbolAndLibBySymbolName (line 825) | _findSymbolAndLibBySymbolName(symName) {
method _findStyleAndLibByStyleName (line 838) | _findStyleAndLibByStyleName(styleName) {
method _findSwatchTokens (line 855) | _findSwatchTokens(cv) {
FILE: docs/Favicon/viewer/VersionViewer.js
function getVersionInfoRequest (line 1) | function getVersionInfoRequest() {
class VersionViewer (line 17) | class VersionViewer extends AbstractViewer {
method constructor (line 18) | constructor() {
method initialize (line 24) | initialize(force = false) {
method goTo (line 35) | goTo(pageIndex) {
method switchMode (line 40) | switchMode(delta) {
method _hideSelf (line 61) | _hideSelf() {
method pageChanged (line 70) | pageChanged() {
method handleKeyDownWhileInactive (line 83) | handleKeyDownWhileInactive(jevent) {
method handleKeyDown (line 101) | handleKeyDown(jevent) {
method _restoreNewImages (line 122) | _restoreNewImages() {
method _showScreens (line 129) | _showScreens(data, showNew) {
method _showCurrentPageDiffs (line 157) | _showCurrentPageDiffs() {
method _loadData (line 186) | _loadData(data) {
method _askServerTools (line 208) | _askServerTools() {
method _showSelf (line 216) | _showSelf() {
method _showLoadingMessage (line 223) | _showLoadingMessage() {
FILE: docs/Favicon/viewer/viewer-page.js
constant EVENT_HOVER (line 25) | const EVENT_HOVER = 1
constant TRANS_ANIM_NONE (line 26) | const TRANS_ANIM_NONE = 0
constant TRANS_ANIMATIONS (line 27) | let TRANS_ANIMATIONS = [
function inViewport (line 38) | function inViewport($el) {
function handleAnimationEndOnHide (line 45) | function handleAnimationEndOnHide(el) {
function handleAnimationEndOnShow (line 54) | function handleAnimationEndOnShow(el) {
class ViewerPage (line 65) | class ViewerPage {
method constructor (line 67) | constructor() {
method showHideGalleryLinks (line 88) | showHideGalleryLinks(show = null) {
method _showHideGalleryLinkSet (line 94) | _showHideGalleryLinkSet(links, forceShow = null) {
method getHash (line 106) | getHash() {
method hide (line 111) | hide(hideChilds = false, disableAnim = false) {
method hideCurrentOverlays (line 147) | hideCurrentOverlays() {
method hideChildOverlays (line 155) | hideChildOverlays() {
method hideOtherParentOverlays (line 163) | hideOtherParentOverlays() {
method show (line 172) | show(disableAnim = false) {
method updatePosition (line 191) | updatePosition() {
method showOverlayByLinkIndex (line 219) | showOverlayByLinkIndex(linkIndex) {
method onMouseMove (line 250) | onMouseMove(x, y) {
method onMouseMoveOverlay (line 259) | onMouseMoveOverlay(x, y) {
method showAsOverlayInCurrentPage (line 312) | showAsOverlayInCurrentPage(orgPage, link, posX, posY, linkParentFixed,...
method loadImages (line 422) | loadImages(force = false) {
method showLayout (line 524) | showLayout() {
method _addLayoutLines (line 531) | _addLayoutLines(imageDiv) {
method _getSrcPageAndLink (line 560) | _getSrcPageAndLink() {
method _getLinkByTargetPage (line 573) | _getLinkByTargetPage(page, links, targetPageIndex) {
method _getLinkByIndex (line 584) | _getLinkByIndex(index) {
method _getLinkByIndexInLinks (line 594) | _getLinkByIndexInLinks(index, links) {
method _loadSingleImage (line 602) | _loadSingleImage(sizeSrc, idPrefix) {
method _createLinks (line 620) | _createLinks(panel) {
function handleLinkEvent (line 688) | function handleLinkEvent(event) {
FILE: docs/Favicon/viewer/viewer.js
function getQuery (line 5) | function getQuery(uri, q) {
function showError (line 9) | function showError(error) {
function showMessage (line 13) | function showMessage(message) {
function checkFolderInfoRequest (line 17) | function checkFolderInfoRequest(resp) {
function handleDecreaseVersion (line 28) | function handleDecreaseVersion() {
function handleIncreaseVersion (line 35) | function handleIncreaseVersion() {
function doTransNext (line 46) | function doTransNext() {
function pagerMarkImageAsLoaded (line 77) | function pagerMarkImageAsLoaded() {
function preloadAllPageImages (line 84) | async function preloadAllPageImages() {
function reloadAllPageImages (line 96) | function reloadAllPageImages() {
function doBlinkHotspots (line 108) | function doBlinkHotspots() {
function splitStylesStr (line 114) | function splitStylesStr(str) {
function createViewer (line 120) | function createViewer(story, files) {
function addRemoveClass (line 1043) | function addRemoveClass(mode, el, cls) {
function redirectFromHashToSearch (line 1069) | function redirectFromHashToSearch() {
function handleStateChanges (line 1099) | function handleStateChanges(e) {
FILE: docs/FixedLayers/resources/jquery.hotkeys.js
function keyHandler (line 130) | function keyHandler(handleObj) {
FILE: docs/FixedLayers/viewer/AbstractViewer.js
class AbstractViewer (line 2) | class AbstractViewer {
method constructor (line 3) | constructor() {
method pageChanged (line 20) | pageChanged() {
method viewerResized (line 25) | viewerResized() {
method hide (line 29) | hide() {
method show (line 33) | show() {
method toggle (line 37) | toggle() {
method handleKeyDown (line 41) | handleKeyDown(jevent) {
method handleKeyDownWhileInactive (line 45) | handleKeyDownWhileInactive(jevent) {
method onContentClick (line 48) | onContentClick() {
method isVisible (line 54) | isVisible() {
method toggle (line 58) | toggle() {
method _showSelf (line 62) | _showSelf() {
method _hideSelf (line 66) | _hideSelf() {
FILE: docs/FixedLayers/viewer/CommentsViewer.js
class CommentsViewer (line 4) | class CommentsViewer extends AbstractViewer {
method constructor (line 5) | constructor() {
method initialize (line 15) | initialize(force = false) {
method _hideSelf (line 27) | _hideSelf() {
method handleKeyDownWhileInactive (line 35) | handleKeyDownWhileInactive(jevent) {
method pageChanged (line 49) | pageChanged() {
method handleKeyDown (line 60) | handleKeyDown(jevent) {
method _showCommentCounter (line 79) | _showCommentCounter() {
method updateCommentCounter (line 99) | updateCommentCounter(total) {
method _showComments (line 109) | _showComments() {
method _showSelf (line 137) | _showSelf() {
method _showLoadingMessage (line 148) | _showLoadingMessage() {
FILE: docs/FixedLayers/viewer/GalleryViewer.js
constant GALLERY_TOP_MARGIN (line 1) | const GALLERY_TOP_MARGIN = 80
constant GALLERY_LEFTRIGH_MARGIN (line 2) | const GALLERY_LEFTRIGH_MARGIN = 40
class GalleryViewerMapLink (line 5) | class GalleryViewerMapLink {
method constructor (line 6) | constructor(index, link, spage, dpage) {
method buildCode (line 18) | buildCode(zoom, visible) {
class GalleryViewer (line 72) | class GalleryViewer extends AbstractViewer {
method constructor (line 73) | constructor() {
method initialize (line 97) | initialize(force = false, skipZoomUpdate = false) {
method handleKeyDown (line 128) | handleKeyDown(jevent) {
method mapZoomChanged (line 150) | mapZoomChanged(zoomValue) {
method viewerResized (line 156) | viewerResized() {
method handleKeyDownWhileInactive (line 161) | handleKeyDownWhileInactive(jevent) {
method handleURLParam (line 176) | handleURLParam(paramValue) {
method enableMapMode (line 185) | enableMapMode(enabled) {
method showMapLinks (line 193) | showMapLinks(visible) {
method resetMapZoom (line 200) | resetMapZoom() {
method _showSelf (line 205) | _showSelf() {
method _hideSelf (line 223) | _hideSelf() {
method loadPages (line 231) | loadPages() {
method loadPagesAbs (line 238) | loadPagesAbs() {
method addMapPageGroupTitle (line 307) | addMapPageGroupTitle(group) {
method selectPage (line 321) | selectPage(index) {
method mouseEnterPage (line 326) | mouseEnterPage(index) {
method loadOnePage (line 336) | loadOnePage(page) {
method loadOnePageAbs (line 379) | loadOnePageAbs(page, pageLeft, pageTop) {
method _valueToStyle (line 423) | _valueToStyle(styleName, v, absDelta = 0) {
method _showHideMapLinks (line 427) | _showHideMapLinks(show) {
method _buildMapLinks (line 434) | _buildMapLinks(finalWidth, finalHeight) {
function searchScreen (line 475) | function searchScreen() {
FILE: docs/FixedLayers/viewer/LayersData.js
constant SYMBOLS_DICT (line 1) | const SYMBOLS_DICT = {"ux1-ui":{"styles":{"Controls/Buttons/BorderButton...
constant TOKENS_DICT (line 2) | const TOKENS_DICT = {'ux1-ui':{"@space-xxx-large":"60px","@space-xx-larg...
FILE: docs/FixedLayers/viewer/SymbolViewer.js
constant ELEMENTINSPECTOR_LINUX_FONT_SIZES (line 1) | const ELEMENTINSPECTOR_LINUX_FONT_SIZES = {
constant SUPPORT_TYPES (line 19) | const SUPPORT_TYPES = ["Text", "ShapePath", "Image"]
class SymbolViewer (line 21) | class SymbolViewer extends AbstractViewer {
method constructor (line 22) | constructor() {
method initialize (line 31) | initialize(force = false) {
method _setSymCheck (line 61) | _setSymCheck(showSymbols) {
method pageChanged (line 69) | pageChanged() {
method _selectLib (line 73) | _selectLib(libName) {
method _reShowContent (line 78) | _reShowContent() {
method toggle (line 95) | toggle() {
method _hideSelf (line 101) | _hideSelf() {
method onContentClick (line 118) | onContentClick() {
method handleKeyDown (line 123) | handleKeyDown(jevent) {
method handleKeyDownWhileInactive (line 138) | handleKeyDownWhileInactive(jevent) {
method _showSelf (line 152) | _showSelf() {
method _showEmptyContent (line 173) | _showEmptyContent() {
method _buildSymbolLinks (line 178) | _buildSymbolLinks() {
method _showPage (line 186) | _showPage(page) {
method _create (line 207) | _create() {
method _processSymbolList (line 216) | _processSymbolList(layers, isParentSymbol = false) {
method _processLayerList (line 243) | _processLayerList(layers, sSI = null) {
method _showElement (line 253) | _showElement(l, siLayer = null) {
method setSelected (line 455) | setSelected(event = null, layer = null, a = null, force = false) {
method findOtherSelection (line 525) | findOtherSelection(click, layers, foundLayers) {
method mouseEnterLayerDiv (line 541) | mouseEnterLayerDiv(div) {
method _drawLeftHMargin (line 566) | _drawLeftHMargin(currentPanel, layer, slayer) {
method _drawRightHMargin (line 594) | _drawRightHMargin(currentPanel, layer, slayer) {
method _drawTopVMargin (line 626) | _drawTopVMargin(currentPanel, layer, slayer) {
method _drawBottomVMargin (line 653) | _drawBottomVMargin(currentPanel, layer, slayer) {
method _findLayersCenterX (line 685) | _findLayersCenterX(l, sl) {
method _findLayersCenterY (line 691) | _findLayersCenterY(l, sl) {
method _drawMarginLine (line 697) | _drawMarginLine(currentPanel, x, y, width, height, className) {
method _drawMarginValue (line 704) | _drawMarginValue(currentPanel, x, y, value) {
method _decorateCSS (line 719) | _decorateCSS(layer, tokens, siLayer) {
method _decorateSwatchToken (line 762) | _decorateSwatchToken(tokens, styleValue) {
method _decorateStyleToken (line 768) | _decorateStyleToken(style, tokens, siLayer, styleValue) {
method _formatStyleValue (line 783) | _formatStyleValue(style = "font-size", styleValue = "13px") {
method _showTextPropery (line 795) | _showTextPropery(propName, propValue, postfix = "") {
method _findTokenValueByName (line 801) | _findTokenValueByName(tokenName, libName, styleValue = null) {
method _findSymbolAndLibBySymbolName (line 825) | _findSymbolAndLibBySymbolName(symName) {
method _findStyleAndLibByStyleName (line 838) | _findStyleAndLibByStyleName(styleName) {
method _findSwatchTokens (line 855) | _findSwatchTokens(cv) {
FILE: docs/FixedLayers/viewer/VersionViewer.js
function getVersionInfoRequest (line 1) | function getVersionInfoRequest() {
class VersionViewer (line 17) | class VersionViewer extends AbstractViewer {
method constructor (line 18) | constructor() {
method initialize (line 24) | initialize(force = false) {
method goTo (line 35) | goTo(pageIndex) {
method switchMode (line 40) | switchMode(delta) {
method _hideSelf (line 61) | _hideSelf() {
method pageChanged (line 70) | pageChanged() {
method handleKeyDownWhileInactive (line 83) | handleKeyDownWhileInactive(jevent) {
method handleKeyDown (line 101) | handleKeyDown(jevent) {
method _restoreNewImages (line 122) | _restoreNewImages() {
method _showScreens (line 129) | _showScreens(data, showNew) {
method _showCurrentPageDiffs (line 157) | _showCurrentPageDiffs() {
method _loadData (line 186) | _loadData(data) {
method _askServerTools (line 208) | _askServerTools() {
method _showSelf (line 216) | _showSelf() {
method _showLoadingMessage (line 223) | _showLoadingMessage() {
FILE: docs/FixedLayers/viewer/viewer-page.js
constant EVENT_HOVER (line 25) | const EVENT_HOVER = 1
constant TRANS_ANIM_NONE (line 26) | const TRANS_ANIM_NONE = 0
constant TRANS_ANIMATIONS (line 27) | let TRANS_ANIMATIONS = [
function inViewport (line 38) | function inViewport($el) {
function handleAnimationEndOnHide (line 45) | function handleAnimationEndOnHide(el) {
function handleAnimationEndOnShow (line 54) | function handleAnimationEndOnShow(el) {
class ViewerPage (line 65) | class ViewerPage {
method constructor (line 67) | constructor() {
method showHideGalleryLinks (line 88) | showHideGalleryLinks(show = null) {
method _showHideGalleryLinkSet (line 94) | _showHideGalleryLinkSet(links, forceShow = null) {
method getHash (line 106) | getHash() {
method hide (line 111) | hide(hideChilds = false, disableAnim = false) {
method hideCurrentOverlays (line 147) | hideCurrentOverlays() {
method hideChildOverlays (line 155) | hideChildOverlays() {
method hideOtherParentOverlays (line 163) | hideOtherParentOverlays() {
method show (line 172) | show(disableAnim = false) {
method updatePosition (line 191) | updatePosition() {
method showOverlayByLinkIndex (line 219) | showOverlayByLinkIndex(linkIndex) {
method onMouseMove (line 250) | onMouseMove(x, y) {
method onMouseMoveOverlay (line 259) | onMouseMoveOverlay(x, y) {
method showAsOverlayInCurrentPage (line 312) | showAsOverlayInCurrentPage(orgPage, link, posX, posY, linkParentFixed,...
method loadImages (line 422) | loadImages(force = false) {
method showLayout (line 524) | showLayout() {
method _addLayoutLines (line 531) | _addLayoutLines(imageDiv) {
method _getSrcPageAndLink (line 560) | _getSrcPageAndLink() {
method _getLinkByTargetPage (line 573) | _getLinkByTargetPage(page, links, targetPageIndex) {
method _getLinkByIndex (line 584) | _getLinkByIndex(index) {
method _getLinkByIndexInLinks (line 594) | _getLinkByIndexInLinks(index, links) {
method _loadSingleImage (line 602) | _loadSingleImage(sizeSrc, idPrefix) {
method _createLinks (line 620) | _createLinks(panel) {
function handleLinkEvent (line 688) | function handleLinkEvent(event) {
FILE: docs/FixedLayers/viewer/viewer.js
function getQuery (line 5) | function getQuery(uri, q) {
function showError (line 9) | function showError(error) {
function showMessage (line 13) | function showMessage(message) {
function checkFolderInfoRequest (line 17) | function checkFolderInfoRequest(resp) {
function handleDecreaseVersion (line 28) | function handleDecreaseVersion() {
function handleIncreaseVersion (line 35) | function handleIncreaseVersion() {
function doTransNext (line 46) | function doTransNext() {
function pagerMarkImageAsLoaded (line 77) | function pagerMarkImageAsLoaded() {
function preloadAllPageImages (line 84) | async function preloadAllPageImages() {
function reloadAllPageImages (line 96) | function reloadAllPageImages() {
function doBlinkHotspots (line 108) | function doBlinkHotspots() {
function splitStylesStr (line 114) | function splitStylesStr(str) {
function createViewer (line 120) | function createViewer(story, files) {
function addRemoveClass (line 1043) | function addRemoveClass(mode, el, cls) {
function redirectFromHashToSearch (line 1069) | function redirectFromHashToSearch() {
function handleStateChanges (line 1099) | function handleStateChanges(e) {
FILE: server_tools/announce.php
function getParam (line 20) | function getParam($key){
function printError (line 25) | function printError($error){
class Worker (line 31) | class Worker{
method __construct (line 36) | public function __construct()
method _readParams (line 61) | private function _readParams(){
method _findPrevVersion (line 104) | private function _findPrevVersion(){
method _compareVers (line 119) | private function _compareVers(){
method _saveData (line 195) | private function _saveData(){
method _saveSplitData (line 222) | private function _saveSplitData($local_dir,$data){
method _postToTelegram (line 246) | private function _postToTelegram(){
method _splitData (line 271) | private function _splitData(){
method run (line 319) | public function run()
FILE: server_tools/journal.php
class Worker (line 3) | class Worker{
method __construct (line 8) | public function __construct()
method _print_data (line 15) | public function _print_data()
method run (line 26) | public function run()
FILE: server_tools/lib/FolderInfo.php
function folderInfoFilter (line 3) | function folderInfoFilter($var){
function find_int_in_string (line 8) | function find_int_in_string($text) {
class FolderInfo (line 16) | class FolderInfo{
method __construct (line 31) | public function __construct()
method _parse_context (line 38) | private function _parse_context($local_url="")
method _read_dir (line 79) | private function _read_dir()
method _build_url (line 100) | private function _build_url($version){
method _calc_links (line 104) | private function _calc_links(){
method collectInfo (line 138) | public function collectInfo($local_url="")
method dump_json (line 147) | public function dump_json(){
method debug (line 151) | public function debug(){
FILE: server_tools/version_info.php
function folderInfoFilter (line 3) | function folderInfoFilter($var){
function find_int_in_string (line 8) | function find_int_in_string($text) {
class Worker (line 16) | class Worker{
method __construct (line 28) | public function __construct()
method _parse_context (line 36) | private function _parse_context($local_url="")
method _tranformImageURLbyPreview (line 80) | private function _tranformImageURLbyPreview($img){
method _find_info (line 85) | private function _find_info(){
method _extend_rec_info (line 105) | private function _extend_rec_info(& $rec){
method _build_url (line 125) | private function _build_url($version){
method collectInfo (line 129) | public function collectInfo($local_url="")
method dump_json (line 137) | public function dump_json(){
Condensed preview — 162 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,370K chars).
[
{
"path": ".gitignore",
"chars": 74,
"preview": "\n.DS_Store\nPuzzlePublisher.sketchplugin/Contents/Sketch/.vscode/sftp.json\n"
},
{
"path": "CHANGELOG.md",
"chars": 18551,
"preview": "# Change Log\nSee discussion on https://github.com/ingrammicro/puzzle-publisher/discussions site\n\n## Version 17.9.0 (25 "
},
{
"path": "Hints.md",
"chars": 2524,
"preview": "# Puzzle Publisher plugin Hints\n\n## [Hint 1](#hint1): Use post-processing to inject your own information in generated HT"
},
{
"path": "Ideas.md",
"chars": 390,
"preview": "### Add page transition effects\n\n### Auto transition to other artboard after delay\nNew artboard setting: \n**Transition "
},
{
"path": "LICENSE",
"chars": 35141,
"preview": " GNU GENERAL PUBLIC LICENSE\n Version 3, 29 June 2007\n\n Copyright (C) 2007 Free "
},
{
"path": "PuzzlePublisher.sketchplugin/Contents/Sketch/cmd-actions.js",
"chars": 405,
"preview": "@import \"lib/ga.js\";\n@import \"lib/utils.js\";\n@import \"constants.js\";\n\nfunction onStartup(context) {\n const Settings ="
},
{
"path": "PuzzlePublisher.sketchplugin/Contents/Sketch/cmdline-functions.js",
"chars": 4305,
"preview": "@import \"exporter/exporter-run.js\"\n\n// osascript -e 'quit app \"Sketch\"'\nconst example = `\n/Applications/Sketch.app/Conte"
},
{
"path": "PuzzlePublisher.sketchplugin/Contents/Sketch/constants.js",
"chars": 8366,
"preview": "const Constants = {\n TAB_SIZE: 2,\n HOTSPOT_PADDING: 0,\n LOGGING: false,\n SERVER_ANNOUNCE_SCRIPT: \"announce.p"
},
{
"path": "PuzzlePublisher.sketchplugin/Contents/Sketch/exporter/PZArtboard.js",
"chars": 27383,
"preview": "@import(\"constants.js\")\n@import(\"lib/utils.js\")\n@import(\"exporter/PZLayer.js\")\n@import(\"exporter/PZDoc.js\")\n\nSketch = re"
},
{
"path": "PuzzlePublisher.sketchplugin/Contents/Sketch/exporter/PZDoc.js",
"chars": 11208,
"preview": "@import(\"constants.js\")\n@import(\"lib/utils.js\")\nSketch = require('sketch/dom')\n\nconst replaceValidKeys = [\n \"x\", \"y\","
},
{
"path": "PuzzlePublisher.sketchplugin/Contents/Sketch/exporter/PZLayer.js",
"chars": 24566,
"preview": "const { off } = require(\"process\")\n@import(\"constants.js\")\n@import(\"lib/utils.js\")\n@import(\"exporter/PZDoc.js\")\n\nvar Ske"
},
{
"path": "PuzzlePublisher.sketchplugin/Contents/Sketch/exporter/PZPage.js",
"chars": 3977,
"preview": "@import(\"constants.js\")\n@import(\"lib/utils.js\")\nSketch = require('sketch/dom')\n\nvar PZPage_touched = false\n\nclass PZPage"
},
{
"path": "PuzzlePublisher.sketchplugin/Contents/Sketch/exporter/exporter-run.js",
"chars": 9572,
"preview": "@import \"constants.js\"\n@import \"exporter/exporter.js\"\n@import \"lib/uidialog.js\"\n@import \"lib/uipanel.js\"\n@import \"lib/ut"
},
{
"path": "PuzzlePublisher.sketchplugin/Contents/Sketch/exporter/exporter.js",
"chars": 16046,
"preview": "@import(\"constants.js\")\n@import(\"lib/utils.js\")\n@import(\"lib/ga.js\")\n@import(\"pp-viewer/exporter/exporter-build-html.js\""
},
{
"path": "PuzzlePublisher.sketchplugin/Contents/Sketch/exporter/publisher.js",
"chars": 30719,
"preview": "@import(\"constants.js\")\n@import(\"lib/utils.js\")\n@import(\"lib/ga.js\")\n@import(\"lib/uidialog.js\")\n\n@import \"miro/api.js\";\n"
},
{
"path": "PuzzlePublisher.sketchplugin/Contents/Sketch/lib/ga.js",
"chars": 2454,
"preview": "@import \"lib/utils.js\";\n@import \"constants.js\";\n\nfunction jsonToQueryString(json) {\n return Object.keys(json)\n "
},
{
"path": "PuzzlePublisher.sketchplugin/Contents/Sketch/lib/uidialog.js",
"chars": 14832,
"preview": "var UIDialog_iconImage = null\nconst TAB_HEIGHT = 55\n\nfunction Class(className, BaseClass, selectorHandlerDict)\n{\n var"
},
{
"path": "PuzzlePublisher.sketchplugin/Contents/Sketch/lib/uipanel.js",
"chars": 783,
"preview": "class UIPanel extends UIAbstractWindow {\n\n constructor(title) {\n let winRect = NSMakeRect(300, 500, 400, 270)\n"
},
{
"path": "PuzzlePublisher.sketchplugin/Contents/Sketch/lib/utils.js",
"chars": 14659,
"preview": "@import \"constants.js\";\nvar DEBUG = Constants.LOGGING || require('sketch/settings').settingForKey(SettingKeys.PLUGIN_LOG"
},
{
"path": "PuzzlePublisher.sketchplugin/Contents/Sketch/manifest.json",
"chars": 4037,
"preview": "{\n \"name\": \"Puzzle Publisher\",\n \"description\": \"Generates and publishes a HTML clickable prototype\",\n \"author\":"
},
{
"path": "PuzzlePublisher.sketchplugin/Contents/Sketch/menu-cmd-other.js",
"chars": 161,
"preview": "@import(\"constants.js\")\n\nvar showChangeLog = function (context) {\n NSWorkspace.sharedWorkspace().openURL(NSURL.URLWit"
},
{
"path": "PuzzlePublisher.sketchplugin/Contents/Sketch/menu-cmd-publish-miro.js",
"chars": 200,
"preview": "@import(\"exporter/publisher.js\")\n\nvar onRun = function (context) {\n\n UIDialog.setUp(context);\n\n const publisher = "
},
{
"path": "PuzzlePublisher.sketchplugin/Contents/Sketch/menu-cmd-publish.js",
"chars": 190,
"preview": "@import(\"exporter/publisher.js\")\n\nvar onRun = function (context) {\n\n UIDialog.setUp(context);\n\n const publisher = "
},
{
"path": "PuzzlePublisher.sketchplugin/Contents/Sketch/menu-conf-artboard.js",
"chars": 15105,
"preview": "@import \"lib/uidialog.js\";\n@import \"lib/utils.js\";\n@import \"constants.js\";\n\nvar dialog = undefined\n\nfunction enableTypeR"
},
{
"path": "PuzzlePublisher.sketchplugin/Contents/Sketch/menu-conf-document.js",
"chars": 4233,
"preview": "@import \"lib/uidialog.js\";\n@import \"lib/utils.js\";\n@import \"constants.js\";\n\n\nvar onRun = function (context) {\n const "
},
{
"path": "PuzzlePublisher.sketchplugin/Contents/Sketch/menu-conf-layer.js",
"chars": 3630,
"preview": "@import \"lib/uidialog.js\";\n@import \"lib/utils.js\";\n@import \"constants.js\";\n\n\nvar onRun = function (context)\n{\n const "
},
{
"path": "PuzzlePublisher.sketchplugin/Contents/Sketch/menu-conf-plugin-export.js",
"chars": 5883,
"preview": "@import \"lib/uidialog.js\";\n@import \"lib/utils.js\";\n@import \"constants.js\";\n\nconst FILE_TYPES = [\"PNG\", \"JPG\"]\n\n\n\nvar onR"
},
{
"path": "PuzzlePublisher.sketchplugin/Contents/Sketch/menu-conf-plugin-publishing.js",
"chars": 4734,
"preview": "@import \"lib/uidialog.js\";\n@import \"lib/utils.js\";\n@import \"constants.js\";\n\nconst UI = require('sketch/ui')\n\nvar onRun ="
},
{
"path": "PuzzlePublisher.sketchplugin/Contents/Sketch/menu-conf-plugin.js",
"chars": 1586,
"preview": "@import \"lib/uidialog.js\";\n@import \"lib/utils.js\";\n@import \"constants.js\";\n\n\nvar onRun = function (context) {\n const "
},
{
"path": "PuzzlePublisher.sketchplugin/Contents/Sketch/menu-edit-annotations.js",
"chars": 1250,
"preview": "@import(\"lib/uidialog.js\")\n@import(\"lib/utils.js\")\n@import(\"constants.js\")\n\n\nvar onRun = function (context) {\n const "
},
{
"path": "PuzzlePublisher.sketchplugin/Contents/Sketch/menu-export-html-adv.js",
"chars": 2102,
"preview": "@import \"lib/utils.js\"\n@import \"lib/uidialog.js\"\n@import \"exporter/exporter-run.js\"\n@import \"constants.js\"\n\n\nfunction as"
},
{
"path": "PuzzlePublisher.sketchplugin/Contents/Sketch/menu-export-html.js",
"chars": 106,
"preview": "@import \"exporter/exporter-run.js\"\n\nvar onRun = function (context) {\n return runExporter(context)\n};\n\n\n"
},
{
"path": "PuzzlePublisher.sketchplugin/Contents/Sketch/menu-external-link.js",
"chars": 2580,
"preview": "@import \"lib/uidialog.js\";\n@import \"lib/utils.js\";\n@import \"constants.js\"\n\nvar onRun = function (context) {\n const sk"
},
{
"path": "PuzzlePublisher.sketchplugin/Contents/Sketch/miro/api.js",
"chars": 17812,
"preview": "/*\nThe MIT License (MIT)\n\nCopyright (c) 2019 Miro Inc.\n\nPermission is hereby granted, free of charge, to any person obta"
},
{
"path": "PuzzlePublisher.sketchplugin/Contents/Sketch/miro/utils.js",
"chars": 3596,
"preview": "/*\nThe MIT License (MIT)\n\nCopyright (c) 2017 RealtimeBoard Inc.\n\nPermission is hereby granted, free of charge, to any pe"
},
{
"path": "PuzzlePublisher.sketchplugin/Contents/Sketch/pp-viewer/exporter/exporter-build-html.js",
"chars": 58243,
"preview": "const ExporterConstants = {\n DOCUMENT_VERSION: \"docVersion\",\n DOCUMENT_VERSION_PLACEHOLDER: \"V_V_V\",\n DOCUMENT_"
},
{
"path": "PuzzlePublisher.sketchplugin/Contents/Sketch/pp-viewer/viewer/js/AbstractViewer.js",
"chars": 1832,
"preview": "\nclass AbstractViewer {\n constructor(divID) {\n this.divID = divID\n // common viewer settings, can be ch"
},
{
"path": "PuzzlePublisher.sketchplugin/Contents/Sketch/pp-viewer/viewer/js/CommentsViewer.js",
"chars": 4925,
"preview": "\nlet commentsViewer = null;\n\nclass CommentsViewer extends AbstractViewer {\n constructor() {\n super(\"comments_v"
},
{
"path": "PuzzlePublisher.sketchplugin/Contents/Sketch/pp-viewer/viewer/js/ExpViewer.js",
"chars": 6900,
"preview": "const EXP_SCOPE_PAGE = \"page\"\nconst EXP_SCOPE_PROJECT = \"project\"\n\nconst EXP_MODE_PAGES = \"pages\"\nconst EXP_MODE_WIDGETS"
},
{
"path": "PuzzlePublisher.sketchplugin/Contents/Sketch/pp-viewer/viewer/js/GalleryViewer.js",
"chars": 20004,
"preview": "const GALLERY_TOP_MARGIN = 80\nconst GALLERY_LEFTRIGH_MARGIN = 40\n\n\nclass GalleryViewerMapLink {\n constructor(index, l"
},
{
"path": "PuzzlePublisher.sketchplugin/Contents/Sketch/pp-viewer/viewer/js/InfoViewer.js",
"chars": 9439,
"preview": "function getVersionInfoRequest() {\n var resp = this\n if (resp.readyState == resp.DONE) {\n if (resp.status ="
},
{
"path": "PuzzlePublisher.sketchplugin/Contents/Sketch/pp-viewer/viewer/js/PresenterViewer.js",
"chars": 2948,
"preview": "let presenterViewer = null;\nconst STORAGE_TRANSPERIOD = \"presenterViewer.transPeriod\"\n\nfunction doPresenterViewerNext() "
},
{
"path": "PuzzlePublisher.sketchplugin/Contents/Sketch/pp-viewer/viewer/js/SymbolViewer.js",
"chars": 37602,
"preview": "const ELEMENTINSPECTOR_LINUX_FONT_SIZES = {\n \"8px\": \"6px\",\n \"10px\": \"7px\",\n \"11px\": \"8px\",\n \"12px\": \"9px\",\n "
},
{
"path": "PuzzlePublisher.sketchplugin/Contents/Sketch/pp-viewer/viewer/js/Viewer.js",
"chars": 38851,
"preview": "// =============================== PRELOAD IMAGES =========================\nvar pagerLoadingTotal = 0\n\nfunction getQuery"
},
{
"path": "PuzzlePublisher.sketchplugin/Contents/Sketch/pp-viewer/viewer/js/ViewerPage.js",
"chars": 45899,
"preview": "\n///\nconst Constants = {\n ARTBOARD_OVERLAY_PIN_HOTSPOT: 0,\n ARTBOARD_OVERLAY_PIN_PAGE: 1,\n //\n ARTBOARD_OVER"
},
{
"path": "PuzzlePublisher.sketchplugin/Contents/Sketch/pp-viewer/viewer/js/other/jquery.hotkeys.js",
"chars": 5019,
"preview": "/*jslint browser: true*/\n/*jslint jquery: true*/\n\n/*\n * jQuery Hotkeys Plugin\n * Copyright 2010, John Resig\n * Dual lice"
},
{
"path": "PuzzlePublisher.sketchplugin/Contents/Sketch/pp-viewer/viewer/resources/animations.css",
"chars": 1919,
"preview": "@keyframes fadeIn {\n from {\n opacity: 0;\n }\n to {\n opacity: 1;\n }\n}\n@keyframes fadeOut {\n from {\n opacity:"
},
{
"path": "PuzzlePublisher.sketchplugin/Contents/Sketch/pp-viewer/viewer/resources/viewer-center.css",
"chars": 472,
"preview": "#container, #content {\n\ttop: 0;\n\tleft: 0;\n\tright: 0;\n\tbottom: 0;\n\theight: 100vh;\n\tdisplay: grid;\n\tmargin-left: auto;\n\tma"
},
{
"path": "PuzzlePublisher.sketchplugin/Contents/Sketch/pp-viewer/viewer/resources/viewer-top.css",
"chars": 450,
"preview": "#container{\n\ttext-align: center;\n margin: 0 auto;\t\n overflow: auto;\n}\n\n#content,#map{\n\ttext-align: center;\n mar"
},
{
"path": "PuzzlePublisher.sketchplugin/Contents/Sketch/pp-viewer/viewer/resources/viewer.css",
"chars": 24202,
"preview": "/*@import \"https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.7.2/animate.css\";*/\n\n/* Tooltip container */\n.tooltip {\n"
},
{
"path": "PuzzlePublisher.sketchplugin/Contents/Sketch/scripts/export.sh",
"chars": 862,
"preview": "#!/bin/bash\n\ntempDir=\"$1\"\nfileName=\"$2\"\ncontext=\"$3\"\n#docName=\"$3\"\n#exportOptions=\"$4\"\n\nSKETCH=$(mdfind kMDItemCFBundleI"
},
{
"path": "PuzzlePublisher.sketchplugin/Contents/Sketch/scripts/preparePublish.sh",
"chars": 1739,
"preview": "#!/bin/bash\n\nver=\"$1\"\nallMockupsFolder=\"$2\"\ndocFolder=\"$3\"\nremoteFolder=\"$4\"\ndocPathValue=\"$5\"\nmirror1=\"$6\"\nsshPort=\"$7\""
},
{
"path": "PuzzlePublisher.sketchplugin/Contents/Sketch/scripts/publish.sh",
"chars": 1041,
"preview": "#!/bin/bash\n\nver=\"$1\"\nallMockupsFolder=\"$2\"\ndocFolder=\"$3\"\nremoteFolder=\"$4\"\ndocPathValue=\"$5\"\nmirror1=\"$6\"\nsshPort=\"$7\""
},
{
"path": "PuzzlePublisher.sketchplugin/Contents/Sketch/tokens/DSExporter.js",
"chars": 24448,
"preview": "@import(\"constants.js\")\n@import(\"lib/utils.js\")\n@import(\"lib/uidialog.js\")\n@import(\"lib/ga.js\")\n\nconst bordedLineJoinMap"
},
{
"path": "README.md",
"chars": 5150,
"preview": "# Puzzle Publisher\n\nA Sketch plugin that exports Sketch artboards into clickable HTML file. \n\n### Join [GitHub Comments]"
},
{
"path": "appcast.xml",
"chars": 732,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<rss version=\"2.0\" xmlns:sparkle=\"http://www.andymatuschak.org/xml-namespaces/spa"
},
{
"path": "comments/backend/.gitignore",
"chars": 13,
"preview": "sendgrid.env\n"
},
{
"path": "comments/backend/config/.htaccess",
"chars": 14,
"preview": "Deny from all\n"
},
{
"path": "comments/backend/config/forums.json",
"chars": 144,
"preview": "{\n \"1234567890\": {\n \"name\": \"Forum\",\n \"email\": {\n \"from-email\": \"\",\n \"sendgrid-ke"
},
{
"path": "comments/backend/data/.htaccess",
"chars": 14,
"preview": "Deny from all\n"
},
{
"path": "comments/backend/lib/Comments.js",
"chars": 29823,
"preview": "function commentReplaceEnds(value) {\n return value.replace(new RegExp('\\r?\\n', 'g'), '<br/>')\n}\n\nclass CommentsAbstra"
},
{
"path": "comments/backend/lib/Forum.php",
"chars": 39611,
"preview": "<?php\n\n////////////// ERROR LIST ///////////////////////////////////////\n// 001. CONTEXT\nconst ERROR_UNKNOWN_CMD "
},
{
"path": "comments/backend/lib/Frontend.php",
"chars": 1341,
"preview": "<?php\n\nclass Frontend\n{ \n public static function buildFullHTML(&$forum,&$page,&$commentsInfo){\n $url = ($_S"
},
{
"path": "comments/backend/server.php",
"chars": 4252,
"preview": "<?php\nheader('Access-Control-Allow-Origin: *');\ninclude 'lib/Forum.php';\ninclude 'lib/Frontend.php';\n\nconst temp_folder "
},
{
"path": "comments/frontend/index.php",
"chars": 62,
"preview": "<?php \n\nvar_dump(mail(\"max@bazarov.ru\",\"Test\",\"ddddddd\"));\n\n?>"
},
{
"path": "comments/frontend/test.html",
"chars": 1985,
"preview": "<html>\n\n<head>\n <!-- Required meta tags -->\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=devic"
},
{
"path": "comments/readme.txt",
"chars": 114,
"preview": " chcon -R -t httpd_sys_content_t .\n chcon -R -t httpd_sys_rw_content_t .\n chown -R apache:apache config\n"
},
{
"path": "docs/Favicon/index.html",
"chars": 38643,
"preview": "<!DOCTYPE html>\n<html>\n<head>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n<meta name=\"generator\""
},
{
"path": "docs/Favicon/resources/animations.css",
"chars": 1919,
"preview": "@keyframes fadeIn {\n from {\n opacity: 0;\n }\n to {\n opacity: 1;\n }\n}\n@keyframes fadeOut {\n from {\n opacity:"
},
{
"path": "docs/Favicon/resources/cb-modern-ui.css",
"chars": 1919,
"preview": "@keyframes fadeIn {\n from {\n opacity: 0;\n }\n to {\n opacity: 1;\n }\n}\n@keyframes fadeOut {\n from {\n opacity:"
},
{
"path": "docs/Favicon/resources/jquery.hotkeys.js",
"chars": 5019,
"preview": "/*jslint browser: true*/\n/*jslint jquery: true*/\n\n/*\n * jQuery Hotkeys Plugin\n * Copyright 2010, John Resig\n * Dual lice"
},
{
"path": "docs/Favicon/resources/ux1-ui.css",
"chars": 1981,
"preview": "/* MOD FOR VARIANT B WAS @color-background-primary*/\n@keyframes fadeIn {\n from {\n opacity: 0;\n }\n to {\n opacity"
},
{
"path": "docs/Favicon/resources/viewer-center.css",
"chars": 472,
"preview": "#container, #content {\n\ttop: 0;\n\tleft: 0;\n\tright: 0;\n\tbottom: 0;\n\theight: 100vh;\n\tdisplay: grid;\n\tmargin-left: auto;\n\tma"
},
{
"path": "docs/Favicon/resources/viewer-fonts.css",
"chars": 472,
"preview": "@font-face {\n font-family: 'Font Awesome';\n font-weight:400;\n src: url('fonts/fa-regular-400.woff2') format(\"wo"
},
{
"path": "docs/Favicon/resources/viewer-top.css",
"chars": 452,
"preview": "#container{\n\ttext-align: center;\n margin: 0 auto;\t\n overflow: auto;\n}\n\n#content,#map{\n\ttext-align: center;\n mar"
},
{
"path": "docs/Favicon/resources/viewer.css",
"chars": 19636,
"preview": "/*@import \"https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.7.2/animate.css\";*/\n\n/* Tooltip container */\n.tooltip {\n"
},
{
"path": "docs/Favicon/viewer/AbstractViewer.js",
"chars": 1149,
"preview": "\nclass AbstractViewer {\n constructor() {\n // common viewer settings, can be changed in child constructors\n "
},
{
"path": "docs/Favicon/viewer/CommentsViewer.js",
"chars": 4014,
"preview": "\nlet commentsViewer = null;\n\nclass CommentsViewer extends AbstractViewer {\n constructor() {\n super()\n\n "
},
{
"path": "docs/Favicon/viewer/GalleryViewer.js",
"chars": 15769,
"preview": "const GALLERY_TOP_MARGIN = 80\nconst GALLERY_LEFTRIGH_MARGIN = 40\n\n\nclass GalleryViewerMapLink {\n constructor(index, l"
},
{
"path": "docs/Favicon/viewer/LayersData.js",
"chars": 1302,
"preview": "const SYMBOLS_DICT = {};\nconst TOKENS_DICT = {};var layersData = [{\"ii\":0,\"nextLinkIndex\":0,\"artboardType\":0,\"isModal\":f"
},
{
"path": "docs/Favicon/viewer/SymbolViewer.js",
"chars": 30922,
"preview": "const ELEMENTINSPECTOR_LINUX_FONT_SIZES = {\n \"8px\": \"6px\",\n \"10px\": \"7px\",\n \"11px\": \"8px\",\n \"12px\": \"9px\",\n "
},
{
"path": "docs/Favicon/viewer/VersionViewer.js",
"chars": 6923,
"preview": "function getVersionInfoRequest() {\n var resp = this\n if (resp.readyState == resp.DONE) {\n if (resp.status ="
},
{
"path": "docs/Favicon/viewer/story.js",
"chars": 1049,
"preview": "var story = {\n \"docName\": \"favicon\",\n \"docPath\": \"P_P_P\",\n \"docVersion\": \"V_V_V\",\n \"authorName\": \"V_V_N\",\n \"authorEmail\""
},
{
"path": "docs/Favicon/viewer/viewer-page.js",
"chars": 34864,
"preview": "\n///\nconst Constants = {\n ARTBOARD_OVERLAY_PIN_HOTSPOT: 0,\n ARTBOARD_OVERLAY_PIN_PAGE: 1,\n //\n ARTBOARD_OVER"
},
{
"path": "docs/Favicon/viewer/viewer.js",
"chars": 38314,
"preview": "\n// =============================== PRELOAD IMAGES =========================\nvar pagerLoadingTotal = 0\n\nfunction getQuer"
},
{
"path": "docs/FixedLayers/index.html",
"chars": 38651,
"preview": "<!DOCTYPE html>\n<html>\n<head>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n<meta name=\"generator\""
},
{
"path": "docs/FixedLayers/resources/animations.css",
"chars": 1919,
"preview": "@keyframes fadeIn {\n from {\n opacity: 0;\n }\n to {\n opacity: 1;\n }\n}\n@keyframes fadeOut {\n from {\n opacity:"
},
{
"path": "docs/FixedLayers/resources/cb-modern-ui.css",
"chars": 1919,
"preview": "@keyframes fadeIn {\n from {\n opacity: 0;\n }\n to {\n opacity: 1;\n }\n}\n@keyframes fadeOut {\n from {\n opacity:"
},
{
"path": "docs/FixedLayers/resources/jquery.hotkeys.js",
"chars": 5019,
"preview": "/*jslint browser: true*/\n/*jslint jquery: true*/\n\n/*\n * jQuery Hotkeys Plugin\n * Copyright 2010, John Resig\n * Dual lice"
},
{
"path": "docs/FixedLayers/resources/ux1-ui.css",
"chars": 1981,
"preview": "/* MOD FOR VARIANT B WAS @color-background-primary*/\n@keyframes fadeIn {\n from {\n opacity: 0;\n }\n to {\n opacity"
},
{
"path": "docs/FixedLayers/resources/viewer-center.css",
"chars": 472,
"preview": "#container, #content {\n\ttop: 0;\n\tleft: 0;\n\tright: 0;\n\tbottom: 0;\n\theight: 100vh;\n\tdisplay: grid;\n\tmargin-left: auto;\n\tma"
},
{
"path": "docs/FixedLayers/resources/viewer-fonts.css",
"chars": 472,
"preview": "@font-face {\n font-family: 'Font Awesome';\n font-weight:400;\n src: url('fonts/fa-regular-400.woff2') format(\"wo"
},
{
"path": "docs/FixedLayers/resources/viewer-top.css",
"chars": 452,
"preview": "#container{\n\ttext-align: center;\n margin: 0 auto;\t\n overflow: auto;\n}\n\n#content,#map{\n\ttext-align: center;\n mar"
},
{
"path": "docs/FixedLayers/resources/viewer.css",
"chars": 19636,
"preview": "/*@import \"https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.7.2/animate.css\";*/\n\n/* Tooltip container */\n.tooltip {\n"
},
{
"path": "docs/FixedLayers/viewer/AbstractViewer.js",
"chars": 1149,
"preview": "\nclass AbstractViewer {\n constructor() {\n // common viewer settings, can be changed in child constructors\n "
},
{
"path": "docs/FixedLayers/viewer/CommentsViewer.js",
"chars": 4014,
"preview": "\nlet commentsViewer = null;\n\nclass CommentsViewer extends AbstractViewer {\n constructor() {\n super()\n\n "
},
{
"path": "docs/FixedLayers/viewer/GalleryViewer.js",
"chars": 15769,
"preview": "const GALLERY_TOP_MARGIN = 80\nconst GALLERY_LEFTRIGH_MARGIN = 40\n\n\nclass GalleryViewerMapLink {\n constructor(index, l"
},
{
"path": "docs/FixedLayers/viewer/LayersData.js",
"chars": 186168,
"preview": "const SYMBOLS_DICT = {\"ux1-ui\":{\"styles\":{\"Controls/Buttons/BorderButton/Primary/Text\":{\"tokens\":[[\"color\",\"@color-prima"
},
{
"path": "docs/FixedLayers/viewer/SymbolViewer.js",
"chars": 30922,
"preview": "const ELEMENTINSPECTOR_LINUX_FONT_SIZES = {\n \"8px\": \"6px\",\n \"10px\": \"7px\",\n \"11px\": \"8px\",\n \"12px\": \"9px\",\n "
},
{
"path": "docs/FixedLayers/viewer/VersionViewer.js",
"chars": 6923,
"preview": "function getVersionInfoRequest() {\n var resp = this\n if (resp.readyState == resp.DONE) {\n if (resp.status ="
},
{
"path": "docs/FixedLayers/viewer/story.js",
"chars": 2851,
"preview": "var story = {\n \"docName\": \"fixedlayers\",\n \"docPath\": \"P_P_P\",\n \"docVersion\": \"V_V_V\",\n \"authorName\": \"V_V_N\",\n \"authorEm"
},
{
"path": "docs/FixedLayers/viewer/viewer-page.js",
"chars": 34864,
"preview": "\n///\nconst Constants = {\n ARTBOARD_OVERLAY_PIN_HOTSPOT: 0,\n ARTBOARD_OVERLAY_PIN_PAGE: 1,\n //\n ARTBOARD_OVER"
},
{
"path": "docs/FixedLayers/viewer/viewer.js",
"chars": 38314,
"preview": "\n// =============================== PRELOAD IMAGES =========================\nvar pagerLoadingTotal = 0\n\nfunction getQuer"
},
{
"path": "docs/Tokens/Lib/_pt-assets/UIKit/inspector.json",
"chars": 661,
"preview": "{\"styles\":{\"Controls/Buttons/Primary/Background\":{\"tokens\":[[\"background-color\",\"@btn-background-color\"],[\"border-color\""
},
{
"path": "docs/Tokens/Lib/_pt-assets/UIKit/vars.json",
"chars": 197,
"preview": "{\"@brand-color\":\"blue\",\"@btn-background-color\":\"#6666ff\",\"@btn-border-color\":\"#000099\",\"@btn-border-width\":\"2px\",\"@btn-b"
},
{
"path": "docs/Tokens/Lib/_pt-assets/UIKit/vars.scss",
"chars": 182,
"preview": "$brand-color: blue;\n$btn-background-color: #6666ff;\n$btn-border-color: #000099;\n$btn-border-radius: 4px;\n$btn-border-wid"
},
{
"path": "docs/Tokens/Lib/design-tokens.less",
"chars": 219,
"preview": "@brand-color: blue;\n\n@btn-background-color: lighten(@brand-color, 20%);\n@btn-border-color: darken(@brand-color, 20%);\n@b"
},
{
"path": "docs/Tokens/Lib/sketch-styles.less",
"chars": 465,
"preview": "@import \"design-tokens.less\";\n\n.Controls {\n .Buttons {\n .Primary {\n .Background {\n b"
},
{
"path": "examples/Link-ModalArtboard/Link-ModalArtboard-vars.json",
"chars": 9135,
"preview": "{\n \"space-xxx-large\": \"60px\",\n \"space-xx-large\": \"50px\",\n \"space-x-large\": \"30px\",\n \"space-large\": \"25px\",\n "
},
{
"path": "examples/Link-ModalArtboard/Link-ModalArtboard-viewer.css",
"chars": 1972,
"preview": "/* MOD FOR VARIANT B WAS @color-background-primary*/\n@keyframes fadeIn {\n from {\n opacity: 0;\n }\n to {\n opacity"
},
{
"path": "server_tools/announce.php",
"chars": 11299,
"preview": "<?php\n\n/*\n To announce published mockups in Telegram channel you need:\n 1. Create a Telegram public channel\n 2."
},
{
"path": "server_tools/config.json",
"chars": 124,
"preview": "{\n \"telegram-bot-token\": \"XXXXXX:XXXXXXXXXXXXXXXXXXXXX\",\n \"telegram-channel-id\": \"@my_channel\",\n \"secret-key\": "
},
{
"path": "server_tools/folder_info.php",
"chars": 153,
"preview": "<?php\n\ninclude_once \"lib/FolderInfo.php\";\n\n$parser = new FolderInfo();\nif($parser->collectInfo()){\n $parser->dump_jso"
},
{
"path": "server_tools/journal.php",
"chars": 690,
"preview": "<?php\n\nclass Worker{\n public $ref = \"\";\n public $baseurl = \"\";\n public $data = [];\n\n public function __const"
},
{
"path": "server_tools/lib/FolderInfo.php",
"chars": 4634,
"preview": "<?php\n\nfunction folderInfoFilter($var){\n return (int)$var>0;\n}\n\n// result [string(starting from \"/\"),pos,integer]\nfun"
},
{
"path": "server_tools/version_info.php",
"chars": 4812,
"preview": "<?php\n\nfunction folderInfoFilter($var){\n return (int)$var>0;\n}\n\n// result [string(starting from \"/\"),pos,integer]\nfun"
}
]
// ... and 46 more files (download for full content)
About this extraction
This page contains the full source code of the ingrammicro/puzzle-publisher GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 162 files (17.9 MB), approximately 341.1k tokens, and a symbol index with 1099 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.