├── .codeclimate.yml
├── .eslintignore
├── .github
└── ISSUE_TEMPLATE
├── .gitignore
├── .npmignore
├── .travis.yml
├── Gruntfile.js
├── LICENSE
├── README.md
├── RELEASE.md
├── bin
├── copy-to-piskel-web.js
└── copy-to-piskel-website.js
├── cli
├── README.md
├── export-png.js
├── index.js
└── piskel-export.js
├── karma.conf.js
├── misc
├── desktop
│ ├── Info.plist
│ ├── logo.ico
│ ├── nw.icns
│ └── package-piskel.evb
├── gif-tests
│ ├── low-colors-no-transparency.piskel
│ ├── low-colors-with-transparency.piskel
│ ├── too-many-colors-no-transparency.piskel
│ └── too-many-colors-with-transparency.piskel
├── icons
│ ├── SVG
│ │ ├── eye.svg
│ │ ├── flip.svg
│ │ ├── import-icon.svg
│ │ ├── lasso.svg
│ │ ├── layers.svg
│ │ ├── onion.svg
│ │ ├── swap-arrow-square.svg
│ │ └── swap-arrow.svg
│ ├── cloud_export.png
│ ├── cursors-resources.jpg
│ ├── eraser.ai
│ ├── icons.png
│ ├── import-icon.png
│ ├── mirror.ai
│ ├── noun-project
│ │ ├── sheep
│ │ │ ├── icon_8389.png
│ │ │ ├── icon_8389.svg
│ │ │ └── license.txt
│ │ └── undo-kyle_levi_fox
│ │ │ ├── icon_10033.svg
│ │ │ └── license.txt
│ ├── rectangle.ai
│ ├── rectangle_selection.ai
│ ├── source
│ │ ├── common-backup.svg
│ │ ├── common-keyboard-gold.svg
│ │ ├── common-swapcolors-arrow-grey.svg
│ │ ├── common-warning-red.svg
│ │ ├── file-icon-base.svg
│ │ ├── frame-dragndrop-white.svg
│ │ ├── frame-duplicate-white-base.pdn
│ │ ├── frame-duplicate-white-base@2x.pdn
│ │ ├── frame-duplicate-white.svg
│ │ ├── frame-plus-white.svg
│ │ ├── frame-recyclebin-white.svg
│ │ ├── minimap-popup-preview-arrow.svg
│ │ ├── settings-export-white.svg
│ │ ├── settings-gear-white.svg
│ │ ├── settings-open-folder-white.svg
│ │ ├── settings-resize-white.svg
│ │ ├── settings-save-white.svg
│ │ ├── tool-center.svg
│ │ ├── tool-circle.svg
│ │ ├── tool-clone.svg
│ │ ├── tool-colorpicker.svg
│ │ ├── tool-colorswap.svg
│ │ ├── tool-dithering.svg
│ │ ├── tool-eraser.svg
│ │ ├── tool-flip.svg
│ │ ├── tool-lasso-select.svg
│ │ ├── tool-lighten.svg
│ │ ├── tool-move.svg
│ │ ├── tool-paint-bucket.svg
│ │ ├── tool-pen.svg
│ │ ├── tool-rectangle-select.svg
│ │ ├── tool-rectangle.svg
│ │ ├── tool-rotate.svg
│ │ ├── tool-shape-select.svg
│ │ ├── tool-stroke.svg
│ │ └── tool-vertical-mirror-pen.svg
│ ├── stroke.ai
│ ├── swap-arrow-small.png
│ ├── swap-arrow-square-small-grey.png
│ ├── swap-arrow-square-small.png
│ └── swap-colors-tests
│ │ ├── swap-colors-square.png
│ │ ├── swap-colors-twirl-2.png
│ │ ├── swap-colors-twirl-3.png
│ │ ├── swap-colors-twirl.png
│ │ └── swap.png
├── proto-ui-1
│ ├── img
│ │ ├── dragndrop-dark.png
│ │ ├── dragndrop.png
│ │ ├── eyedropper-dark.png
│ │ ├── eyedropper-icon.png
│ │ ├── garbage-dark.png
│ │ ├── garbage.png
│ │ ├── magicwand-icon-dark.png
│ │ ├── magicwand-icon.png
│ │ ├── paintbucket-icon-dark.png
│ │ ├── paintbucket-icon.png
│ │ ├── pen-icon-dark.png
│ │ ├── pen-icon.png
│ │ ├── preview-state-1.png
│ │ ├── preview.gif
│ │ └── transparent_background.png
│ ├── index.html
│ ├── jquery-1.8.0.js
│ ├── main.css
│ └── main.js
├── scripts
│ ├── build-mac-application.txt
│ ├── package-mac-application.cmd
│ ├── package-windows-executable.cmd
│ └── piskel-root
└── selenium-ide
│ ├── change-colors.html
│ ├── change-size.html
│ ├── select-tools.html
│ └── user-preferences.html
├── package-lock.json
├── package.json
├── pre-commit
├── src
├── css
│ ├── animations.css
│ ├── bootstrap
│ │ ├── bootstrap-tooltip-custom.css
│ │ ├── bootstrap.css
│ │ └── readme.txt
│ ├── color-picker-slider.css
│ ├── dialogs-browse-backups.css
│ ├── dialogs-browse-local.css
│ ├── dialogs-cheatsheet.css
│ ├── dialogs-create-palette.css
│ ├── dialogs-import.css
│ ├── dialogs-performance-info.css
│ ├── dialogs-unsupported-browser.css
│ ├── dialogs.css
│ ├── font-icon.css
│ ├── fonts
│ │ ├── icomoon.eot
│ │ ├── icomoon.svg
│ │ ├── icomoon.ttf
│ │ ├── icomoon.woff
│ │ ├── piskel.eot
│ │ ├── piskel.svg
│ │ ├── piskel.ttf
│ │ └── piskel.woff
│ ├── forms.css
│ ├── frames-list.css
│ ├── layout.css
│ ├── minimap.css
│ ├── notifications.css
│ ├── reset.css
│ ├── settings-application.css
│ ├── settings-export.css
│ ├── settings-import.css
│ ├── settings-resize.css
│ ├── settings-save.css
│ ├── settings.css
│ ├── spectrum
│ │ ├── spectrum-overrides.css
│ │ └── spectrum.css
│ ├── style.css
│ ├── toolbox-animated-preview.css
│ ├── toolbox-layers-list.css
│ ├── toolbox-palettes-list.css
│ ├── toolbox.css
│ ├── tools.css
│ ├── transformations.css
│ ├── variables.css
│ ├── widgets-anchor.css
│ ├── widgets-frame-picker.css
│ ├── widgets-size-picker.css
│ ├── widgets-tabs.css
│ └── widgets-wizard.css
├── img
│ ├── canvas-backgrounds
│ │ ├── canvas-background-light.png
│ │ ├── canvas-background-lowcontrast-dark.png
│ │ ├── canvas-background-lowcontrast-medium.png
│ │ └── canvas-background-medium.png
│ ├── cursors
│ │ ├── circle.png
│ │ ├── color-palette.png
│ │ ├── dither.png
│ │ ├── dropper.png
│ │ ├── eraser.png
│ │ ├── hand.png
│ │ ├── lighten.png
│ │ ├── mirror-pen.png
│ │ ├── paint-bucket.png
│ │ ├── pen.png
│ │ ├── rectangle.png
│ │ ├── select.png
│ │ ├── stroke.png
│ │ ├── vertical-mirror-pen.png
│ │ └── wand.png
│ ├── favicon.png
│ ├── icons
│ │ ├── common
│ │ │ ├── common-backup-white.png
│ │ │ ├── common-backup-white@2x.png
│ │ │ ├── common-keyboard-gold.png
│ │ │ ├── common-keyboard-gold@2x.png
│ │ │ ├── common-swapcolors-arrow-grey.png
│ │ │ ├── common-swapcolors-arrow-grey@2x.png
│ │ │ ├── common-warning-red.png
│ │ │ └── common-warning-red@2x.png
│ │ ├── frame
│ │ │ ├── frame-dragndrop-white.png
│ │ │ ├── frame-dragndrop-white@2x.png
│ │ │ ├── frame-duplicate-white.png
│ │ │ ├── frame-duplicate-white@2x.png
│ │ │ ├── frame-plus-white.png
│ │ │ ├── frame-plus-white@2x.png
│ │ │ ├── frame-recyclebin-white.png
│ │ │ └── frame-recyclebin-white@2x.png
│ │ ├── minimap
│ │ │ ├── minimap-grid-gold.png
│ │ │ ├── minimap-grid-gold@2x.png
│ │ │ ├── minimap-grid-white.png
│ │ │ ├── minimap-grid-white@2x.png
│ │ │ ├── minimap-popup-preview-arrow-gold.png
│ │ │ ├── minimap-popup-preview-arrow-gold@2x.png
│ │ │ ├── minimap-popup-preview-arrow-white.png
│ │ │ └── minimap-popup-preview-arrow-white@2x.png
│ │ ├── settings
│ │ │ ├── settings-export-white.png
│ │ │ ├── settings-export-white@2x.png
│ │ │ ├── settings-gear-white.png
│ │ │ ├── settings-gear-white@2x.png
│ │ │ ├── settings-open-folder-white.png
│ │ │ ├── settings-open-folder-white@2x.png
│ │ │ ├── settings-resize-white.png
│ │ │ ├── settings-resize-white@2x.png
│ │ │ ├── settings-save-white.png
│ │ │ └── settings-save-white@2x.png
│ │ ├── tools
│ │ │ ├── tool-circle.png
│ │ │ ├── tool-circle@2x.png
│ │ │ ├── tool-colorpicker.png
│ │ │ ├── tool-colorpicker@2x.png
│ │ │ ├── tool-colorswap.png
│ │ │ ├── tool-colorswap@2x.png
│ │ │ ├── tool-dithering.png
│ │ │ ├── tool-dithering@2x.png
│ │ │ ├── tool-eraser.png
│ │ │ ├── tool-eraser@2x.png
│ │ │ ├── tool-lasso-select.png
│ │ │ ├── tool-lasso-select@2x.png
│ │ │ ├── tool-lighten.png
│ │ │ ├── tool-lighten@2x.png
│ │ │ ├── tool-move.png
│ │ │ ├── tool-move@2x.png
│ │ │ ├── tool-paint-bucket.png
│ │ │ ├── tool-paint-bucket@2x.png
│ │ │ ├── tool-pen.png
│ │ │ ├── tool-pen@2x.png
│ │ │ ├── tool-rectangle-select.png
│ │ │ ├── tool-rectangle-select@2x.png
│ │ │ ├── tool-rectangle.png
│ │ │ ├── tool-rectangle@2x.png
│ │ │ ├── tool-shape-select.png
│ │ │ ├── tool-shape-select@2x.png
│ │ │ ├── tool-stroke.png
│ │ │ ├── tool-stroke@2x.png
│ │ │ ├── tool-vertical-mirror-pen.png
│ │ │ └── tool-vertical-mirror-pen@2x.png
│ │ └── transform
│ │ │ ├── tool-center.png
│ │ │ ├── tool-center@2x.png
│ │ │ ├── tool-clone.png
│ │ │ ├── tool-clone@2x.png
│ │ │ ├── tool-crop.png
│ │ │ ├── tool-crop@2x.png
│ │ │ ├── tool-flip.png
│ │ │ ├── tool-flip@2x.png
│ │ │ ├── tool-rotate.png
│ │ │ └── tool-rotate@2x.png
│ └── unused
│ │ ├── circle-dark.png
│ │ ├── eraser-dark.png
│ │ ├── eyedropper-dark.png
│ │ ├── gallery.png
│ │ ├── hand-dark.png
│ │ ├── lasso-dark.png
│ │ ├── magicwand-dark.png
│ │ ├── paintbucket-dark.png
│ │ ├── pen-dark.png
│ │ ├── rectangle-dark.png
│ │ └── rectangle_selection-dark.png
├── index.html
├── js
│ ├── .eslintrc
│ ├── Constants.js
│ ├── Events.js
│ ├── app.js
│ ├── controller
│ │ ├── CanvasBackgroundController.js
│ │ ├── CursorCoordinatesController.js
│ │ ├── DrawingController.js
│ │ ├── FramesListController.js
│ │ ├── HeaderController.js
│ │ ├── LayersListController.js
│ │ ├── MinimapController.js
│ │ ├── NotificationController.js
│ │ ├── PaletteController.js
│ │ ├── PalettesListController.js
│ │ ├── PenSizeController.js
│ │ ├── ProgressBarController.js
│ │ ├── ToolController.js
│ │ ├── TransformationsController.js
│ │ ├── UserWarningController.js
│ │ ├── dialogs
│ │ │ ├── AbstractDialogController.js
│ │ │ ├── BrowseLocalController.js
│ │ │ ├── CheatsheetController.js
│ │ │ ├── CreatePaletteController.js
│ │ │ ├── DialogsController.js
│ │ │ ├── PerformanceInfoController.js
│ │ │ ├── UnsupportedBrowserController.js
│ │ │ ├── backups
│ │ │ │ ├── BrowseBackups.js
│ │ │ │ └── steps
│ │ │ │ │ ├── SelectSession.js
│ │ │ │ │ └── SessionDetails.js
│ │ │ └── importwizard
│ │ │ │ ├── ImportWizard.js
│ │ │ │ └── steps
│ │ │ │ ├── AbstractImportStep.js
│ │ │ │ ├── AdjustSize.js
│ │ │ │ ├── ImageImport.js
│ │ │ │ ├── InsertLocation.js
│ │ │ │ └── SelectMode.js
│ │ ├── drawing
│ │ │ └── DragHandler.js
│ │ ├── piskel
│ │ │ ├── PiskelController.js
│ │ │ └── PublicPiskelController.js
│ │ ├── preview
│ │ │ ├── PopupPreviewController.js
│ │ │ ├── PreviewActionsController.js
│ │ │ └── PreviewController.js
│ │ └── settings
│ │ │ ├── AbstractSettingController.js
│ │ │ ├── ImportController.js
│ │ │ ├── PreferencesController.js
│ │ │ ├── SaveController.js
│ │ │ ├── SettingsController.js
│ │ │ ├── exportimage
│ │ │ ├── ExportController.js
│ │ │ ├── GifExportController.js
│ │ │ ├── MiscExportController.js
│ │ │ ├── PngExportController.js
│ │ │ └── ZipExportController.js
│ │ │ ├── preferences
│ │ │ ├── GridPreferencesController.js
│ │ │ ├── MiscPreferencesController.js
│ │ │ └── TilePreferencesController.js
│ │ │ └── resize
│ │ │ ├── DefaultSizeController.js
│ │ │ └── ResizeController.js
│ ├── database
│ │ ├── BackupDatabase.js
│ │ ├── PiskelDatabase.js
│ │ └── migrate
│ │ │ └── MigrateLocalStorageToIndexedDb.js
│ ├── devtools
│ │ ├── DrawingTestPlayer.js
│ │ ├── DrawingTestRecorder.js
│ │ ├── DrawingTestRunner.js
│ │ ├── DrawingTestSuiteController.js
│ │ ├── DrawingTestSuiteRunner.js
│ │ ├── MouseEvent.js
│ │ ├── TestRecordController.js
│ │ ├── init.js
│ │ └── lib
│ │ │ └── Blob.js
│ ├── lib
│ │ ├── .DS_Store
│ │ ├── bootstrap
│ │ │ ├── bootstrap.js
│ │ │ └── readme.txt
│ │ ├── gif
│ │ │ ├── gif.ie.worker.js
│ │ │ ├── gif.js
│ │ │ ├── gif.worker.js
│ │ │ └── libgif.js
│ │ ├── iframeLoader-0.1.0.js
│ │ ├── jquery-1.8.0.js
│ │ ├── jquery-ui-1.10.3.custom.js
│ │ ├── jszip
│ │ │ └── jszip.min.js
│ │ ├── pubsub.js
│ │ ├── q.js
│ │ ├── scrollifneeded
│ │ │ └── scrollifneeded.js
│ │ ├── smoothscroll
│ │ │ ├── LICENSE.txt
│ │ │ └── smoothscroll.js
│ │ └── spectrum
│ │ │ └── spectrum.js
│ ├── model
│ │ ├── Frame.js
│ │ ├── Layer.js
│ │ ├── Palette.js
│ │ ├── Piskel.js
│ │ ├── frame
│ │ │ ├── AsyncCachedFrameProcessor.js
│ │ │ ├── CachedFrameProcessor.js
│ │ │ └── RenderedFrame.js
│ │ └── piskel
│ │ │ └── Descriptor.js
│ ├── rendering
│ │ ├── AbstractRenderer.js
│ │ ├── CanvasRenderer.js
│ │ ├── CompositeRenderer.js
│ │ ├── DrawingLoop.js
│ │ ├── FramesheetRenderer.js
│ │ ├── OnionSkinRenderer.js
│ │ ├── PiskelRenderer.js
│ │ ├── frame
│ │ │ ├── BackgroundImageFrameRenderer.js
│ │ │ ├── CachedFrameRenderer.js
│ │ │ └── FrameRenderer.js
│ │ └── layer
│ │ │ └── LayersRenderer.js
│ ├── selection
│ │ ├── BaseSelection.js
│ │ ├── LassoSelection.js
│ │ ├── RectangularSelection.js
│ │ ├── SelectionManager.js
│ │ └── ShapeSelection.js
│ ├── service
│ │ ├── BackupService.js
│ │ ├── BeforeUnloadService.js
│ │ ├── ClipboardService.js
│ │ ├── CurrentColorsService.js
│ │ ├── FileDropperService.js
│ │ ├── HistoryService.js
│ │ ├── ImageUploadService.js
│ │ ├── ImportService.js
│ │ ├── MouseStateService.js
│ │ ├── SavedStatusService.js
│ │ ├── SelectedColorsService.js
│ │ ├── color
│ │ │ └── ColorSorter.js
│ │ ├── keyboard
│ │ │ ├── KeyUtils.js
│ │ │ ├── KeycodeTranslator.js
│ │ │ ├── Shortcut.js
│ │ │ ├── ShortcutService.js
│ │ │ └── Shortcuts.js
│ │ ├── palette
│ │ │ ├── CurrentColorsPalette.js
│ │ │ ├── PaletteGplWriter.js
│ │ │ ├── PaletteImportService.js
│ │ │ ├── PaletteService.js
│ │ │ └── reader
│ │ │ │ ├── AbstractPaletteFileReader.js
│ │ │ │ ├── PaletteGplReader.js
│ │ │ │ ├── PaletteImageReader.js
│ │ │ │ ├── PalettePalReader.js
│ │ │ │ └── PaletteTxtReader.js
│ │ ├── pensize
│ │ │ └── PenSizeService.js
│ │ ├── performance
│ │ │ ├── PerformanceReport.js
│ │ │ └── PerformanceReportService.js
│ │ └── storage
│ │ │ ├── DesktopStorageService.js
│ │ │ ├── FileDownloadStorageService.js
│ │ │ ├── GalleryStorageService.js
│ │ │ ├── IndexedDbStorageService.js
│ │ │ ├── LocalStorageService.js
│ │ │ └── StorageService.js
│ ├── snippets.js
│ ├── tools
│ │ ├── Tool.js
│ │ ├── ToolIconBuilder.js
│ │ ├── ToolsHelper.js
│ │ ├── drawing
│ │ │ ├── BaseTool.js
│ │ │ ├── Circle.js
│ │ │ ├── ColorPicker.js
│ │ │ ├── ColorSwap.js
│ │ │ ├── DitheringTool.js
│ │ │ ├── Eraser.js
│ │ │ ├── Lighten.js
│ │ │ ├── Move.js
│ │ │ ├── PaintBucket.js
│ │ │ ├── Rectangle.js
│ │ │ ├── ShapeTool.js
│ │ │ ├── SimplePen.js
│ │ │ ├── Stroke.js
│ │ │ ├── VerticalMirrorPen.js
│ │ │ └── selection
│ │ │ │ ├── AbstractDragSelect.js
│ │ │ │ ├── BaseSelect.js
│ │ │ │ ├── LassoSelect.js
│ │ │ │ ├── RectangleSelect.js
│ │ │ │ └── ShapeSelect.js
│ │ └── transform
│ │ │ ├── AbstractTransformTool.js
│ │ │ ├── Center.js
│ │ │ ├── Clone.js
│ │ │ ├── Crop.js
│ │ │ ├── Flip.js
│ │ │ ├── Rotate.js
│ │ │ └── TransformUtils.js
│ ├── utils
│ │ ├── Array.js
│ │ ├── Base64.js
│ │ ├── BlobUtils.js
│ │ ├── CanvasUtils.js
│ │ ├── ColorUtils.js
│ │ ├── DateUtils.js
│ │ ├── Dom.js
│ │ ├── Environment.js
│ │ ├── Event.js
│ │ ├── FileUtils.js
│ │ ├── FileUtilsDesktop.js
│ │ ├── FrameUtils.js
│ │ ├── FunctionUtils.js
│ │ ├── ImageResizer.js
│ │ ├── LayerUtils.js
│ │ ├── Math.js
│ │ ├── MergeUtils.js
│ │ ├── PiskelFileUtils.js
│ │ ├── PixelUtils.js
│ │ ├── ResizeUtils.js
│ │ ├── StringUtils.js
│ │ ├── Template.js
│ │ ├── TooltipFormatter.js
│ │ ├── UserAgent.js
│ │ ├── UserSettings.js
│ │ ├── Uuid.js
│ │ ├── WorkerUtils.js
│ │ ├── Xhr.js
│ │ ├── core.js
│ │ └── serialization
│ │ │ ├── Deserializer.js
│ │ │ ├── Serializer.js
│ │ │ ├── arraybuffer
│ │ │ ├── ArrayBufferDeserializer.js
│ │ │ └── ArrayBufferSerializer.js
│ │ │ └── backward
│ │ │ ├── Deserializer_v0.js
│ │ │ └── Deserializer_v1.js
│ ├── widgets
│ │ ├── AnchorWidget.js
│ │ ├── ColorsList.js
│ │ ├── FramePicker.js
│ │ ├── HslRgbColorPicker.js
│ │ ├── SizeInput.js
│ │ ├── SizePicker.js
│ │ ├── SynchronizedInputs.js
│ │ ├── Tabs.js
│ │ └── Wizard.js
│ └── worker
│ │ ├── framecolors
│ │ ├── FrameColors.js
│ │ └── FrameColorsWorker.js
│ │ ├── hash
│ │ ├── Hash.js
│ │ └── HashWorker.js
│ │ └── imageprocessor
│ │ ├── ImageProcessor.js
│ │ └── ImageProcessorWorker.js
├── logo.png
├── piskel-boot.js
├── piskel-script-list.js
├── piskel-style-list.js
└── templates
│ ├── data-uri-export.html
│ ├── debug-header.html
│ ├── dialogs
│ ├── browse-backups.html
│ ├── browse-local.html
│ ├── cheatsheet.html
│ ├── create-palette.html
│ ├── import.html
│ ├── performance-info.html
│ └── unsupported-browser.html
│ ├── drawing-tools.html
│ ├── frames-list.html
│ ├── layers-list.html
│ ├── misc-templates.html
│ ├── palettes-list.html
│ ├── popup-preview.html
│ ├── preview.html
│ ├── settings.html
│ ├── settings
│ ├── export.html
│ ├── export
│ │ ├── gif.html
│ │ ├── misc.html
│ │ ├── png.html
│ │ └── zip.html
│ ├── import.html
│ ├── preferences.html
│ ├── preferences
│ │ ├── grid.html
│ │ ├── misc.html
│ │ └── tile.html
│ ├── resize.html
│ └── save.html
│ └── transformations.html
└── test
├── casperjs
├── DrawingTest.js
└── integration
│ ├── IntegrationSuite.js
│ ├── include.js
│ ├── palettes
│ └── test-tiny-palettes.js
│ ├── preview
│ └── test-toggle-grid.js
│ └── settings
│ ├── test-export-gif-scale.js
│ ├── test-export-gif-simple.js
│ ├── test-export-gif.js
│ ├── test-export-png-scale.js
│ ├── test-export-png.js
│ ├── test-import-image-empty.js
│ ├── test-import-image-twice.js
│ ├── test-import-image.js
│ ├── test-preferences-main.js
│ ├── test-resize-complete.js
│ ├── test-resize-content-complete.js
│ ├── test-resize-default-size.js
│ ├── test-resize-input-synchronization.js
│ ├── test-resize-origin.js
│ ├── test-resize.js
│ └── test-settings-open-panels-on-click.js
├── drawing
├── DrawingTests.browser.js
├── DrawingTests.casper.js
├── DrawingTests.pensize.js
├── DrawingTests.perf.js
└── tests
│ ├── bucket.drawing.json
│ ├── color.picker.2.json
│ ├── color.picker.json
│ ├── dithering.basic.json
│ ├── eraser.bucket.json
│ ├── frames.fun.json
│ ├── history.basic.json
│ ├── layers.duplicate.json
│ ├── layers.fun.json
│ ├── layers.merge.json
│ ├── layers.top.bottom.json
│ ├── lighten.darken.json
│ ├── move-alllayers-allframes.json
│ ├── move.json
│ ├── pen.drawing.json
│ ├── pen.mirror.pensize.json
│ ├── pen.secondary.color.json
│ ├── pensize.circle.basic.json
│ ├── pensize.circle.undo.json
│ ├── pensize.eraser.basic.json
│ ├── pensize.eraser.undo.json
│ ├── pensize.pen.basic.json
│ ├── pensize.pen.undo.json
│ ├── pensize.rectangle.basic.json
│ ├── pensize.rectangle.undo.json
│ ├── pensize.stroke.basic.json
│ ├── pensize.stroke.undo.json
│ ├── perf.1024.pen.bucket.json
│ ├── perf.512.layers.undo.json
│ ├── selection.lasso.json
│ ├── selection.rectangular.json
│ ├── squares.circles.json
│ ├── stroke.json
│ ├── swapcolor.alllayers.allframes.twice.undo.once.json
│ ├── swapcolor.twice.undo.once.json
│ ├── transform.center.json
│ ├── transform.clone.once.json
│ ├── transform.clone.twice.undo.once.json
│ ├── transform.crop.json
│ ├── transform.crop.selection.json
│ ├── transform.flip.once.alt.json
│ ├── transform.flip.thrice.undo.all.redo.all.json
│ ├── transform.flip.twice.undo.once.json
│ ├── transform.rotate.alt.twice.undo.once.json
│ ├── transform.rotate.once.alt.json
│ ├── transform.rotate.twice.undo.once.json
│ └── verticalpen.drawing.json
└── js
├── database
├── BackupDatabaseTest.js
└── PiskelDatabaseTest.js
├── model
├── LayerTest.js
└── PaletteTest.js
├── rendering
├── CanvasRendererTest.js
├── FramesheetRendererTest.js
└── OnionSkinRendererTest.js
├── selection
└── SelectionManagerTest.js
├── service
├── BackupServiceTest.js
├── HistoryServiceTest.js
├── SelectedColorsServiceTest.js
├── keyboard
│ └── ShortcutServiceTest.js
├── palette
│ └── PaletteServiceTest.js
├── pensize
│ └── PenSizeServiceTest.js
└── storage
│ └── StorageServiceTest.js
├── testutils
└── TestUtils.js
├── tools
└── transform
│ └── TransformUtilsTest.js
└── utils
├── ArrayTest.js
├── ColorUtilsTest.js
├── CoreTest.js
├── FrameUtilsTest.js
├── FrameUtilsTest_add_image.js
├── LayerUtilsTest.js
├── MergeUtilsTest.js
├── PixelUtilsTest.js
├── PixelUtilsTest_visit_connected.js
├── UuidTest.js
└── serialization
├── Deserializer_v0Test.js
├── Deserializer_v1Test.js
└── SerializerTest.js
/.codeclimate.yml:
--------------------------------------------------------------------------------
1 | engines:
2 | csslint:
3 | enabled: true
4 | duplication:
5 | enabled: true
6 | config:
7 | languages:
8 | - javascript
9 | eslint:
10 | enabled: true
11 | checks:
12 | wrap-iife:
13 | enabled: false
14 | fixme:
15 | enabled: true
16 | ratings:
17 | paths:
18 | - "**.css"
19 | - "**.js"
20 | exclude_paths:
21 | - .github/
22 | - bin/
23 | - misc/
24 | - src/js/lib/
25 | - test/
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | # Exclude libs.
2 | **/lib/**/*.js
3 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE:
--------------------------------------------------------------------------------
1 | Bug description
2 |
3 | (this template is intended for bug reports, erase it when requesting features/enhancements)
4 |
5 | ### Steps to reproduce the bug
6 | 1. go to piskelapp.com
7 | 2. select a tool
8 | 3. ...
9 |
10 | ### Environment details
11 | * operating system:
12 | * browser (or offline application version):
13 |
14 | ### Sprite details
15 | * number of frames:
16 | * sprite resolution:
17 | * session duration:
18 |
19 | Feel free to include a screenshot of the bug, a .piskel file of the sprite or anything else that can help us investigate.
20 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # mac artifacts
2 | *.DS_Store
3 |
4 | # nodejs local installs
5 | node_modules
6 | npm-debug.log
7 |
8 | # node webkit cache
9 | cache
10 |
11 | # sublime text stuff (the -project should actually be shared, but then we'd have to share the same disk location)
12 | *.sublime-project
13 | *.sublime-workspace
14 |
15 | # netbeans project folder
16 | nbproject
17 |
18 | # vscode workspace folder
19 | .vscode
20 |
21 | # git stackdumps
22 | *.stackdump
23 |
24 | # diffs
25 | diff.txt
26 |
27 | # build destination
28 | dest
29 | build/closure/closure_compiled_binary.js
30 | out
31 |
32 | # spriting artifacts
33 | src/img/icons.png
34 | src/img/icons@2x.png
35 | src/css/icons.css
36 |
37 | # plato report directory
38 | report
39 |
40 | # marked as private
41 | *.private.*
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | test/
3 |
4 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "7.4.0"
4 | before_install:
5 | - npm update -g npm
6 | - npm install -g grunt-cli
7 | before_script:
8 | - phantomjs --version
9 | - casperjs --version
10 | sudo: false
11 |
--------------------------------------------------------------------------------
/cli/README.md:
--------------------------------------------------------------------------------
1 | # Piskel CLI
2 |
3 | Wraps the Piskel pixel editing application to enable similar export options via the command line.
4 |
5 | ## Installation
6 |
7 | Option 1: Globally install Piskel
8 | ```
9 | npm install -g https://github.com/piskelapp/piskel/tarball/master
10 | ```
11 |
12 | Option 2: Clone and install Piskel normally and then run npm link inside the installation root
13 |
14 | ## Usage
15 |
16 | **Export provided .piskel file as a png sprite sheet using app defaults**
17 | ```
18 | piskel-cli snow-monster.piskel
19 | ```
20 |
21 | **Export scaled sprite sheet**
22 | ```
23 | piskel-cli snow-monster.piskel --scale 5
24 | ```
25 |
26 | **Export scaled to specific (single frame) width value**
27 | ```
28 | piskel-cli snow-monster.piskel --scaledWidth 435
29 | ```
30 |
31 | **Export scaled to specific (single frame) height value**
32 | ```
33 | piskel-cli snow-monster.piskel --scaledHeight 435
34 | ```
35 |
36 | **Export sprite sheet as a single column**
37 | ```
38 | piskel-cli snow-monster.piskel --columns 1
39 | ```
40 |
41 | **Export sprite sheet as a single row**
42 | ```
43 | piskel-cli snow-monster.piskel --rows 1
44 | ```
45 |
46 | **Export a single frame (0 is first frame)**
47 | ```
48 | piskel-cli snow-monster.piskel --frame 3
49 | ```
50 |
51 | **Export a second file containing the data-uri for the exported png**
52 | ```
53 | piskel-cli snow-monster.piskel --dataUri
54 | ```
55 |
56 | **Export cropped**
57 | ```
58 | piskel-cli snow-monster.piskel --crop
59 | ```
60 |
61 | **Custom output path and/or filename**
62 | ```
63 | piskel-cli snow-monster.piskel --dest ./output-folder/snah-monstah.png
64 | ```
--------------------------------------------------------------------------------
/cli/piskel-export.js:
--------------------------------------------------------------------------------
1 | // PhantomJS system
2 | const system = require('system');
3 |
4 | // Exporter
5 | const exporter = require('./export-png');
6 |
7 | // Get passed args
8 | const args = system.args;
9 |
10 | // Parse input piskel file and options
11 | const piskelFile = JSON.parse(args[1]);
12 | const options = JSON.parse(args[2]);
13 |
14 | // Create page w/ canvas
15 | const page = require('webpage').create();
16 |
17 | page.content = '
';
18 |
19 | // Inject Piskel JS
20 | page.injectJs(options.piskelAppJsPath);
21 |
22 | // Listen for page console logs
23 | page.onConsoleMessage = function (msg) {
24 | console.log(msg);
25 | };
26 |
27 | // Run page logic
28 | page.evaluate(function (piskelFile, options, onPageEvaluate) {
29 | // Zero out default body margin
30 | document.body.style.margin = 0;
31 |
32 | // Deserialize piskel file and run exporter's page evaluate task
33 | pskl.utils.serialization.Deserializer.deserialize(piskelFile, function (piskel) {
34 | onPageEvaluate(window, options, piskel);
35 | });
36 | }, piskelFile, options, exporter.onPageEvaluate);
37 |
38 | // Wait for page to trigger exit
39 | page.onCallback = function (data) {
40 | // Run exporter page exit task
41 | exporter.onPageExit(page, options, data);
42 |
43 | // Exit
44 | phantom.exit(0);
45 | };
46 |
--------------------------------------------------------------------------------
/misc/desktop/logo.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/misc/desktop/logo.ico
--------------------------------------------------------------------------------
/misc/desktop/nw.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/misc/desktop/nw.icns
--------------------------------------------------------------------------------
/misc/desktop/package-piskel.evb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/misc/desktop/package-piskel.evb
--------------------------------------------------------------------------------
/misc/gif-tests/low-colors-no-transparency.piskel:
--------------------------------------------------------------------------------
1 | {"modelVersion":2,"piskel":{"name":"low-colors-no-transparency","description":"","fps":12,"height":60,"width":60,"layers":["{\"name\":\"Layer 1\",\"frameCount\":2,\"base64PNG\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHgAAAA8CAYAAACtrX6oAAABZUlEQVR4nO3cwRHCMAxE0RSUGlIF1TBDMTRAi1BCsCVZK/kf/n0n7xr5+FzXd6fe57lVR/YHBxhggAEGGGCA9yj7gwMMMMAAAwwwwHsEcPOWAR/Px22dgM/367bywP+grsbORl2NHQJsgY2GVoONhnYH9sSNQFbGjUB2A46AjYBWho2AdgFegeuFXAXXC9kMvBLXA7kSrgeyCTgD14pcDdeKDDDAergW5Iq4FuQp4GxYC3JV3FlkgAHWxZ1Brow7gwwwwACrFQacDemBXB13FBlggAFWDGCAAQYYYNkABhhggAGWLQRYFXlk/wiwKvLIfoABBlitUGA15NHto8BqyKPbAQZYF3lm9wywCvLMbv6qLII8uxlggPWQLXstwFnIlr3cJokjW7dyXSiM7LGT+2BBaM99XPiLIXtv440OEeioTbyyk4i9YgfvZDUP4OYB3DyAmwdw8wBuHsDNA7h5ADcP4Ob9AHU/4CXfXtXyAAAAAElFTkSuQmCC\"}"],"expanded":false}}
--------------------------------------------------------------------------------
/misc/gif-tests/low-colors-with-transparency.piskel:
--------------------------------------------------------------------------------
1 | {"modelVersion":2,"piskel":{"name":"low-colors-with-transparency","description":"","fps":12,"height":60,"width":60,"layers":["{\"name\":\"Layer 1\",\"frameCount\":2,\"base64PNG\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHgAAAA8CAYAAACtrX6oAAABOUlEQVR4nO3bQWoDQQxEUR3Ix8lpAj5MTmovQsNgEmwYdZVG+h9q3+KtO4KIiIiIiIiIiIhoUN9fj7dr1O3n/ng39xvP9wlqI+xPUHtgn4G9IPQZ2OtBZ+JeADkTtzbyDtjC0Dtg60IrcAshK3DrICtxCyArcf3IDlwjsgPXiwxwY2AnrgHZiatHdsOKkd2wemQ3KsAbc4OKkd2gemQ3JsAAZ+bG1AK7IcXIbkg9shsRYIAzcyMCDHBybkSAAc7MjQgwwMm5EQEGODM3oh44oibyxtyQWtwIgAts570AF9jOe39zg4pwV25QLW4EwO2BI2ogC3PDanFXQ3BXs3AjAG4PHMHPhta4qyG4q1m4qyG4q1m4x5rDvjYH9tgQ3NUs3GPNYV+bA/tXTVH/awYqERERERERERFRVk8BxgukicHldgAAAABJRU5ErkJggg==\"}"],"expanded":false}}
--------------------------------------------------------------------------------
/misc/icons/cloud_export.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/misc/icons/cloud_export.png
--------------------------------------------------------------------------------
/misc/icons/cursors-resources.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/misc/icons/cursors-resources.jpg
--------------------------------------------------------------------------------
/misc/icons/eraser.ai:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/misc/icons/eraser.ai
--------------------------------------------------------------------------------
/misc/icons/icons.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/misc/icons/icons.png
--------------------------------------------------------------------------------
/misc/icons/import-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/misc/icons/import-icon.png
--------------------------------------------------------------------------------
/misc/icons/mirror.ai:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/misc/icons/mirror.ai
--------------------------------------------------------------------------------
/misc/icons/noun-project/sheep/icon_8389.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/misc/icons/noun-project/sheep/icon_8389.png
--------------------------------------------------------------------------------
/misc/icons/noun-project/sheep/license.txt:
--------------------------------------------------------------------------------
1 | Thank you for using The Noun Project. This icon is licensed under Creative
2 | Commons Attribution and must be attributed as:
3 |
4 | Sheep by Unrecognized MJ from The Noun Project
5 |
6 | If you have a Premium Account or have purchased a license for this icon, you
7 | don't need to worry about attribution! We will share the profits from your
8 | purchase with this icon's designer.
9 |
--------------------------------------------------------------------------------
/misc/icons/noun-project/undo-kyle_levi_fox/icon_10033.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/misc/icons/noun-project/undo-kyle_levi_fox/license.txt:
--------------------------------------------------------------------------------
1 | Thank you for using The Noun Project. This icon is licensed under Creative
2 | Commons Attribution and must be attributed as:
3 |
4 | Undo by Kyle Levi Fox from The Noun Project
5 |
6 | If you have a Premium Account or have purchased a license for this icon, you
7 | don't need to worry about attribution! We will share the profits from your
8 | purchase with this icon's designer.
9 |
--------------------------------------------------------------------------------
/misc/icons/rectangle.ai:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/misc/icons/rectangle.ai
--------------------------------------------------------------------------------
/misc/icons/rectangle_selection.ai:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/misc/icons/rectangle_selection.ai
--------------------------------------------------------------------------------
/misc/icons/source/frame-duplicate-white-base.pdn:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/misc/icons/source/frame-duplicate-white-base.pdn
--------------------------------------------------------------------------------
/misc/icons/source/frame-duplicate-white-base@2x.pdn:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/misc/icons/source/frame-duplicate-white-base@2x.pdn
--------------------------------------------------------------------------------
/misc/icons/stroke.ai:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/misc/icons/stroke.ai
--------------------------------------------------------------------------------
/misc/icons/swap-arrow-small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/misc/icons/swap-arrow-small.png
--------------------------------------------------------------------------------
/misc/icons/swap-arrow-square-small-grey.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/misc/icons/swap-arrow-square-small-grey.png
--------------------------------------------------------------------------------
/misc/icons/swap-arrow-square-small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/misc/icons/swap-arrow-square-small.png
--------------------------------------------------------------------------------
/misc/icons/swap-colors-tests/swap-colors-square.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/misc/icons/swap-colors-tests/swap-colors-square.png
--------------------------------------------------------------------------------
/misc/icons/swap-colors-tests/swap-colors-twirl-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/misc/icons/swap-colors-tests/swap-colors-twirl-2.png
--------------------------------------------------------------------------------
/misc/icons/swap-colors-tests/swap-colors-twirl-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/misc/icons/swap-colors-tests/swap-colors-twirl-3.png
--------------------------------------------------------------------------------
/misc/icons/swap-colors-tests/swap-colors-twirl.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/misc/icons/swap-colors-tests/swap-colors-twirl.png
--------------------------------------------------------------------------------
/misc/icons/swap-colors-tests/swap.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/misc/icons/swap-colors-tests/swap.png
--------------------------------------------------------------------------------
/misc/proto-ui-1/img/dragndrop-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/misc/proto-ui-1/img/dragndrop-dark.png
--------------------------------------------------------------------------------
/misc/proto-ui-1/img/dragndrop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/misc/proto-ui-1/img/dragndrop.png
--------------------------------------------------------------------------------
/misc/proto-ui-1/img/eyedropper-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/misc/proto-ui-1/img/eyedropper-dark.png
--------------------------------------------------------------------------------
/misc/proto-ui-1/img/eyedropper-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/misc/proto-ui-1/img/eyedropper-icon.png
--------------------------------------------------------------------------------
/misc/proto-ui-1/img/garbage-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/misc/proto-ui-1/img/garbage-dark.png
--------------------------------------------------------------------------------
/misc/proto-ui-1/img/garbage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/misc/proto-ui-1/img/garbage.png
--------------------------------------------------------------------------------
/misc/proto-ui-1/img/magicwand-icon-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/misc/proto-ui-1/img/magicwand-icon-dark.png
--------------------------------------------------------------------------------
/misc/proto-ui-1/img/magicwand-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/misc/proto-ui-1/img/magicwand-icon.png
--------------------------------------------------------------------------------
/misc/proto-ui-1/img/paintbucket-icon-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/misc/proto-ui-1/img/paintbucket-icon-dark.png
--------------------------------------------------------------------------------
/misc/proto-ui-1/img/paintbucket-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/misc/proto-ui-1/img/paintbucket-icon.png
--------------------------------------------------------------------------------
/misc/proto-ui-1/img/pen-icon-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/misc/proto-ui-1/img/pen-icon-dark.png
--------------------------------------------------------------------------------
/misc/proto-ui-1/img/pen-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/misc/proto-ui-1/img/pen-icon.png
--------------------------------------------------------------------------------
/misc/proto-ui-1/img/preview-state-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/misc/proto-ui-1/img/preview-state-1.png
--------------------------------------------------------------------------------
/misc/proto-ui-1/img/preview.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/misc/proto-ui-1/img/preview.gif
--------------------------------------------------------------------------------
/misc/proto-ui-1/img/transparent_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/misc/proto-ui-1/img/transparent_background.png
--------------------------------------------------------------------------------
/misc/scripts/build-mac-application.txt:
--------------------------------------------------------------------------------
1 | 01 - Run grunt desktop on mac os x
2 | 02 - Retrieve piskel.new.dmg.zip on Dropbox. Unzip it.
3 | 03 - Retrieve nw.icns on Dropbox
4 | 04 - Go to piskel/dest/desktop/releases/mac ; copy piskel.app to your working directory
5 | 05 - Expand piskel.app
6 | 06 - Replace nw.icns by the one from dropbox in Contents/Resources
7 | 07 - Open piskel.new.dmg
8 | 08 - Drag and drop piskel.app in it
9 | 09 - Update readme.txt
10 | 10 - Open disk utility, and open piskel.new.dmg in it
11 | 11 - Convert image, piskel-x.y.z.dmg
12 | 12 - Upload to Dropbox
--------------------------------------------------------------------------------
/misc/scripts/package-mac-application.cmd:
--------------------------------------------------------------------------------
1 | setlocal
2 | @ECHO off
3 |
4 | pushd ..\..
5 | set PISKEL_HOME=%cd%
6 | popd
7 |
8 | set APP_BIN=%PISKEL_HOME%\dest\desktop\cache\mac\0.9.2
9 | set MISC_FOLDER=%PISKEL_HOME%\misc
10 | set RELEASES_FOLDER=%PISKEL_HOME%\dest\desktop\releases
11 | set DEST_FOLDER=%RELEASES_FOLDER%\mac
12 |
13 | ECHO "Building Piskel executable for Windows ..."
14 |
15 | ECHO "Creating release directory ..."
16 | mkdir %DEST_FOLDER%
17 | ECHO "DONE"
18 |
19 | ECHO "Creating application folder ..."
20 | mkdir "%DEST_FOLDER%\piskel.app"
21 | ECHO "DONE"
22 |
23 | ECHO "Unzip application ..."
24 | mkdir "%APP_BIN%\node-webkit-unzipped"
25 | 7za x "%APP_BIN%\node-webkit-v0.9.2-osx-ia32.zip" -o"%APP_BIN%\node-webkit-unzipped"
26 | ECHO "DONE"
27 |
28 | pause
29 |
30 | ECHO "Copy application ..."
31 | xcopy "%APP_BIN%\node-webkit-unzipped\node-webkit.app" "%DEST_FOLDER%\piskel.app" /E
32 | :: xcopy "%APP_BIN%\node-webkit.app" "%DEST_FOLDER%\piskel.app" /E
33 | ECHO "DONE"
34 |
35 | ECHO "Copy Info.plist ..."
36 | set CONTENTS_FOLDER=%DEST_FOLDER%\piskel.app\Contents
37 | copy "%MISC_FOLDER%\desktop\Info.plist" "%CONTENTS_FOLDER%\"
38 | ECHO "DONE"
39 |
40 | ECHO "Copy application ..."
41 | set RESOURCES_FOLDER=%CONTENTS_FOLDER%\Resources
42 | copy "%RELEASES_FOLDER%\piskel\piskel.nw" "%RESOURCES_FOLDER%\"
43 | mv "%RESOURCES_FOLDER%\piskel.nw" "%RESOURCES_FOLDER%\app.nw"
44 | ECHO "%RESOURCES_FOLDER%"
45 | ECHO "DONE"
46 |
47 | ECHO "Copy icon ..."
48 | DEL "%RESOURCES_FOLDER%\nw.icns"
49 | COPY "%MISC_FOLDER%\desktop\nw.icns" "%RESOURCES_FOLDER%\"
50 | ECHO "DONE"
51 |
52 | pause
53 |
54 | explorer "%DEST_FOLDER%"
55 |
56 | endlocal
--------------------------------------------------------------------------------
/misc/scripts/package-windows-executable.cmd:
--------------------------------------------------------------------------------
1 | @ECHO off
2 |
3 | SETLOCAL
4 |
5 | PUSHD ..\..
6 | set PISKEL_HOME=%cd%
7 | POPD
8 |
9 | set RESOURCE_HACKER_PATH="C:\Program Files (x86)\Resource Hacker"
10 |
11 | set MISC_FOLDER=%PISKEL_HOME%\misc
12 | set RELEASES_FOLDER=%PISKEL_HOME%\dest\desktop
13 | set DEST_FOLDER=%RELEASES_FOLDER%\piskel\win32
14 |
15 | ECHO "Updating Piskel icon -- Using Resource Hacker"
16 | %RESOURCE_HACKER_PATH%\ResHacker -addoverwrite "%DEST_FOLDER%\piskel.exe", "%DEST_FOLDER%\piskel-logo.exe", "%MISC_FOLDER%\desktop\logo.ico", ICONGROUP, IDR_MAINFRAME, 1033
17 | DEL "%DEST_FOLDER%\piskel.exe"
18 | ECHO "DONE"
19 |
20 |
21 | PAUSE
22 | explorer "%DEST_FOLDER%\"
23 |
24 | ENDLOCAL
--------------------------------------------------------------------------------
/misc/scripts/piskel-root:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | // Writes the absolute path to the release build root to stdout
3 | var path = require('path');
4 | process.stdout.write(path.resolve(__dirname, '../../dest/prod') + '\n');
5 |
--------------------------------------------------------------------------------
/misc/selenium-ide/change-colors.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | change-colors
8 |
9 |
10 |
11 |
12 | change-colors |
13 |
14 |
15 | open |
16 | /?debug |
17 | |
18 |
19 |
20 | click |
21 | css=div.sp-preview-inner |
22 | |
23 |
24 |
25 | click |
26 | css=input.sp-input |
27 | |
28 |
29 |
30 | type |
31 | css=input.sp-input |
32 | rgb(170, 187, 204) |
33 |
34 |
35 | mouseDown |
36 | id=preview-list-scroller |
37 | |
38 |
39 |
40 | click |
41 | css=div.swap-colors-icon |
42 | |
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/misc/selenium-ide/change-size.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | change-size
8 |
9 |
10 |
11 |
12 | change-size |
13 |
14 |
15 | open |
16 | /?debug |
17 | |
18 |
19 |
20 | click |
21 | css=div.tool-icon.resize-icon |
22 | |
23 |
24 |
25 | type |
26 | name=resize-width |
27 | 64 |
28 |
29 |
30 | type |
31 | name=resize-height |
32 | 64 |
33 |
34 |
35 | click |
36 | name=resize-content-checkbox |
37 | |
38 |
39 |
40 | click |
41 | //input[@value='Resize'] |
42 | |
43 |
44 |
45 | mouseDown |
46 | id=column-wrapper |
47 | |
48 |
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/pre-commit:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # stash unstaged changes, run release task, stage release updates and restore stashed files
3 |
4 | NAME=$(git branch | grep '*' | sed 's/* //')
5 |
6 | # don't run on rebase
7 | if [ $NAME != '(no branch)' ]
8 | then
9 | NOT_STAGED=$(git status | grep 'not staged')
10 | # if [ "${NOT_STAGED#*not staged}" != "$NOT_STAGED" ]
11 | if [ "$NOT_STAGED" != "" ]
12 | then
13 | echo "Unclean directory, aborting commit. Run git status."
14 | exit 1
15 | fi
16 |
17 | grunt precommit
18 | RETVAL=$?
19 |
20 | if [ $RETVAL -ne 0 ]
21 | then
22 | echo "grunt test failed, aborting commit. Run grunt test"
23 | exit 1
24 | fi
25 | fi
--------------------------------------------------------------------------------
/src/css/animations.css:
--------------------------------------------------------------------------------
1 | @keyframes fade {
2 | 50% { opacity: 0.5; }
3 | }
4 |
5 | @keyframes glow {
6 | 0% { opacity: 0.66; }
7 | 50% { opacity: 1; }
8 | 100% { opacity: 0.66; }
9 | }
10 |
--------------------------------------------------------------------------------
/src/css/bootstrap/bootstrap-tooltip-custom.css:
--------------------------------------------------------------------------------
1 | .tooltip.in {
2 | opacity: 0.95;
3 | filter: alpha(opacity=95);
4 | }
5 |
6 | .tooltip {
7 | line-height: 20px;
8 | }
--------------------------------------------------------------------------------
/src/css/bootstrap/readme.txt:
--------------------------------------------------------------------------------
1 | Bootstrap custom build containing only the tooltip component
--------------------------------------------------------------------------------
/src/css/dialogs-browse-local.css:
--------------------------------------------------------------------------------
1 |
2 | /************************************************************************************************/
3 | /* Browse local piskels panel */
4 | /************************************************************************************************/
5 |
6 | #dialog-container.browse-local {
7 | width: 700px;
8 | height: 500px;
9 | top : 50%;
10 | left : 50%;
11 | position : absolute;
12 | margin-left: -350px;
13 | }
14 |
15 | .show #dialog-container.browse-local {
16 | margin-top: -250px;
17 | }
18 |
19 | .local-piskel-list {
20 | width: 100%;
21 | }
22 |
23 | .local-piskel-item {
24 | height: 3em;
25 | }
26 |
27 | .local-piskel-name {
28 | width: 40%;
29 | }
30 |
31 | .local-piskel-save-date {
32 | font-weight : normal;
33 | }
34 |
35 | .local-piskel-list a {
36 | text-decoration: none;
37 | }
38 |
39 | .local-piskel-list a:hover {
40 | text-decoration: underline;
41 | }
42 |
43 | .local-piskel-list-head {
44 | font-weight: bold;
45 | color: var(--highlight-color);
46 | }
47 |
48 | .local-piskel-load-button,
49 | .local-piskel-delete-button {
50 | width : 75px;
51 | }
--------------------------------------------------------------------------------
/src/css/dialogs-performance-info.css:
--------------------------------------------------------------------------------
1 | .performance-link {
2 | display: none;
3 | position: fixed;
4 | bottom: 10px;
5 | right: 10px;
6 | z-index: 11000;
7 | cursor: pointer;
8 | opacity: 0;
9 | transition : opacity 0.3s;
10 | }
11 |
12 | .performance-link.visible {
13 | display: block;
14 | opacity: 0.66;
15 | animation: glow 2s infinite;
16 | }
17 |
18 | .performance-link.visible:hover {
19 | opacity: 1;
20 | animation: none;
21 | }
22 |
23 | #dialog-container.performance-info {
24 | width: 500px;
25 | height: 525px;
26 | top : 50%;
27 | left : 50%;
28 | position : absolute;
29 | margin-left: -250px;
30 | margin-top: -260px;
31 |
32 | }
33 |
34 | .dialog-performance-info-body {
35 | font-size: 13px;
36 | letter-spacing: 1px;
37 | padding: 10px 20px;
38 | }
39 |
40 | .dialog-performance-info-body ul {
41 | border: 1px solid #666;
42 | padding: 5px;
43 | border-radius: 3px;
44 | }
45 |
46 | .dialog-performance-info-body li {
47 | list-style-type: initial;
48 | margin: 0 20px;
49 | }
50 |
51 | .dialog-performance-info-body sup {
52 | color: var(--highlight-color);
53 | cursor: pointer;
54 | }
55 |
56 | .show #dialog-container.performance-info {
57 | margin-top: -300px;
58 | }
59 |
60 | .dialog-performance-info-body .warning-icon {
61 | float: left;
62 | margin-top: 10px;
63 | }
64 |
65 | .dialog-performance-info-body .warning-icon-info {
66 | overflow: hidden;
67 | margin-left: 30px;
68 | }
69 |
--------------------------------------------------------------------------------
/src/css/dialogs-unsupported-browser.css:
--------------------------------------------------------------------------------
1 | /************************************************************************************************/
2 | /* Unsupported browser dialog */
3 | /************************************************************************************************/
4 |
5 | #dialog-container.unsupported-browser {
6 | width: 600px;
7 | height: 260px;
8 | top : 50%;
9 | left : 50%;
10 | position : absolute;
11 | margin-top: -130px;
12 | margin-left: -300px;
13 | }
14 |
15 | .unsupported-browser .dialog-content {
16 | font-size:1.2em;
17 | letter-spacing: 1px;
18 | padding:10px 20px;
19 | overflow: auto;
20 | }
21 |
22 | .unsupported-browser .supported-browser-list {
23 | padding: 5px 20px;
24 | }
25 |
26 | .unsupported-browser .supported-browser-list li {
27 | list-style-type: square;
28 | }
29 |
30 | #current-user-agent {
31 | color: var(--highlight-color);
32 | }
33 |
--------------------------------------------------------------------------------
/src/css/dialogs.css:
--------------------------------------------------------------------------------
1 | #dialog-container-wrapper {
2 | position: absolute;
3 | z-index: 20000;
4 |
5 | top: 0;
6 | right: 0;
7 | bottom: 0;
8 | left: 0;
9 |
10 | padding: 50px 150px;
11 | overflow: hidden;
12 |
13 | box-sizing: border-box;
14 | -moz-box-sizing : border-box;
15 |
16 | background-color: rgba(0,0,0,0.8);
17 | opacity: 0;
18 | pointer-events: none;
19 |
20 | color: white;
21 | }
22 |
23 | #dialog-container-wrapper.animated {
24 | transition: opacity 0.2s;
25 | }
26 |
27 | #dialog-container-wrapper.show {
28 | opacity: 1;
29 | pointer-events: auto;
30 | }
31 |
32 | #dialog-container {
33 | width: 100%;
34 | height: 100%;
35 |
36 | margin-top: -1500px;
37 |
38 | box-sizing: border-box;
39 | -moz-box-sizing : border-box;
40 |
41 | border-radius: 3px;
42 | border : 3px solid var(--highlight-color);
43 | background: #444;
44 | overflow: auto;
45 | }
46 |
47 | .show #dialog-container {
48 | margin-top: 0;
49 | }
50 |
51 | .dialog-wrapper {
52 | height: 100%;
53 | position : relative;
54 | }
55 |
56 | .dialog-content {
57 | position: absolute;
58 | top: 45px;
59 | bottom: 0;
60 | width: 100%;
61 | box-sizing: border-box;
62 | }
63 |
64 | .dialog-head {
65 | width: 100%;
66 | background: var(--highlight-color);
67 | margin: 0;
68 | padding: 10px;
69 | color: black;
70 | font-size: 1.8em;
71 | height: 45px;
72 | box-sizing: border-box;
73 | -moz-box-sizing: border-box;
74 | }
75 |
76 | .dialog-close {
77 | position: absolute;
78 | top: 0;
79 | right: 0;
80 | line-height: 45px;
81 | margin-right: 10px;
82 | font-size: 1.3em;
83 | cursor: pointer;
84 | }
--------------------------------------------------------------------------------
/src/css/fonts/icomoon.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/css/fonts/icomoon.eot
--------------------------------------------------------------------------------
/src/css/fonts/icomoon.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/css/fonts/icomoon.ttf
--------------------------------------------------------------------------------
/src/css/fonts/icomoon.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/css/fonts/icomoon.woff
--------------------------------------------------------------------------------
/src/css/fonts/piskel.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/css/fonts/piskel.eot
--------------------------------------------------------------------------------
/src/css/fonts/piskel.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/css/fonts/piskel.ttf
--------------------------------------------------------------------------------
/src/css/fonts/piskel.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/css/fonts/piskel.woff
--------------------------------------------------------------------------------
/src/css/forms.css:
--------------------------------------------------------------------------------
1 | .row {
2 | display: block;
3 | }
4 |
5 | .textfield {
6 | box-sizing:border-box;
7 |
8 | background : black;
9 | border : 1px solid #888;
10 | border-radius : 2px;
11 | padding : 3px 10px;
12 | color : white;
13 |
14 | height: 23px;
15 | }
16 |
17 | .textfield[readonly="true"] {
18 | background: transparent;
19 | }
20 |
21 | .textfield[disabled=disabled] {
22 | background : #3a3a3a;
23 | }
24 |
25 | .textfield:focus {
26 | border-color: var(--highlight-color);
27 | outline: none;
28 | }
29 |
30 | .textfield-small {
31 | width : 50px;
32 | }
33 |
34 | .button {
35 | box-sizing: border-box;
36 | height: 24px;
37 | background-color: #666;
38 | border-style: none;
39 | border-radius: 2px;
40 | cursor: pointer;
41 | text-decoration: none;
42 |
43 | color: white;
44 | font-weight: bold;
45 | font-size: 1rem;
46 | text-align: center;
47 |
48 | transition: background-color 0.2s linear;
49 | }
50 |
51 | .button:hover {
52 | color: var(--highlight-color);
53 | }
54 |
55 | .button-primary {
56 | background-color: var(--highlight-color);
57 | color: black;
58 | }
59 |
60 | .button-primary:hover {
61 | background-color: white;
62 | color: black;
63 | }
64 |
65 | .button[disabled],
66 | .button[disabled]:hover {
67 | cursor:default;
68 | background-color: #aaa;
69 | color: #777;
70 | }
71 |
72 | .import-size-field,
73 | .resize-size-field,
74 | .export-size-field {
75 | width: 50px;
76 | margin-right: 8px;
77 | text-align: right;
78 | }
--------------------------------------------------------------------------------
/src/css/minimap.css:
--------------------------------------------------------------------------------
1 | .minimap-crop-frame {
2 | position: absolute;
3 | border: 2px solid gold;
4 | z-index: 100;
5 | box-sizing: border-box;
6 | -moz-box-sizing: border-box;
7 | cursor: pointer;
8 | }
--------------------------------------------------------------------------------
/src/css/notifications.css:
--------------------------------------------------------------------------------
1 | .user-message {
2 | position: absolute;
3 | right: 0;
4 | bottom: 0;
5 | padding: 10px 47px;
6 | max-width: 300px;
7 |
8 | border-top-left-radius: 7px;
9 | border: #e1a325 2px solid;
10 | border-right: 0;
11 | border-bottom: 0;
12 |
13 | color: #222;
14 | background-color: var(--highlight-color);
15 |
16 | font-weight: bold;
17 | font-size: 13px;
18 |
19 | z-index: 30000;
20 | }
21 |
22 | .user-message .close {
23 | position: absolute;
24 | top: 6px;
25 | right: 17px;
26 |
27 | font-size: 18px;
28 | font-weight: bold;
29 |
30 | cursor: pointer;
31 | }
32 |
33 | .user-message .close:hover {
34 | color: black;
35 | }
36 |
37 | .progress-bar-container {
38 | position: absolute;
39 | left: 0;
40 | bottom: 0;
41 | padding: 10px;
42 | width: 360px;
43 | border-top-right-radius: 2px;
44 | border: var(--highlight-color) 2px solid;
45 | border-left: 0;
46 | border-bottom: 0;
47 | background-color: #444;
48 | font-size: 14px;
49 | z-index: 30000;
50 | color: #eee;
51 | }
52 |
53 | .progress-bar-item {
54 | float: left;
55 | height:20px;
56 | }
57 |
58 | .progress-bar-status {
59 | line-height: 20px;
60 | width : 40px;
61 | overflow : hidden;
62 | margin: 0 0 0 10px;
63 | }
64 |
65 | .progress-bar {
66 | border : 1px solid grey;
67 | margin-top: 8px;
68 | height : 4px;
69 | width : 300px;
70 | background : linear-gradient(to left, var(--highlight-color), var(--highlight-color)) no-repeat -300px 0;
71 | background-color : black;
72 | }
--------------------------------------------------------------------------------
/src/css/reset.css:
--------------------------------------------------------------------------------
1 | html, body {
2 | height : 100%; width: 100%;
3 | margin : 0;
4 | overflow: hidden;
5 | cursor : default;
6 | font-family: Arial;
7 | font-size: 11px;
8 | line-height: 1.1;
9 | -webkit-touch-callout: none;
10 | -webkit-user-select: none;
11 | -khtml-user-select: none;
12 | -moz-user-select: none;
13 | -ms-user-select: none;
14 | user-select: none;
15 | }
16 |
17 | ul, li {
18 | margin : 0;
19 | padding : 0;
20 | list-style-type: none;
21 | }
22 |
23 | /** Firefox overrides this with -moz-use-system-font */
24 | button,
25 | input,
26 | input[type="submit"] {
27 | font-family: Arial;
28 | }
29 |
30 | /* IE11 applies a big default margin for range inputs */
31 | input[type="range"] {
32 | padding: 0;
33 | }
34 |
35 | /* Force apparition of scrollbars on leopard */
36 | ::-webkit-scrollbar {
37 | -webkit-appearance: none;
38 | width: 6px;
39 | }
40 |
41 | ::-webkit-scrollbar-thumb {
42 | border-radius: 2px;
43 | background-color: #666;
44 | }
45 |
46 | ::-webkit-scrollbar-track {
47 | background-color: rgba(50, 50, 50, 0.4);
48 | }
49 |
50 | a, a:visited {
51 | color: var(--highlight-color);
52 | }
--------------------------------------------------------------------------------
/src/css/settings-import.css:
--------------------------------------------------------------------------------
1 | /************************************************************************************************/
2 | /* Import panel */
3 | /************************************************************************************************/
4 |
5 | .import-section,
6 | .resize-section {
7 | margin: 10px 0;
8 | }
9 |
10 | .file-input-button {
11 | margin-right: 8px;
12 | border-radius: 2px;
13 | }
14 |
15 | .import-highlight {
16 | font-weight: bold;
17 | color: white;
18 | }
--------------------------------------------------------------------------------
/src/css/settings-resize.css:
--------------------------------------------------------------------------------
1 | .resize-section-title {
2 | vertical-align: top;
3 | display: inline-block;
4 | padding-top: 5px;
5 | width: 25%;
6 | }
7 |
8 | .resize-anchor-container {
9 | position: relative;
10 | margin-top: 5px;
11 | display: inline-block;
12 | }
13 |
--------------------------------------------------------------------------------
/src/css/settings-save.css:
--------------------------------------------------------------------------------
1 | .save-field {
2 | width: 100%;
3 | }
4 |
5 | .save-status {
6 | margin-top: 10px;
7 | margin-bottom: -10px;
8 | vertical-align: middle;
9 | font-weight: normal;
10 | text-shadow: none;
11 | font-style: italic;
12 | }
13 |
14 | .save-file-name {
15 | white-space: nowrap;
16 | font-weight: bold;
17 | color: white;
18 | font-style: normal;
19 | }
20 |
21 | .save-desktop-file-name {
22 | word-wrap: break-word;
23 | font-weight: bold;
24 | color: white;
25 | font-style: normal;
26 | }
27 |
28 | .save-status-warning-icon {
29 | float: left;
30 | margin-top: 5px;
31 | }
32 |
33 | .save-status-warning-icon {
34 | overflow: hidden;
35 | padding-left: 10px;
36 | }
37 |
--------------------------------------------------------------------------------
/src/css/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | background: #1D1D1D;
3 | }
4 |
5 | /* Browser fixes */
6 | ::-ms-clear {
7 | display: none;
8 | }
9 |
10 | .allow-user-select {
11 | -webkit-touch-callout: initial;
12 | -webkit-user-select: initial;
13 | -khtml-user-select: initial;
14 | -moz-user-select: initial;
15 | -ms-user-select: initial;
16 | user-select: initial;
17 | }
18 |
19 | .no-overflow {
20 | overflow: hidden;
21 | }
22 |
23 | .highlight {
24 | color: var(--highlight-color);
25 | }
26 |
27 | .pull-top,
28 | .pull-right,
29 | .pull-bottom,
30 | .pull-left {
31 | position:absolute;
32 | }
33 |
34 | .pull-top {
35 | top:0;
36 | }
37 |
38 | .pull-right {
39 | right:0;
40 | }
41 |
42 | .pull-bottom {
43 | bottom:0;
44 | }
45 |
46 | .pull-left {
47 | left:0;
48 | }
49 |
50 | .uppercase {
51 | text-transform: uppercase;
52 | }
53 |
54 | .checkbox-fix {
55 | margin: 3px 3px 3px 0;
56 | }
57 |
58 | .checkbox-container {
59 | display: flex;
60 | align-items: center;
61 | }
62 |
63 | .hidden {
64 | display: none;
65 | }
66 |
67 | /**
68 | * TOOLTIPS
69 | */
70 | .tooltip-shortcut {
71 | color: var(--highlight-color);
72 | }
73 |
74 | .tooltip-container {
75 | text-align: left;
76 | }
77 |
78 | .tooltip-descriptor {
79 | color:#999;
80 | }
81 |
82 | .tooltip-descriptor:last-child {
83 | padding-bottom: 5px;
84 | }
85 |
86 | .tooltip-descriptor-button {
87 | padding: 2px;
88 | border: 1px Solid #999;
89 | font-size: 0.8em;
90 | margin-right: 5px;
91 | width: 35px;
92 | text-align: center;
93 | border-radius: 3px;
94 | display: inline-block;
95 | line-height: 10px;
96 | }
--------------------------------------------------------------------------------
/src/css/toolbox-layers-list.css:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * Layers container
4 | */
5 | .layers-list-container {
6 | min-height: 85px;
7 | display: flex;
8 | flex-direction: column;
9 | }
10 |
11 | /**
12 | * Layers title and toggle preview
13 | */
14 |
15 | .layers-title {
16 | position: relative;
17 | flex-shrink: 0;
18 | }
19 |
20 | .layers-toggle-preview {
21 | position: absolute;
22 | top: 0.3em;
23 | right: 0.5em;
24 |
25 | color: #999;
26 | font-size: 1.3em;
27 | cursor: pointer;
28 |
29 | transition: 0.2s linear;
30 | }
31 |
32 | .layers-toggle-preview:hover {
33 | color: white;
34 | }
35 |
36 | .layers-toggle-preview-enabled,
37 | .layers-toggle-preview-enabled:hover {
38 | color : var(--highlight-color);
39 | }
40 |
41 | /**
42 | * Layers buttons
43 | */
44 |
45 | .layers-button {
46 | margin: 0;
47 | width: 16.66667%;
48 | float : left;
49 | }
50 |
51 | /**
52 | * Layers list
53 | */
54 |
55 | .layers-list {
56 | font-size : 12px;
57 | overflow: auto;
58 | }
59 |
60 | .layer-item {
61 | position: relative;
62 | display: flex;
63 | height:24px;
64 | line-height: 24px;
65 | border-top: 1px solid #444;
66 | cursor: pointer;
67 | }
68 |
69 | .layer-item .layer-name,
70 | .layer-item .layer-name-input {
71 | padding: 0 0 0 10px;
72 | flex: 1 auto;
73 | white-space: nowrap;
74 | }
75 |
76 | .layer-item .layer-name-input {
77 | width: 80%;
78 | }
79 |
80 | .layer-item .layer-name.overflowing-name {
81 | overflow: hidden;
82 | text-overflow: ellipsis;
83 | }
84 |
85 | .layer-item:hover {
86 | background : #222;
87 | }
88 |
89 | .layer-item-opacity {
90 | padding: 0 8px 0 8px;
91 | flex: 0 auto;
92 | }
93 |
94 | .current-layer-item,
95 | .current-layer-item:hover {
96 | background : #333;
97 | color: var(--highlight-color);
98 | }
--------------------------------------------------------------------------------
/src/css/toolbox.css:
--------------------------------------------------------------------------------
1 | .toolbox-container {
2 | border: 2px solid #888;
3 | font-size: medium;
4 | color: white;
5 | text-align: left;
6 | border-radius: 2px;
7 | margin-top: 5px;
8 | overflow: hidden;
9 | }
10 |
11 | .toolbox-title {
12 | padding: 8px;
13 | margin: 0;
14 | font-size: 15px;
15 | /* reset for firefox */
16 | height: 16px;
17 | background: #222;
18 | }
19 |
20 | .toolbox-buttons {
21 | flex-shrink: 0;
22 | overflow: hidden;
23 | border-top: 1px solid #666;
24 | border-bottom: 1px solid #222;
25 | }
26 |
27 | .toolbox-buttons .button {
28 | /* Override border propery on .button elements from form.css */
29 | border-style: solid;
30 | border-color: #333;
31 | border-width: 0 1px 0 0;
32 | border-radius: 0;
33 |
34 | background-color: #3f3f3f;
35 | }
36 |
37 | .toolbox-buttons .button[disabled],
38 | .toolbox-buttons .button[disabled]:hover {
39 | background-color: #aaa;
40 | }
41 |
42 | .toolbox-buttons button:last-child {
43 | border-right-width: 0;
44 | }
45 |
--------------------------------------------------------------------------------
/src/css/transformations.css:
--------------------------------------------------------------------------------
1 | .transformations-container {
2 | flex-shrink: 0;
3 | }
4 |
5 | .transformations-container .tool-icon {
6 | margin: 0 0 5px 0;
7 | }
8 |
9 | .transformations-container .tools-wrapper {
10 | display: flex;
11 | flex-wrap: wrap;
12 | justify-content: space-between;
13 | height: 46px;
14 |
15 | /* Override the float:left set on tools-wrapper in layout.css; */
16 | float: initial;
17 | }
18 |
19 | .transformations-container.show-more .tools-wrapper {
20 | height: auto;
21 | /* Compensate the 5px bottom-margin coming from the tool-icon */
22 | margin-bottom: -5px;
23 | }
24 |
25 | .transformations-show-more-link {
26 | position: absolute;
27 | color: #999;
28 | right: 10px;
29 | font-weight: normal;
30 | cursor: pointer;
31 | transition: 0.2s linear;
32 | }
33 |
34 | .transformations-show-more-link:hover {
35 | color: white;
36 | }
37 |
38 | .show-more .transformations-show-more-link {
39 | color: gold;
40 | }
--------------------------------------------------------------------------------
/src/css/variables.css:
--------------------------------------------------------------------------------
1 | html, body {
2 | --highlight-color: gold;
3 | }
--------------------------------------------------------------------------------
/src/css/widgets-frame-picker.css:
--------------------------------------------------------------------------------
1 | /***********************/
2 | /* FRAME PICKER WIDGET */
3 | /***********************/
4 |
5 | .frame-picker-wrapper {
6 | width: 150px;
7 | height: 150px;
8 | border: 3px solid #666;
9 | border-radius: 3px;
10 | }
11 |
12 | .frame-viewer {
13 | width: 100%;
14 | height: calc(100% - 25px);
15 | display: flex;
16 | align-items: center;
17 | justify-content: center;
18 |
19 | }
20 |
21 | .frame-viewer > canvas,
22 | .frame-viewer > img {
23 | max-width: 100%;
24 | max-height: 100%;
25 | }
26 |
27 | .frame-nav {
28 | display: flex;
29 | width: 100%;
30 | height: 24px;
31 | border-top: 1px solid #666;
32 | }
33 |
34 | .frame-nav .button {
35 | flex-shrink: 0;
36 | border-radius: 0;
37 | height: 24px;
38 | background-color: #3f3f3f;
39 | }
40 |
41 | .frame-nav .button[disabled],
42 | .frame-nav .button[disabled]:hover {
43 | background-color: #aaa;
44 | }
45 |
46 | .frame-nav .button + .button {
47 | border-left: 1px solid #333;
48 | }
49 |
50 | .frame-nav-input {
51 | min-width: 1px;
52 | border-style: none;
53 | height: 24px;
54 | text-align: center;
55 | }
56 |
--------------------------------------------------------------------------------
/src/css/widgets-size-picker.css:
--------------------------------------------------------------------------------
1 | /***********************/
2 | /* SIZE PICKER WIDGET */
3 | /***********************/
4 |
5 | .size-picker-container {
6 | overflow: hidden;
7 | padding: 5px 5px;
8 | }
9 |
10 | .size-picker-option {
11 | float: left;
12 | box-sizing: border-box;
13 | width: 20px;
14 | height: 20px;
15 | margin-right: 2px;
16 | border-style: solid;
17 | border-width: 2px;
18 | border-color: #444;
19 | cursor: pointer;
20 | }
21 |
22 | .size-picker-option[data-size='1'] {
23 | padding: 5px;
24 | }
25 | .size-picker-option[data-size='2'] {
26 | padding: 4px;
27 | }
28 | .size-picker-option[data-size='3'] {
29 | padding: 3px;
30 | }
31 | .size-picker-option[data-size='4'] {
32 | padding: 2px;
33 | }
34 |
35 | .size-picker-option:before {
36 | content: '';
37 | width: 100%;
38 | height: 100%;
39 | background-color: white;
40 | display: block;
41 | text-align: center;
42 | line-height: 12px;
43 | font-size: 90%;
44 | }
45 |
46 | .size-picker-option:hover {
47 | border-color: #888;
48 | }
49 |
50 | .size-picker-option.selected:before {
51 | background-color: var(--highlight-color);
52 | }
53 |
54 | .size-picker-option.selected {
55 | border-color: var(--highlight-color);
56 | }
57 |
58 | .size-picker-option.labeled:before {
59 | content: attr(real-size);
60 | color: black;
61 | }
62 |
--------------------------------------------------------------------------------
/src/css/widgets-tabs.css:
--------------------------------------------------------------------------------
1 | /*****************/
2 | /* TABS WIDGET */
3 | /*****************/
4 |
5 | .tab-list {
6 | overflow: hidden;
7 | position: relative;
8 | }
9 |
10 | .tab-list:after {
11 | content: "";
12 | display: block;
13 | position: absolute;
14 | bottom: 0;
15 | width: 100%;
16 | height: 1px;
17 | z-index: 0;
18 | background-color: var(--highlight-color);
19 | }
20 |
21 | .tab-item {
22 | float: left;
23 | cursor: pointer;
24 | padding: 5px;
25 | border: 1px solid transparent;
26 | border-radius: 2px 2px 0 0;
27 | /* Make sure the tab and its border are positioned above the :after element; */
28 | position: relative;
29 | z-index: 1;
30 | }
31 |
32 | .tab-item.selected,
33 | .tab-item:hover {
34 | color: var(--highlight-color);
35 | }
36 |
37 | .tab-item.selected {
38 | border-color: var(--highlight-color);
39 | border-bottom-color: #444;
40 | border-style: solid;
41 | border-width: 1px;
42 | }
--------------------------------------------------------------------------------
/src/css/widgets-wizard.css:
--------------------------------------------------------------------------------
1 | .wizard-wrapper {
2 | z-index: 1;
3 | position: relative;
4 | width: 100%;
5 | height: 100%;
6 | overflow: hidden;
7 | }
8 |
9 | .wizard-step {
10 | z-index: -1;
11 | margin-left: calc(100% + 5px);
12 | position: absolute;
13 | }
14 |
15 | .current-step {
16 | z-index: 1;
17 | margin-left: 0;
18 | }
19 |
20 | .current-step-in,
21 | .current-step-out {
22 | z-index: 10;
23 | transition: margin-left 200ms;
24 | }
25 |
26 | .current-step-in {
27 | margin-left: 0;
28 | }
29 |
30 | .current-step-out {
31 | margin-left: 100%;
32 | }
33 |
--------------------------------------------------------------------------------
/src/img/canvas-backgrounds/canvas-background-light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/canvas-backgrounds/canvas-background-light.png
--------------------------------------------------------------------------------
/src/img/canvas-backgrounds/canvas-background-lowcontrast-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/canvas-backgrounds/canvas-background-lowcontrast-dark.png
--------------------------------------------------------------------------------
/src/img/canvas-backgrounds/canvas-background-lowcontrast-medium.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/canvas-backgrounds/canvas-background-lowcontrast-medium.png
--------------------------------------------------------------------------------
/src/img/canvas-backgrounds/canvas-background-medium.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/canvas-backgrounds/canvas-background-medium.png
--------------------------------------------------------------------------------
/src/img/cursors/circle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/cursors/circle.png
--------------------------------------------------------------------------------
/src/img/cursors/color-palette.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/cursors/color-palette.png
--------------------------------------------------------------------------------
/src/img/cursors/dither.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/cursors/dither.png
--------------------------------------------------------------------------------
/src/img/cursors/dropper.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/cursors/dropper.png
--------------------------------------------------------------------------------
/src/img/cursors/eraser.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/cursors/eraser.png
--------------------------------------------------------------------------------
/src/img/cursors/hand.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/cursors/hand.png
--------------------------------------------------------------------------------
/src/img/cursors/lighten.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/cursors/lighten.png
--------------------------------------------------------------------------------
/src/img/cursors/mirror-pen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/cursors/mirror-pen.png
--------------------------------------------------------------------------------
/src/img/cursors/paint-bucket.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/cursors/paint-bucket.png
--------------------------------------------------------------------------------
/src/img/cursors/pen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/cursors/pen.png
--------------------------------------------------------------------------------
/src/img/cursors/rectangle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/cursors/rectangle.png
--------------------------------------------------------------------------------
/src/img/cursors/select.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/cursors/select.png
--------------------------------------------------------------------------------
/src/img/cursors/stroke.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/cursors/stroke.png
--------------------------------------------------------------------------------
/src/img/cursors/vertical-mirror-pen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/cursors/vertical-mirror-pen.png
--------------------------------------------------------------------------------
/src/img/cursors/wand.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/cursors/wand.png
--------------------------------------------------------------------------------
/src/img/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/favicon.png
--------------------------------------------------------------------------------
/src/img/icons/common/common-backup-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/common/common-backup-white.png
--------------------------------------------------------------------------------
/src/img/icons/common/common-backup-white@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/common/common-backup-white@2x.png
--------------------------------------------------------------------------------
/src/img/icons/common/common-keyboard-gold.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/common/common-keyboard-gold.png
--------------------------------------------------------------------------------
/src/img/icons/common/common-keyboard-gold@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/common/common-keyboard-gold@2x.png
--------------------------------------------------------------------------------
/src/img/icons/common/common-swapcolors-arrow-grey.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/common/common-swapcolors-arrow-grey.png
--------------------------------------------------------------------------------
/src/img/icons/common/common-swapcolors-arrow-grey@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/common/common-swapcolors-arrow-grey@2x.png
--------------------------------------------------------------------------------
/src/img/icons/common/common-warning-red.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/common/common-warning-red.png
--------------------------------------------------------------------------------
/src/img/icons/common/common-warning-red@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/common/common-warning-red@2x.png
--------------------------------------------------------------------------------
/src/img/icons/frame/frame-dragndrop-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/frame/frame-dragndrop-white.png
--------------------------------------------------------------------------------
/src/img/icons/frame/frame-dragndrop-white@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/frame/frame-dragndrop-white@2x.png
--------------------------------------------------------------------------------
/src/img/icons/frame/frame-duplicate-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/frame/frame-duplicate-white.png
--------------------------------------------------------------------------------
/src/img/icons/frame/frame-duplicate-white@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/frame/frame-duplicate-white@2x.png
--------------------------------------------------------------------------------
/src/img/icons/frame/frame-plus-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/frame/frame-plus-white.png
--------------------------------------------------------------------------------
/src/img/icons/frame/frame-plus-white@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/frame/frame-plus-white@2x.png
--------------------------------------------------------------------------------
/src/img/icons/frame/frame-recyclebin-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/frame/frame-recyclebin-white.png
--------------------------------------------------------------------------------
/src/img/icons/frame/frame-recyclebin-white@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/frame/frame-recyclebin-white@2x.png
--------------------------------------------------------------------------------
/src/img/icons/minimap/minimap-grid-gold.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/minimap/minimap-grid-gold.png
--------------------------------------------------------------------------------
/src/img/icons/minimap/minimap-grid-gold@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/minimap/minimap-grid-gold@2x.png
--------------------------------------------------------------------------------
/src/img/icons/minimap/minimap-grid-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/minimap/minimap-grid-white.png
--------------------------------------------------------------------------------
/src/img/icons/minimap/minimap-grid-white@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/minimap/minimap-grid-white@2x.png
--------------------------------------------------------------------------------
/src/img/icons/minimap/minimap-popup-preview-arrow-gold.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/minimap/minimap-popup-preview-arrow-gold.png
--------------------------------------------------------------------------------
/src/img/icons/minimap/minimap-popup-preview-arrow-gold@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/minimap/minimap-popup-preview-arrow-gold@2x.png
--------------------------------------------------------------------------------
/src/img/icons/minimap/minimap-popup-preview-arrow-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/minimap/minimap-popup-preview-arrow-white.png
--------------------------------------------------------------------------------
/src/img/icons/minimap/minimap-popup-preview-arrow-white@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/minimap/minimap-popup-preview-arrow-white@2x.png
--------------------------------------------------------------------------------
/src/img/icons/settings/settings-export-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/settings/settings-export-white.png
--------------------------------------------------------------------------------
/src/img/icons/settings/settings-export-white@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/settings/settings-export-white@2x.png
--------------------------------------------------------------------------------
/src/img/icons/settings/settings-gear-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/settings/settings-gear-white.png
--------------------------------------------------------------------------------
/src/img/icons/settings/settings-gear-white@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/settings/settings-gear-white@2x.png
--------------------------------------------------------------------------------
/src/img/icons/settings/settings-open-folder-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/settings/settings-open-folder-white.png
--------------------------------------------------------------------------------
/src/img/icons/settings/settings-open-folder-white@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/settings/settings-open-folder-white@2x.png
--------------------------------------------------------------------------------
/src/img/icons/settings/settings-resize-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/settings/settings-resize-white.png
--------------------------------------------------------------------------------
/src/img/icons/settings/settings-resize-white@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/settings/settings-resize-white@2x.png
--------------------------------------------------------------------------------
/src/img/icons/settings/settings-save-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/settings/settings-save-white.png
--------------------------------------------------------------------------------
/src/img/icons/settings/settings-save-white@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/settings/settings-save-white@2x.png
--------------------------------------------------------------------------------
/src/img/icons/tools/tool-circle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/tools/tool-circle.png
--------------------------------------------------------------------------------
/src/img/icons/tools/tool-circle@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/tools/tool-circle@2x.png
--------------------------------------------------------------------------------
/src/img/icons/tools/tool-colorpicker.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/tools/tool-colorpicker.png
--------------------------------------------------------------------------------
/src/img/icons/tools/tool-colorpicker@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/tools/tool-colorpicker@2x.png
--------------------------------------------------------------------------------
/src/img/icons/tools/tool-colorswap.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/tools/tool-colorswap.png
--------------------------------------------------------------------------------
/src/img/icons/tools/tool-colorswap@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/tools/tool-colorswap@2x.png
--------------------------------------------------------------------------------
/src/img/icons/tools/tool-dithering.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/tools/tool-dithering.png
--------------------------------------------------------------------------------
/src/img/icons/tools/tool-dithering@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/tools/tool-dithering@2x.png
--------------------------------------------------------------------------------
/src/img/icons/tools/tool-eraser.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/tools/tool-eraser.png
--------------------------------------------------------------------------------
/src/img/icons/tools/tool-eraser@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/tools/tool-eraser@2x.png
--------------------------------------------------------------------------------
/src/img/icons/tools/tool-lasso-select.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/tools/tool-lasso-select.png
--------------------------------------------------------------------------------
/src/img/icons/tools/tool-lasso-select@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/tools/tool-lasso-select@2x.png
--------------------------------------------------------------------------------
/src/img/icons/tools/tool-lighten.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/tools/tool-lighten.png
--------------------------------------------------------------------------------
/src/img/icons/tools/tool-lighten@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/tools/tool-lighten@2x.png
--------------------------------------------------------------------------------
/src/img/icons/tools/tool-move.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/tools/tool-move.png
--------------------------------------------------------------------------------
/src/img/icons/tools/tool-move@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/tools/tool-move@2x.png
--------------------------------------------------------------------------------
/src/img/icons/tools/tool-paint-bucket.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/tools/tool-paint-bucket.png
--------------------------------------------------------------------------------
/src/img/icons/tools/tool-paint-bucket@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/tools/tool-paint-bucket@2x.png
--------------------------------------------------------------------------------
/src/img/icons/tools/tool-pen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/tools/tool-pen.png
--------------------------------------------------------------------------------
/src/img/icons/tools/tool-pen@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/tools/tool-pen@2x.png
--------------------------------------------------------------------------------
/src/img/icons/tools/tool-rectangle-select.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/tools/tool-rectangle-select.png
--------------------------------------------------------------------------------
/src/img/icons/tools/tool-rectangle-select@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/tools/tool-rectangle-select@2x.png
--------------------------------------------------------------------------------
/src/img/icons/tools/tool-rectangle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/tools/tool-rectangle.png
--------------------------------------------------------------------------------
/src/img/icons/tools/tool-rectangle@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/tools/tool-rectangle@2x.png
--------------------------------------------------------------------------------
/src/img/icons/tools/tool-shape-select.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/tools/tool-shape-select.png
--------------------------------------------------------------------------------
/src/img/icons/tools/tool-shape-select@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/tools/tool-shape-select@2x.png
--------------------------------------------------------------------------------
/src/img/icons/tools/tool-stroke.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/tools/tool-stroke.png
--------------------------------------------------------------------------------
/src/img/icons/tools/tool-stroke@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/tools/tool-stroke@2x.png
--------------------------------------------------------------------------------
/src/img/icons/tools/tool-vertical-mirror-pen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/tools/tool-vertical-mirror-pen.png
--------------------------------------------------------------------------------
/src/img/icons/tools/tool-vertical-mirror-pen@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/tools/tool-vertical-mirror-pen@2x.png
--------------------------------------------------------------------------------
/src/img/icons/transform/tool-center.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/transform/tool-center.png
--------------------------------------------------------------------------------
/src/img/icons/transform/tool-center@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/transform/tool-center@2x.png
--------------------------------------------------------------------------------
/src/img/icons/transform/tool-clone.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/transform/tool-clone.png
--------------------------------------------------------------------------------
/src/img/icons/transform/tool-clone@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/transform/tool-clone@2x.png
--------------------------------------------------------------------------------
/src/img/icons/transform/tool-crop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/transform/tool-crop.png
--------------------------------------------------------------------------------
/src/img/icons/transform/tool-crop@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/transform/tool-crop@2x.png
--------------------------------------------------------------------------------
/src/img/icons/transform/tool-flip.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/transform/tool-flip.png
--------------------------------------------------------------------------------
/src/img/icons/transform/tool-flip@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/transform/tool-flip@2x.png
--------------------------------------------------------------------------------
/src/img/icons/transform/tool-rotate.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/transform/tool-rotate.png
--------------------------------------------------------------------------------
/src/img/icons/transform/tool-rotate@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/icons/transform/tool-rotate@2x.png
--------------------------------------------------------------------------------
/src/img/unused/circle-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/unused/circle-dark.png
--------------------------------------------------------------------------------
/src/img/unused/eraser-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/unused/eraser-dark.png
--------------------------------------------------------------------------------
/src/img/unused/eyedropper-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/unused/eyedropper-dark.png
--------------------------------------------------------------------------------
/src/img/unused/gallery.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/unused/gallery.png
--------------------------------------------------------------------------------
/src/img/unused/hand-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/unused/hand-dark.png
--------------------------------------------------------------------------------
/src/img/unused/lasso-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/unused/lasso-dark.png
--------------------------------------------------------------------------------
/src/img/unused/magicwand-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/unused/magicwand-dark.png
--------------------------------------------------------------------------------
/src/img/unused/paintbucket-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/unused/paintbucket-dark.png
--------------------------------------------------------------------------------
/src/img/unused/pen-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/unused/pen-dark.png
--------------------------------------------------------------------------------
/src/img/unused/rectangle-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/unused/rectangle-dark.png
--------------------------------------------------------------------------------
/src/img/unused/rectangle_selection-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/img/unused/rectangle_selection-dark.png
--------------------------------------------------------------------------------
/src/js/controller/CanvasBackgroundController.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.controller');
3 |
4 | ns.CanvasBackgroundController = function () {
5 | this.body = document.body;
6 | };
7 |
8 | ns.CanvasBackgroundController.prototype.init = function () {
9 | $.subscribe(Events.USER_SETTINGS_CHANGED, this.onUserSettingsChange_.bind(this));
10 | this.updateBackgroundClass_(pskl.UserSettings.get(pskl.UserSettings.CANVAS_BACKGROUND));
11 | };
12 |
13 | ns.CanvasBackgroundController.prototype.onUserSettingsChange_ = function (evt, settingName, settingValue) {
14 | if (settingName == pskl.UserSettings.CANVAS_BACKGROUND) {
15 | this.updateBackgroundClass_(settingValue);
16 | }
17 | };
18 |
19 | ns.CanvasBackgroundController.prototype.updateBackgroundClass_ = function (newClass) {
20 | var currentClass = this.body.dataset.currentBackgroundClass;
21 | if (currentClass) {
22 | this.body.classList.remove(currentClass);
23 | }
24 | this.body.classList.add(newClass);
25 | this.body.dataset.currentBackgroundClass = newClass;
26 | };
27 | })();
28 |
--------------------------------------------------------------------------------
/src/js/controller/NotificationController.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.controller');
3 |
4 | ns.NotificationController = function () {};
5 |
6 | /**
7 | * @public
8 | */
9 | ns.NotificationController.prototype.init = function() {
10 | $.subscribe(Events.SHOW_NOTIFICATION, this.displayMessage_.bind(this));
11 | $.subscribe(Events.HIDE_NOTIFICATION, this.removeMessage_.bind(this));
12 | };
13 |
14 | /**
15 | * @private
16 | */
17 | ns.NotificationController.prototype.displayMessage_ = function (evt, messageInfo) {
18 | this.removeMessage_();
19 |
20 | var message = document.createElement('div');
21 | message.id = 'user-message';
22 | message.className = 'user-message';
23 | message.innerHTML = messageInfo.content;
24 | message.innerHTML = message.innerHTML + 'x
';
25 | document.body.appendChild(message);
26 |
27 | message.querySelector('.close').addEventListener('click', this.removeMessage_.bind(this));
28 |
29 | if (messageInfo.hideDelay) {
30 | window.setTimeout(this.removeMessage_.bind(this), messageInfo.hideDelay);
31 | }
32 | };
33 |
34 | /**
35 | * @private
36 | */
37 | ns.NotificationController.prototype.removeMessage_ = function (evt) {
38 | var message = document.querySelector('#user-message');
39 | if (message) {
40 | message.parentNode.removeChild(message);
41 | }
42 | };
43 | })();
44 |
--------------------------------------------------------------------------------
/src/js/controller/PenSizeController.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.controller');
3 |
4 | ns.PenSizeController = function () {
5 | this.sizePicker = new pskl.widgets.SizePicker(this.onSizePickerChanged_.bind(this));
6 | };
7 |
8 | ns.PenSizeController.prototype.init = function () {
9 | this.sizePicker.init(document.querySelector('.pen-size-container'));
10 |
11 | $.subscribe(Events.PEN_SIZE_CHANGED, this.onPenSizeChanged_.bind(this));
12 | this.updateSelectedOption_();
13 | };
14 |
15 | ns.PenSizeController.prototype.onSizePickerChanged_ = function (size) {
16 | pskl.app.penSizeService.setPenSize(size);
17 | };
18 |
19 | ns.PenSizeController.prototype.onPenSizeChanged_ = function (e) {
20 | this.updateSelectedOption_();
21 | };
22 |
23 | ns.PenSizeController.prototype.updateSelectedOption_ = function () {
24 | var size = pskl.app.penSizeService.getPenSize();
25 | this.sizePicker.setSize(size);
26 | };
27 | })();
28 |
--------------------------------------------------------------------------------
/src/js/controller/dialogs/AbstractDialogController.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.controller.dialogs');
3 |
4 | ns.AbstractDialogController = function () {};
5 |
6 | ns.AbstractDialogController.prototype.init = function () {
7 | var closeButton = document.querySelector('.dialog-close');
8 | this.addEventListener(closeButton, 'click', this.closeDialog);
9 | };
10 |
11 | ns.AbstractDialogController.prototype.addEventListener = function (el, type, cb) {
12 | pskl.utils.Event.addEventListener(el, type, cb, this);
13 | };
14 |
15 | ns.AbstractDialogController.prototype.destroy = function () {
16 | pskl.utils.Event.removeAllEventListeners(this);
17 | };
18 |
19 | ns.AbstractDialogController.prototype.closeDialog = function () {
20 | $.publish(Events.DIALOG_HIDE);
21 | };
22 |
23 | ns.AbstractDialogController.prototype.setTitle = function (title) {
24 | var dialogTitle = document.querySelector('.dialog-title');
25 | if (dialogTitle) {
26 | dialogTitle.innerText = title;
27 | }
28 | };
29 | })();
30 |
--------------------------------------------------------------------------------
/src/js/controller/dialogs/PerformanceInfoController.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.controller.dialogs');
3 |
4 | ns.PerformanceInfoController = function () {};
5 |
6 | pskl.utils.inherit(ns.PerformanceInfoController, ns.AbstractDialogController);
7 |
8 | ns.PerformanceInfoController.prototype.init = function () {
9 | this.superclass.init.call(this);
10 | };
11 | })();
12 |
--------------------------------------------------------------------------------
/src/js/controller/dialogs/UnsupportedBrowserController.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.controller.dialogs');
3 |
4 | ns.UnsupportedBrowserController = function () {};
5 |
6 | pskl.utils.inherit(ns.UnsupportedBrowserController, ns.AbstractDialogController);
7 |
8 | ns.UnsupportedBrowserController.prototype.init = function () {
9 | this.superclass.init.call(this);
10 | var currentUserAgentElement = document.querySelector('#current-user-agent');
11 | currentUserAgentElement.innerText = pskl.utils.UserAgent.getDisplayName();
12 | };
13 | })();
14 |
--------------------------------------------------------------------------------
/src/js/controller/dialogs/importwizard/steps/SelectMode.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.controller.dialogs.importwizard.steps');
3 |
4 | ns.SelectMode = function (piskelController, importController, container) {
5 | this.superclass.constructor.apply(this, arguments);
6 | };
7 |
8 | ns.SelectMode.MODES = {
9 | REPLACE : 'replace',
10 | MERGE : 'merge'
11 | };
12 |
13 | pskl.utils.inherit(ns.SelectMode, ns.AbstractImportStep);
14 |
15 | ns.SelectMode.prototype.init = function () {
16 | this.superclass.init.call(this);
17 |
18 | var replaceButton = this.container.querySelector('.import-mode-replace-button');
19 | var mergeButton = this.container.querySelector('.import-mode-merge-button');
20 |
21 | this.addEventListener(replaceButton, 'click', this.onReplaceButtonClick_);
22 | this.addEventListener(mergeButton, 'click', this.onMergeButtonClick_);
23 | };
24 |
25 | ns.SelectMode.prototype.onShow = function () {
26 | this.superclass.onShow.call(this);
27 | };
28 |
29 | ns.SelectMode.prototype.destroy = function () {
30 | this.superclass.destroy.call(this);
31 | };
32 |
33 | ns.SelectMode.prototype.onReplaceButtonClick_ = function () {
34 | this.mergeData.importMode = ns.SelectMode.MODES.REPLACE;
35 | this.onNextClick();
36 | };
37 |
38 | ns.SelectMode.prototype.onMergeButtonClick_ = function () {
39 | this.mergeData.importMode = ns.SelectMode.MODES.MERGE;
40 | this.onNextClick();
41 | };
42 | })();
43 |
--------------------------------------------------------------------------------
/src/js/controller/settings/AbstractSettingController.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.controller.settings');
3 | ns.AbstractSettingController = function () {};
4 |
5 | ns.AbstractSettingController.prototype.addEventListener = function (el, type, callback) {
6 | pskl.utils.Event.addEventListener(el, type, callback, this);
7 | };
8 |
9 | ns.AbstractSettingController.prototype.destroy = function () {
10 | pskl.utils.Event.removeAllEventListeners(this);
11 | this.nullifyDomReferences_();
12 | };
13 |
14 | ns.AbstractSettingController.prototype.nullifyDomReferences_ = function () {
15 | for (var key in this) {
16 | if (this.hasOwnProperty(key)) {
17 | var isHTMLElement = this[key] && this[key].nodeName;
18 | if (isHTMLElement) {
19 | this[key] = null;
20 | }
21 | }
22 | }
23 | };
24 | })();
25 |
--------------------------------------------------------------------------------
/src/js/controller/settings/PreferencesController.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.controller.settings');
3 |
4 | var tabs = {
5 | 'misc' : {
6 | template : 'templates/settings/preferences/misc.html',
7 | controller : ns.preferences.MiscPreferencesController
8 | },
9 | 'grid' : {
10 | template : 'templates/settings/preferences/grid.html',
11 | controller : ns.preferences.GridPreferencesController
12 | },
13 | 'tile' : {
14 | template : 'templates/settings/preferences/tile.html',
15 | controller : ns.preferences.TilePreferencesController
16 | }
17 | };
18 |
19 | ns.PreferencesController = function () {
20 | this.tabsWidget = new pskl.widgets.Tabs(tabs, this, pskl.UserSettings.PREFERENCES_TAB);
21 | };
22 |
23 | pskl.utils.inherit(ns.PreferencesController, pskl.controller.settings.AbstractSettingController);
24 |
25 | ns.PreferencesController.prototype.init = function() {
26 | var container = document.querySelector('.settings-section-preferences');
27 | this.tabsWidget.init(container);
28 | };
29 |
30 | ns.PreferencesController.prototype.destroy = function () {
31 | this.tabsWidget.destroy();
32 | this.superclass.destroy.call(this);
33 | };
34 |
35 | })();
36 |
--------------------------------------------------------------------------------
/src/js/devtools/DrawingTestRunner.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.devtools');
3 |
4 | ns.DrawingTestRunner = function (testName) {
5 | this.testName = testName;
6 | $.subscribe(Events.TEST_RECORD_END, this.onTestRecordEnd_.bind(this));
7 | };
8 |
9 | ns.DrawingTestRunner.prototype.start = function () {
10 | pskl.utils.Xhr.get(this.testName, function (response) {
11 | var res = response.responseText;
12 | var recordPlayer = new ns.DrawingTestPlayer(JSON.parse(res));
13 | recordPlayer.start();
14 | }.bind(this));
15 | };
16 |
17 | ns.DrawingTestRunner.prototype.onTestRecordEnd_ = function (evt, success) {
18 | var testResult = document.createElement('div');
19 | testResult.id = 'drawing-test-result';
20 | testResult.setAttribute('data-test-name', this.testName);
21 | testResult.innerHTML = success ? 'OK' : 'KO';
22 | document.body.appendChild(testResult);
23 | };
24 | })();
25 |
--------------------------------------------------------------------------------
/src/js/devtools/MouseEvent.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.devtools');
3 |
4 | ns.MouseEvent = function (event, coords) {
5 | this.event = {
6 | type : event.type,
7 | button : event.button,
8 | shiftKey : event.shiftKey,
9 | altKey : event.altKey,
10 | ctrlKey : event.ctrlKey
11 | };
12 | this.coords = coords;
13 | this.type = 'mouse-event';
14 | };
15 |
16 | ns.MouseEvent.prototype.equals = function (otherEvent) {
17 | if (otherEvent && otherEvent instanceof ns.MouseEvent) {
18 | var sameEvent = JSON.stringify(otherEvent.event) == JSON.stringify(this.event);
19 | var sameCoords = JSON.stringify(otherEvent.coords) == JSON.stringify(this.coords);
20 | return sameEvent && sameCoords;
21 | } else {
22 | return false;
23 | }
24 | };
25 | })();
26 |
--------------------------------------------------------------------------------
/src/js/devtools/init.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.devtools');
3 |
4 | ns.init = function () {
5 | var href = document.location.href.toLowerCase();
6 | // test tools
7 | var testModeOn = href.indexOf('test=true') !== -1;
8 | if (testModeOn) {
9 | this.testRecorder = new pskl.devtools.DrawingTestRecorder(pskl.app.piskelController);
10 | this.testRecorder.init();
11 |
12 | this.testRecordController = new pskl.devtools.TestRecordController(this.testRecorder);
13 | this.testRecordController.init();
14 | }
15 |
16 | // test tools
17 | var runTestModeOn = href.indexOf('test-run=') !== -1;
18 | if (runTestModeOn) {
19 | var testPath = href.split('test-run=')[1];
20 | this.testRunner = new pskl.devtools.DrawingTestRunner(testPath);
21 | this.testRunner.start();
22 | }
23 |
24 | // test tools
25 | var runSuiteModeOn = href.indexOf('test-suite=') !== -1;
26 | if (runSuiteModeOn) {
27 | var suitePath = href.split('test-suite=')[1];
28 | this.testSuiteController = new pskl.devtools.DrawingTestSuiteController(suitePath);
29 | this.testSuiteController.init();
30 | this.testSuiteController.start();
31 | }
32 | };
33 |
34 | })();
35 |
--------------------------------------------------------------------------------
/src/js/lib/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/js/lib/.DS_Store
--------------------------------------------------------------------------------
/src/js/lib/bootstrap/readme.txt:
--------------------------------------------------------------------------------
1 | Bootstrap custom build containing only the tooltip component
--------------------------------------------------------------------------------
/src/js/lib/pubsub.js:
--------------------------------------------------------------------------------
1 | /* jQuery Tiny Pub/Sub - v0.7 - 10/27/2011
2 | * http://benalman.com/
3 | * Copyright (c) 2011 "Cowboy" Ben Alman; Licensed MIT, GPL */
4 |
5 | (function($) {
6 |
7 | var o = $({});
8 |
9 | $.subscribe = function() {
10 | //console.log("SUBSCRIBE: " + arguments[0]);
11 | o.on.apply(o, arguments);
12 | };
13 |
14 | $.unsubscribe = function() {
15 | o.off.apply(o, arguments);
16 | };
17 |
18 | $.publish = function() {
19 | //console.log("PUBLISH: " + arguments[0]);
20 | o.trigger.apply(o, arguments);
21 | };
22 |
23 | }(jQuery));
24 |
25 |
26 |
--------------------------------------------------------------------------------
/src/js/lib/scrollifneeded/scrollifneeded.js:
--------------------------------------------------------------------------------
1 | if (!Element.prototype.scrollIntoViewIfNeeded) {
2 | Element.prototype.scrollIntoViewIfNeeded = function (centerIfNeeded) {
3 | centerIfNeeded = arguments.length === 0 ? true : !!centerIfNeeded;
4 |
5 | var parent = this.parentNode,
6 | parentComputedStyle = window.getComputedStyle(parent, null),
7 | parentBorderTopWidth = parseInt(parentComputedStyle.getPropertyValue('border-top-width')),
8 | parentBorderLeftWidth = parseInt(parentComputedStyle.getPropertyValue('border-left-width')),
9 | overTop = this.offsetTop - parent.offsetTop < parent.scrollTop,
10 | overBottom = (this.offsetTop - parent.offsetTop + this.clientHeight - parentBorderTopWidth) > (parent.scrollTop + parent.clientHeight),
11 | overLeft = this.offsetLeft - parent.offsetLeft < parent.scrollLeft,
12 | overRight = (this.offsetLeft - parent.offsetLeft + this.clientWidth - parentBorderLeftWidth) > (parent.scrollLeft + parent.clientWidth),
13 | alignWithTop = overTop && !overBottom;
14 |
15 | if ((overTop || overBottom) && centerIfNeeded) {
16 | parent.scrollTop = this.offsetTop - parent.offsetTop - parent.clientHeight / 2 - parentBorderTopWidth + this.clientHeight / 2;
17 | }
18 |
19 | if ((overLeft || overRight) && centerIfNeeded) {
20 | parent.scrollLeft = this.offsetLeft - parent.offsetLeft - parent.clientWidth / 2 - parentBorderLeftWidth + this.clientWidth / 2;
21 | }
22 |
23 | if ((overTop || overBottom || overLeft || overRight) && !centerIfNeeded) {
24 | this.scrollIntoView(alignWithTop);
25 | }
26 | };
27 | }
--------------------------------------------------------------------------------
/src/js/lib/smoothscroll/LICENSE.txt:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2013 Dustan Kasten
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/src/js/model/Palette.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.model');
3 |
4 | ns.Palette = function (id, name, colors) {
5 | this.id = id;
6 | this.name = name;
7 | this.colors = colors;
8 | };
9 |
10 | ns.Palette.fromObject = function (paletteObj) {
11 | var colors = paletteObj.colors.slice(0 , paletteObj.colors.length);
12 | return new ns.Palette(paletteObj.id, paletteObj.name, colors);
13 | };
14 |
15 | ns.Palette.prototype.getColors = function () {
16 | return this.colors;
17 | };
18 |
19 | ns.Palette.prototype.setColors = function (colors) {
20 | this.colors = colors;
21 | };
22 |
23 | ns.Palette.prototype.get = function (index) {
24 | return this.colors[index];
25 | };
26 |
27 | ns.Palette.prototype.set = function (index, color) {
28 | this.colors[index] = color;
29 | };
30 |
31 | ns.Palette.prototype.add = function (color) {
32 | this.colors.push(color);
33 | };
34 |
35 | ns.Palette.prototype.size = function () {
36 | return this.colors.length;
37 | };
38 |
39 | ns.Palette.prototype.removeAt = function (index) {
40 | this.colors.splice(index, 1);
41 | };
42 |
43 | ns.Palette.prototype.move = function (oldIndex, newIndex) {
44 | this.colors.splice(newIndex, 0, this.colors.splice(oldIndex, 1)[0]);
45 | };
46 | })();
47 |
--------------------------------------------------------------------------------
/src/js/model/frame/AsyncCachedFrameProcessor.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.model.frame');
3 |
4 | ns.AsyncCachedFrameProcessor = function (cacheResetInterval) {
5 | ns.CachedFrameProcessor.call(this, cacheResetInterval);
6 | };
7 |
8 | pskl.utils.inherit(ns.AsyncCachedFrameProcessor, ns.CachedFrameProcessor);
9 |
10 | /**
11 | * Retrieve the processed frame from the cache, in the (optional) namespace
12 | * If the first level cache is empty, attempt to clone it from 2nd level cache.
13 | * If second level cache is empty process the frame.
14 | * @param {pskl.model.Frame} frame
15 | * @param {String} namespace
16 | * @return {Object} the processed frame
17 | */
18 | ns.AsyncCachedFrameProcessor.prototype.get = function (frame, namespace) {
19 | var processedFrame = null;
20 | namespace = namespace || this.defaultNamespace;
21 |
22 | if (!this.cache_[namespace]) {
23 | this.cache_[namespace] = {};
24 | }
25 |
26 | var deferred = Q.defer();
27 |
28 | var cache = this.cache_[namespace];
29 |
30 | var key1 = frame.getHash();
31 | if (cache[key1]) {
32 | processedFrame = cache[key1];
33 | } else {
34 | var callback = this.onProcessorComplete_.bind(this, deferred, cache, key1);
35 | this.frameProcessor(frame, callback);
36 | }
37 |
38 | if (processedFrame) {
39 | deferred.resolve(processedFrame);
40 | }
41 |
42 | return deferred.promise;
43 | };
44 |
45 | ns.AsyncCachedFrameProcessor.prototype.onProcessorComplete_ = function (deferred, cache, key1, result) {
46 | cache[key1] = result;
47 | deferred.resolve(result);
48 | };
49 | })();
50 |
--------------------------------------------------------------------------------
/src/js/model/piskel/Descriptor.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.model.piskel');
3 |
4 | ns.Descriptor = function (name, description, isPublic) {
5 | this.name = name;
6 | this.description = description;
7 | this.isPublic = isPublic;
8 | };
9 | })();
10 |
--------------------------------------------------------------------------------
/src/js/rendering/AbstractRenderer.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.rendering');
3 |
4 | ns.AbstractRenderer = function () {};
5 |
6 | ns.AbstractRenderer.prototype.clear = Constants.ABSTRACT_FUNCTION;
7 | ns.AbstractRenderer.prototype.render = Constants.ABSTRACT_FUNCTION;
8 |
9 | ns.AbstractRenderer.prototype.getCoordinates = Constants.ABSTRACT_FUNCTION;
10 |
11 | ns.AbstractRenderer.prototype.setGridWidth = Constants.ABSTRACT_FUNCTION;
12 | ns.AbstractRenderer.prototype.getGridWidth = Constants.ABSTRACT_FUNCTION;
13 |
14 | ns.AbstractRenderer.prototype.setZoom = Constants.ABSTRACT_FUNCTION;
15 | ns.AbstractRenderer.prototype.getZoom = Constants.ABSTRACT_FUNCTION;
16 |
17 | ns.AbstractRenderer.prototype.setOffset = Constants.ABSTRACT_FUNCTION;
18 | ns.AbstractRenderer.prototype.getOffset = Constants.ABSTRACT_FUNCTION;
19 |
20 | ns.AbstractRenderer.prototype.setDisplaySize = Constants.ABSTRACT_FUNCTION;
21 | ns.AbstractRenderer.prototype.getDisplaySize = Constants.ABSTRACT_FUNCTION;
22 | })();
23 |
--------------------------------------------------------------------------------
/src/js/rendering/CanvasRenderer.js:
--------------------------------------------------------------------------------
1 | (function () {
2 |
3 | var ns = $.namespace('pskl.rendering');
4 | ns.CanvasRenderer = function (frame, zoom) {
5 | this.frame = frame;
6 | this.zoom = zoom;
7 | this.opacity_ = 1;
8 | this.transparentColor_ = 'white';
9 | };
10 |
11 | /**
12 | * Decide which color should be used to represent transparent pixels
13 | * Default : white
14 | * @param {String} color the color to use either as '#ABCDEF' or 'red' or 'rgb(x,y,z)' or 'rgba(x,y,z,a)'
15 | */
16 | ns.CanvasRenderer.prototype.drawTransparentAs = function (color) {
17 | this.transparentColor_ = color;
18 | };
19 |
20 | ns.CanvasRenderer.prototype.setOpacity = function (opacity) {
21 | this.opacity_ = opacity;
22 | };
23 |
24 | ns.CanvasRenderer.prototype.render = function () {
25 | var canvas = this.createCanvas_();
26 |
27 | // Draw in canvas
28 | pskl.utils.FrameUtils.drawToCanvas(this.frame, canvas, this.transparentColor_, this.opacity_);
29 |
30 | var scaledCanvas = this.createCanvas_(this.zoom);
31 | var scaledContext = scaledCanvas.getContext('2d');
32 | pskl.utils.CanvasUtils.disableImageSmoothing(scaledCanvas);
33 | scaledContext.scale(this.zoom, this.zoom);
34 | scaledContext.drawImage(canvas, 0, 0);
35 |
36 | return scaledCanvas;
37 | };
38 |
39 | ns.CanvasRenderer.prototype.createCanvas_ = function (zoom) {
40 | zoom = zoom || 1;
41 | var width = this.frame.getWidth() * zoom;
42 | var height = this.frame.getHeight() * zoom;
43 | return pskl.utils.CanvasUtils.createCanvas(width, height);
44 | };
45 | })();
46 |
--------------------------------------------------------------------------------
/src/js/rendering/FramesheetRenderer.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.rendering');
3 |
4 | /**
5 | * Render an array of frames
6 | * @param {Array.} frames
7 | */
8 | ns.FramesheetRenderer = function (frames) {
9 | if (frames.length > 0) {
10 | this.frames = frames;
11 | } else {
12 | throw 'FramesheetRenderer : Invalid argument : frames is empty';
13 | }
14 | };
15 |
16 | ns.FramesheetRenderer.prototype.renderAsCanvas = function (columns) {
17 | columns = columns || this.frames.length;
18 | var rows = Math.ceil(this.frames.length / columns);
19 |
20 | var canvas = this.createCanvas_(columns, rows);
21 |
22 | for (var i = 0 ; i < this.frames.length ; i++) {
23 | var frame = this.frames[i];
24 | var posX = (i % columns) * frame.getWidth();
25 | var posY = Math.floor(i / columns) * frame.getHeight();
26 | this.drawFrameInCanvas_(frame, canvas, posX, posY);
27 | }
28 | return canvas;
29 | };
30 |
31 | ns.FramesheetRenderer.prototype.drawFrameInCanvas_ = function (frame, canvas, offsetWidth, offsetHeight) {
32 | var context = canvas.getContext('2d');
33 | var imageData = context.createImageData(frame.getWidth(), frame.getHeight());
34 | var pixels = frame.getPixels();
35 | var data = new Uint8ClampedArray(pixels.buffer);
36 | imageData.data.set(data);
37 | context.putImageData(imageData, offsetWidth, offsetHeight);
38 | };
39 |
40 | ns.FramesheetRenderer.prototype.createCanvas_ = function (columns, rows) {
41 | var sampleFrame = this.frames[0];
42 | var width = columns * sampleFrame.getWidth();
43 | var height = rows * sampleFrame.getHeight();
44 | return pskl.utils.CanvasUtils.createCanvas(width, height);
45 | };
46 | })();
47 |
--------------------------------------------------------------------------------
/src/js/rendering/PiskelRenderer.js:
--------------------------------------------------------------------------------
1 | (function () {
2 |
3 | var ns = $.namespace('pskl.rendering');
4 |
5 | ns.PiskelRenderer = function (piskelController) {
6 | var frames = [];
7 | for (var i = 0 ; i < piskelController.getFrameCount() ; i++) {
8 | frames.push(piskelController.renderFrameAt(i, true));
9 | }
10 | this.piskelController = piskelController;
11 | this.frames = frames;
12 | };
13 |
14 | ns.PiskelRenderer.prototype.renderAsCanvas = function (columns) {
15 | columns = columns || this.frames.length;
16 | var rows = Math.ceil(this.frames.length / columns);
17 |
18 | var canvas = this.createCanvas_(columns, rows);
19 |
20 | for (var i = 0 ; i < this.frames.length ; i++) {
21 | var frame = this.frames[i];
22 | var posX = (i % columns) * this.piskelController.getWidth();
23 | var posY = Math.floor(i / columns) * this.piskelController.getHeight();
24 | this.drawFrameInCanvas_(frame, canvas, posX, posY);
25 | }
26 | return canvas;
27 | };
28 |
29 | ns.PiskelRenderer.prototype.drawFrameInCanvas_ = function (frame, canvas, offsetWidth, offsetHeight) {
30 | var context = canvas.getContext('2d');
31 | context.drawImage(frame, offsetWidth, offsetHeight, frame.width, frame.height);
32 | };
33 |
34 | ns.PiskelRenderer.prototype.createCanvas_ = function (columns, rows) {
35 | var width = columns * this.piskelController.getWidth();
36 | var height = rows * this.piskelController.getHeight();
37 | return pskl.utils.CanvasUtils.createCanvas(width, height);
38 | };
39 | })();
40 |
--------------------------------------------------------------------------------
/src/js/selection/BaseSelection.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.selection');
3 |
4 | ns.BaseSelection = function () {
5 | this.reset();
6 | };
7 |
8 | ns.BaseSelection.prototype.stringify = function () {
9 | return JSON.stringify({
10 | pixels: this.pixels,
11 | time: this.time
12 | });
13 | };
14 |
15 | ns.BaseSelection.prototype.parse = function (str) {
16 | var selectionData = JSON.parse(str);
17 | this.pixels = selectionData.pixels;
18 | this.time = selectionData.time;
19 | };
20 |
21 | ns.BaseSelection.prototype.reset = function () {
22 | this.pixels = [];
23 | this.hasPastedContent = false;
24 | this.time = -1;
25 | };
26 |
27 | ns.BaseSelection.prototype.move = function (colDiff, rowDiff) {
28 | var movedPixels = [];
29 |
30 | for (var i = 0, l = this.pixels.length; i < l; i++) {
31 | var movedPixel = this.pixels[i];
32 | movedPixel.col += colDiff;
33 | movedPixel.row += rowDiff;
34 | movedPixels.push(movedPixel);
35 | }
36 |
37 | this.pixels = movedPixels;
38 | };
39 |
40 | ns.BaseSelection.prototype.fillSelectionFromFrame = function (targetFrame) {
41 | this.pixels.forEach(function (pixel) {
42 | var color = targetFrame.getPixel(pixel.col, pixel.row);
43 | pixel.color = color || Constants.TRANSPARENT_COLOR;
44 | });
45 |
46 | this.hasPastedContent = true;
47 | // Keep track of the selection time to compare between local selection and
48 | // paste event selections.
49 | this.time = Date.now();
50 | };
51 | })();
52 |
--------------------------------------------------------------------------------
/src/js/selection/RectangularSelection.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.selection');
3 |
4 | ns.RectangularSelection = function (x0, y0, x1, y1) {
5 | this.pixels = pskl.PixelUtils.getRectanglePixels(x0, y0, x1, y1);
6 | };
7 |
8 | pskl.utils.inherit(ns.RectangularSelection, ns.BaseSelection);
9 | })();
10 |
--------------------------------------------------------------------------------
/src/js/selection/ShapeSelection.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.selection');
3 |
4 | ns.ShapeSelection = function (pixels) {
5 | this.pixels = pixels;
6 | };
7 |
8 | pskl.utils.inherit(ns.ShapeSelection, ns.BaseSelection);
9 | })();
10 |
--------------------------------------------------------------------------------
/src/js/service/BeforeUnloadService.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.service');
3 |
4 | ns.BeforeUnloadService = function (piskelController) {
5 | this.piskelController = piskelController;
6 | };
7 |
8 | ns.BeforeUnloadService.prototype.init = function () {
9 | if (pskl.utils.Environment.detectNodeWebkit()) {
10 | // Add a dedicated listener to window 'close' event in nwjs environment.
11 | var win = require('nw.gui').Window.get();
12 | win.on('close', this.onNwWindowClose.bind(this, win));
13 | }
14 |
15 | window.addEventListener('beforeunload', this.onBeforeUnload.bind(this));
16 | };
17 |
18 | /**
19 | * In nw.js environment "onbeforeunload" is not triggered when closing the window.
20 | * Polyfill the behavior here.
21 | */
22 | ns.BeforeUnloadService.prototype.onNwWindowClose = function (win) {
23 | var msg = this.onBeforeUnload();
24 | if (msg) {
25 | if (!window.confirm(msg)) {
26 | return false;
27 | }
28 | }
29 | win.close(true);
30 | };
31 |
32 | ns.BeforeUnloadService.prototype.onBeforeUnload = function (evt) {
33 | // Attempt one last backup. Some of it may fail due to the asynchronous
34 | // nature of IndexedDB.
35 | pskl.app.backupService.backup();
36 | if (pskl.app.savedStatusService.isDirty()) {
37 | var confirmationMessage = 'Your current sprite has unsaved changes. Are you sure you want to quit?';
38 |
39 | evt = evt || window.event;
40 | if (evt) {
41 | evt.returnValue = confirmationMessage;
42 | }
43 | return confirmationMessage;
44 | }
45 | };
46 |
47 | })();
48 |
--------------------------------------------------------------------------------
/src/js/service/ClipboardService.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.service');
3 |
4 | ns.ClipboardService = function (piskelController) {
5 | this.piskelController = piskelController;
6 | };
7 |
8 | ns.ClipboardService.prototype.init = function () {
9 | window.addEventListener('copy', this._onCopy.bind(this), true);
10 | window.addEventListener('cut', this._onCut.bind(this), true);
11 | window.addEventListener('paste', this._onPaste.bind(this), true);
12 | };
13 |
14 | ns.ClipboardService.prototype._onCut = function (event) {
15 | $.publish(Events.CLIPBOARD_CUT, event);
16 | };
17 |
18 | ns.ClipboardService.prototype._onCopy = function (event) {
19 | $.publish(Events.CLIPBOARD_COPY, event);
20 | };
21 |
22 | ns.ClipboardService.prototype._onPaste = function (event) {
23 | $.publish(Events.CLIPBOARD_PASTE, event);
24 | };
25 | })();
26 |
--------------------------------------------------------------------------------
/src/js/service/ImageUploadService.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.service');
3 | ns.ImageUploadService = function () {};
4 | ns.ImageUploadService.prototype.init = function () {};
5 |
6 | /**
7 | * Upload a base64 image data to distant service.
8 | * If successful, will call provided callback with the image URL as first argument;
9 | * @param {String} imageData base64 image data (such as the return value of canvas.toDataUrl())
10 | * @param {Function} success success callback. 1st argument will be the uploaded image URL
11 | * @param {Function} error error callback
12 | */
13 | ns.ImageUploadService.prototype.upload = function (imageData, success, error) {
14 | var data = {
15 | data : imageData
16 | };
17 |
18 | var protocol = pskl.utils.Environment.isHttps() ? 'https' : 'http';
19 | var wrappedSuccess = function (response) {
20 | var getUrl = pskl.utils.Template.replace(Constants.IMAGE_SERVICE_GET_URL, {
21 | protocol: protocol
22 | });
23 | success(getUrl + response.responseText);
24 | };
25 |
26 | var uploadUrl = pskl.utils.Template.replace(Constants.IMAGE_SERVICE_UPLOAD_URL, {
27 | protocol: protocol
28 | });
29 | pskl.utils.Xhr.post(uploadUrl, data, wrappedSuccess, error);
30 | };
31 | })();
32 |
--------------------------------------------------------------------------------
/src/js/service/MouseStateService.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.service');
3 |
4 | var BUTTON_UNSET = null;
5 |
6 | /**
7 | * This service exists mostly due to a FF/IE bug.
8 | * For mousemove events, the button type is set to 0 (e.g. left button type) whatever was the
9 | * pressed button on mousedown. We use this service to cache the button type value on mousedown
10 | * and make it available to mousemove events.
11 | */
12 | ns.MouseStateService = function () {
13 | this.lastButtonPressed_ = BUTTON_UNSET;
14 | };
15 |
16 | ns.MouseStateService.prototype.init = function () {
17 | $.subscribe(Events.MOUSE_EVENT, this.onMouseEvent_.bind(this));
18 | };
19 |
20 | ns.MouseStateService.prototype.isLeftButtonPressed = function () {
21 | return this.isMouseButtonPressed_(Constants.LEFT_BUTTON);
22 | };
23 |
24 | ns.MouseStateService.prototype.isRightButtonPressed = function () {
25 | return this.isMouseButtonPressed_(Constants.RIGHT_BUTTON);
26 | };
27 |
28 | ns.MouseStateService.prototype.isMiddleButtonPressed = function () {
29 | return this.isMouseButtonPressed_(Constants.MIDDLE_BUTTON);
30 | };
31 |
32 | ns.MouseStateService.prototype.isMouseButtonPressed_ = function (mouseButton) {
33 | return this.lastButtonPressed_ != BUTTON_UNSET && this.lastButtonPressed_ == mouseButton;
34 | };
35 |
36 | ns.MouseStateService.prototype.onMouseEvent_ = function(evt, mouseEvent) {
37 | if (mouseEvent.type == 'mousedown') {
38 | this.lastButtonPressed_ = mouseEvent.button;
39 | } else if (mouseEvent.type == 'mouseup') {
40 | this.lastButtonPressed_ = BUTTON_UNSET;
41 | }
42 | };
43 | })();
44 |
--------------------------------------------------------------------------------
/src/js/service/SavedStatusService.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.service');
3 |
4 | ns.SavedStatusService = function (piskelController, historyService) {
5 | this.piskelController = piskelController;
6 | this.historyService = historyService;
7 | this.lastSavedStateIndex = '';
8 |
9 | this.publishStatusUpdateEvent_ = this.publishStatusUpdateEvent_.bind(this);
10 | };
11 |
12 | ns.SavedStatusService.prototype.init = function () {
13 | $.subscribe(Events.TOOL_RELEASED, this.publishStatusUpdateEvent_);
14 | $.subscribe(Events.PISKEL_RESET, this.publishStatusUpdateEvent_);
15 | $.subscribe(Events.PISKEL_SAVED, this.onPiskelSaved.bind(this));
16 | this.lastSavedStateIndex = this.historyService.getCurrentStateId();
17 | };
18 |
19 | ns.SavedStatusService.prototype.onPiskelSaved = function () {
20 | this.lastSavedStateIndex = this.historyService.getCurrentStateId();
21 | this.publishStatusUpdateEvent_();
22 | };
23 |
24 | ns.SavedStatusService.prototype.publishStatusUpdateEvent_ = function () {
25 | $.publish(Events.PISKEL_SAVED_STATUS_UPDATE);
26 | };
27 |
28 | ns.SavedStatusService.prototype.isDirty = function () {
29 | return (this.lastSavedStateIndex != this.historyService.getCurrentStateId());
30 | };
31 | })();
32 |
--------------------------------------------------------------------------------
/src/js/service/SelectedColorsService.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.service');
3 |
4 | ns.SelectedColorsService = function () {
5 | this.primaryColor_ = Constants.DEFAULT_PEN_COLOR;
6 | this.secondaryColor_ = Constants.TRANSPARENT_COLOR;
7 | };
8 |
9 | ns.SelectedColorsService.prototype.init = function () {
10 | $.subscribe(Events.PRIMARY_COLOR_SELECTED, this.onPrimaryColorUpdate_.bind(this));
11 | $.subscribe(Events.SECONDARY_COLOR_SELECTED, this.onSecondaryColorUpdate_.bind(this));
12 | };
13 |
14 | ns.SelectedColorsService.prototype.getPrimaryColor = function () {
15 | return this.primaryColor_;
16 | };
17 |
18 | ns.SelectedColorsService.prototype.getSecondaryColor = function () {
19 | return this.secondaryColor_;
20 | };
21 |
22 | ns.SelectedColorsService.prototype.onPrimaryColorUpdate_ = function (evt, color) {
23 | this.primaryColor_ = color;
24 | };
25 |
26 | ns.SelectedColorsService.prototype.onSecondaryColorUpdate_ = function (evt, color) {
27 | this.secondaryColor_ = color;
28 | };
29 | })();
30 |
--------------------------------------------------------------------------------
/src/js/service/keyboard/KeycodeTranslator.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var specialKeys = {
3 | 8 : 'back',
4 | 13 : 'enter',
5 | 27 : 'esc',
6 | 32 : 'space',
7 | 37 : 'left',
8 | 38 : 'up',
9 | 39 : 'right',
10 | 40 : 'down',
11 | 46 : 'del',
12 | 189 : '-',
13 | // 109 for numpad -
14 | 109 : '-',
15 | // 173 on Firefox for minus key
16 | 173 : '-',
17 | 187 : '+',
18 | // 107 for numpad +
19 | 107 : '+',
20 | // 61 on Firefox for =/+ key
21 | 61 : '+',
22 | 188 : '<',
23 | 190 : '>',
24 | 191 : '?',
25 | 219 : '[',
26 | 221 : ']'
27 | };
28 |
29 | var ns = $.namespace('pskl.service.keyboard');
30 |
31 | ns.KeycodeTranslator = {
32 | toChar : function (keycode) {
33 | if (keycode >= 48 && keycode <= 57) {
34 | // key is 0-9
35 | return (keycode - 48) + '';
36 | } else if (keycode >= 96 && keycode <= 105) {
37 | // key is numpad 0-9
38 | return (keycode - 96) + '';
39 | } else if (keycode >= 65 && keycode <= 90) {
40 | // key is a-z, use base 36 to get the string representation
41 | return (keycode - 65 + 10).toString(36);
42 | } else {
43 | return specialKeys[keycode];
44 | }
45 | }
46 | };
47 | })();
48 |
--------------------------------------------------------------------------------
/src/js/service/palette/CurrentColorsPalette.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.service.palette');
3 |
4 | ns.CurrentColorsPalette = function () {
5 | this.name = 'Current colors';
6 | this.id = Constants.CURRENT_COLORS_PALETTE_ID;
7 | this.colorSorter = new pskl.service.color.ColorSorter();
8 | };
9 |
10 | ns.CurrentColorsPalette.prototype.getColors = function () {
11 | var currentColors = pskl.app.currentColorsService.getCurrentColors();
12 | currentColors = currentColors.slice(0, Constants.MAX_PALETTE_COLORS);
13 | return this.colorSorter.sort(currentColors);
14 | };
15 | })();
16 |
--------------------------------------------------------------------------------
/src/js/service/palette/PaletteGplWriter.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.service.palette');
3 |
4 | ns.PaletteGplWriter = function (palette) {
5 | this.palette = palette;
6 | };
7 |
8 | ns.PaletteGplWriter.prototype.write = function () {
9 | var lines = [];
10 | lines.push('GIMP Palette');
11 | lines.push('Name: ' + this.palette.name);
12 | lines.push('Columns: 0');
13 | lines.push('#');
14 | this.palette.getColors().forEach(function (color) {
15 | lines.push(this.writeColorLine(color));
16 | }.bind(this));
17 | lines.push('\r\n');
18 |
19 | return lines.join('\r\n');
20 | };
21 |
22 | ns.PaletteGplWriter.prototype.writeColorLine = function (color) {
23 | var tinycolor = window.tinycolor(color);
24 | var rgb = tinycolor.toRgb();
25 | var strBuffer = [];
26 | strBuffer.push(this.padString(rgb.r, 3));
27 | strBuffer.push(this.padString(rgb.g, 3));
28 | strBuffer.push(this.padString(rgb.b, 3));
29 | strBuffer.push('Untitled');
30 |
31 | return strBuffer.join(' ');
32 | };
33 |
34 | ns.PaletteGplWriter.prototype.padString = function (str, size) {
35 | str = str.toString();
36 | var pad = (new Array(1 + size - str.length)).join(' ');
37 | return pad + str;
38 | };
39 |
40 | })();
41 |
--------------------------------------------------------------------------------
/src/js/service/palette/PaletteImportService.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.service.palette');
3 |
4 | var fileReaders = {
5 | 'gpl' : ns.reader.PaletteGplReader,
6 | 'pal' : ns.reader.PalettePalReader,
7 | 'txt' : ns.reader.PaletteTxtReader,
8 | 'img' : ns.reader.PaletteImageReader
9 | };
10 |
11 | ns.PaletteImportService = function () {};
12 | ns.PaletteImportService.prototype.init = function () {};
13 |
14 | ns.PaletteImportService.prototype.read = function (file, onSuccess, onError) {
15 | var reader = this.getReader_(file, onSuccess, onError);
16 | if (reader) {
17 | reader.read();
18 | } else {
19 | console.error('Could not find reader for file : %s', file.name);
20 | }
21 | };
22 |
23 | ns.PaletteImportService.prototype.isImage_ = function (file) {
24 | return file.type.indexOf('image') === 0;
25 | };
26 |
27 | ns.PaletteImportService.prototype.getReader_ = function (file, onSuccess, onError) {
28 | var ReaderClass = this.getReaderClass_(file);
29 | if (ReaderClass) {
30 | return new ReaderClass(file, onSuccess, onError);
31 | } else {
32 | return null;
33 | }
34 | };
35 |
36 | ns.PaletteImportService.prototype.getReaderClass_ = function (file) {
37 | var ReaderClass;
38 | if (this.isImage_(file)) {
39 | ReaderClass = fileReaders.img;
40 | } else {
41 | var extension = this.getExtension_(file);
42 | ReaderClass = fileReaders[extension];
43 | }
44 | return ReaderClass;
45 | };
46 |
47 | ns.PaletteImportService.prototype.getExtension_ = function (file) {
48 | var parts = file.name.split('.');
49 | var extension = parts[parts.length - 1];
50 | return extension.toLowerCase();
51 | };
52 | })();
53 |
--------------------------------------------------------------------------------
/src/js/service/palette/reader/AbstractPaletteFileReader.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.service.palette.reader');
3 |
4 | ns.AbstractPaletteFileReader = function (file, onSuccess, onError, colorLineRegexp) {
5 | this.file = file;
6 | this.onSuccess = onSuccess;
7 | this.onError = onError;
8 | this.colorLineRegexp = colorLineRegexp;
9 | };
10 |
11 | ns.AbstractPaletteFileReader.prototype.extractColorFromLine = Constants.ABSTRACT_FUNCTION;
12 |
13 | ns.AbstractPaletteFileReader.prototype.read = function () {
14 | pskl.utils.FileUtils.readFile(this.file, this.onFileLoaded_.bind(this));
15 | };
16 |
17 | ns.AbstractPaletteFileReader.prototype.onFileLoaded_ = function (content) {
18 | var text = pskl.utils.Base64.toText(content);
19 | var lines = text.match(/[^\r\n]+/g);
20 |
21 | var colorLines = lines.filter(function (l) {
22 | return this.colorLineRegexp.test(l);
23 | }.bind(this));
24 |
25 | var colors = colorLines.map(this.extractColorFromLine.bind(this));
26 |
27 | if (colors.length) {
28 | var uuid = pskl.utils.Uuid.generate();
29 | var palette = new pskl.model.Palette(uuid, this.file.name, colors);
30 | this.onSuccess(palette);
31 | } else {
32 | this.onError();
33 | }
34 | };
35 | })();
36 |
--------------------------------------------------------------------------------
/src/js/service/palette/reader/PaletteGplReader.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.service.palette.reader');
3 |
4 | var RE_COLOR_LINE = /^(\s*\d{1,3})(\s*\d{1,3})(\s*\d{1,3})/;
5 | var RE_EXTRACT_NAME = /^name\s*\:\s*(.*)$/i;
6 |
7 | ns.PaletteGplReader = function (file, onSuccess, onError) {
8 | this.superclass.constructor.call(this, file, onSuccess, onError, RE_COLOR_LINE);
9 | };
10 |
11 | pskl.utils.inherit(ns.PaletteGplReader, ns.AbstractPaletteFileReader);
12 |
13 | ns.PaletteGplReader.prototype.extractColorFromLine = function (line) {
14 | var matches = line.match(RE_COLOR_LINE);
15 | var color = window.tinycolor({
16 | r : parseInt(matches[1], 10),
17 | g : parseInt(matches[2], 10),
18 | b : parseInt(matches[3], 10)
19 | });
20 |
21 | return color.toHexString();
22 | };
23 | })();
24 |
--------------------------------------------------------------------------------
/src/js/service/palette/reader/PalettePalReader.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.service.palette.reader');
3 |
4 | var RE_COLOR_LINE = /^(\d{1,3})\s+(\d{1,3})\s+(\d{1,3})/;
5 |
6 | ns.PalettePalReader = function (file, onSuccess, onError) {
7 | this.superclass.constructor.call(this, file, onSuccess, onError, RE_COLOR_LINE);
8 | };
9 |
10 | pskl.utils.inherit(ns.PalettePalReader, ns.AbstractPaletteFileReader);
11 |
12 | ns.PalettePalReader.prototype.extractColorFromLine = function (line) {
13 | var matches = line.match(RE_COLOR_LINE);
14 | var rgbColor = 'rgb(' + matches[1] + ',' + matches[2] + ',' + matches[3] + ')';
15 | var color = window.tinycolor(rgbColor);
16 |
17 | return color.toHexString();
18 | };
19 | })();
20 |
--------------------------------------------------------------------------------
/src/js/service/palette/reader/PaletteTxtReader.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.service.palette.reader');
3 |
4 | var RE_COLOR_LINE = /^[A-F0-9]{2}([A-F0-9]{2})([A-F0-9]{2})([A-F0-9]{2})/;
5 |
6 | ns.PaletteTxtReader = function (file, onSuccess, onError) {
7 | this.superclass.constructor.call(this, file, onSuccess, onError, RE_COLOR_LINE);
8 | };
9 |
10 | pskl.utils.inherit(ns.PaletteTxtReader, ns.AbstractPaletteFileReader);
11 |
12 | ns.PaletteTxtReader.prototype.extractColorFromLine = function (line) {
13 | var matches = line.match(RE_COLOR_LINE);
14 | var color = '#' + matches[1] + matches[2] + matches[3];
15 | return color.toLowerCase();
16 | };
17 | })();
18 |
--------------------------------------------------------------------------------
/src/js/service/pensize/PenSizeService.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.service.pensize');
3 |
4 | var MIN_PENSIZE = 1;
5 | var MAX_PENSIZE = 32;
6 |
7 | /**
8 | * Service to retrieve and modify the current pen size.
9 | */
10 | ns.PenSizeService = function () {
11 | this.size = MIN_PENSIZE;
12 | };
13 |
14 | ns.PenSizeService.prototype.init = function () {
15 | this.size = pskl.UserSettings.get(pskl.UserSettings.PEN_SIZE);
16 |
17 | var shortcuts = pskl.service.keyboard.Shortcuts;
18 | pskl.app.shortcutService.registerShortcut(shortcuts.MISC.INCREASE_PENSIZE, this.increasePenSize_.bind(this));
19 | pskl.app.shortcutService.registerShortcut(shortcuts.MISC.DECREASE_PENSIZE, this.decreasePenSize_.bind(this));
20 | };
21 |
22 | ns.PenSizeService.prototype.increasePenSize_ = function () {
23 | this.setPenSize(this.size + 1);
24 | };
25 |
26 | ns.PenSizeService.prototype.decreasePenSize_ = function () {
27 | this.setPenSize(this.size - 1);
28 | };
29 |
30 | ns.PenSizeService.prototype.getPenSize = function () {
31 | return this.size;
32 | };
33 |
34 | ns.PenSizeService.prototype.setPenSize = function (size) {
35 | if (this.isPenSizeValid_(size) && size != this.size) {
36 | this.size = size;
37 | pskl.UserSettings.set(pskl.UserSettings.PEN_SIZE, size);
38 | $.publish(Events.PEN_SIZE_CHANGED);
39 | }
40 | };
41 |
42 | ns.PenSizeService.prototype.isPenSizeValid_ = function (size) {
43 | if (isNaN(size)) {
44 | return false;
45 | }
46 |
47 | return size >= MIN_PENSIZE && size <= MAX_PENSIZE;
48 | };
49 |
50 | })();
51 |
--------------------------------------------------------------------------------
/src/js/service/performance/PerformanceReportService.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.service.performance');
3 |
4 | ns.PerformanceReportService = function (piskelController, currentColorsService) {
5 | this.piskelController = piskelController;
6 | this.currentColorsService = currentColorsService;
7 |
8 | this.currentReport = null;
9 | };
10 |
11 | ns.PerformanceReportService.prototype.init = function () {
12 | $.subscribe(Events.HISTORY_STATE_SAVED, this.createReport_.bind(this));
13 | };
14 |
15 | ns.PerformanceReportService.prototype.createReport_ = function () {
16 | var report = new ns.PerformanceReport(
17 | this.piskelController.getPiskel(),
18 | this.currentColorsService.getCurrentColors().length);
19 |
20 | if (!report.equals(this.currentReport)) {
21 | $.publish(Events.PERFORMANCE_REPORT_CHANGED, [report]);
22 | this.currentReport = report;
23 | }
24 | };
25 |
26 | ns.PerformanceReportService.prototype.hasProblem = function () {
27 | return this.currentReport && this.currentReport.hasProblem();
28 | };
29 | })();
30 |
--------------------------------------------------------------------------------
/src/js/service/storage/FileDownloadStorageService.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.service.storage');
3 |
4 | ns.FileDownloadStorageService = function () {};
5 | ns.FileDownloadStorageService.prototype.init = function () {};
6 |
7 | ns.FileDownloadStorageService.prototype.save = function (piskel) {
8 | var serialized = pskl.utils.serialization.Serializer.serialize(piskel);
9 | var deferred = Q.defer();
10 |
11 | pskl.utils.BlobUtils.stringToBlob(serialized, function(blob) {
12 | var piskelName = piskel.getDescriptor().name;
13 | var timestamp = pskl.utils.DateUtils.format(new Date(), '{{Y}}{{M}}{{D}}-{{H}}{{m}}{{s}}');
14 | var fileName = piskelName + '-' + timestamp + '.piskel';
15 |
16 | try {
17 | pskl.utils.FileUtils.downloadAsFile(blob, fileName);
18 | deferred.resolve();
19 | } catch (e) {
20 | deferred.reject(e.message);
21 | }
22 | }.bind(this), 'application/piskel+json');
23 |
24 | return deferred.promise;
25 | };
26 |
27 | })();
28 |
--------------------------------------------------------------------------------
/src/js/snippets.js:
--------------------------------------------------------------------------------
1 | (function () {})();
2 |
--------------------------------------------------------------------------------
/src/js/tools/Tool.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.tools');
3 |
4 | ns.Tool = function () {
5 | this.toolId = 'tool';
6 | this.helpText = 'Abstract tool';
7 | this.tooltipDescriptors = [];
8 | };
9 |
10 | ns.Tool.prototype.getHelpText = function() {
11 | return this.helpText;
12 | };
13 |
14 | ns.Tool.prototype.getId = function() {
15 | return this.toolId;
16 | };
17 |
18 | ns.Tool.prototype.raiseSaveStateEvent = function (replayData) {
19 | $.publish(Events.PISKEL_SAVE_STATE, {
20 | type : pskl.service.HistoryService.REPLAY,
21 | scope : this,
22 | replay : replayData
23 | });
24 | };
25 | })();
26 |
--------------------------------------------------------------------------------
/src/js/tools/ToolIconBuilder.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.tools');
3 |
4 | ns.ToolIconBuilder = function () {};
5 |
6 | ns.ToolIconBuilder.prototype.createIcon = function (tool, tooltipPosition) {
7 | tooltipPosition = tooltipPosition || 'right';
8 | var tpl = pskl.utils.Template.get('drawingTool-item-template');
9 | return pskl.utils.Template.replace(tpl, {
10 | cssclass : ['tool-icon', 'icon-' + tool.toolId].join(' '),
11 | toolid : tool.toolId,
12 | title : this.getTooltipText(tool),
13 | tooltipposition : tooltipPosition
14 | });
15 | };
16 |
17 | ns.ToolIconBuilder.prototype.getTooltipText = function(tool) {
18 | var descriptors = tool.tooltipDescriptors;
19 | return pskl.utils.TooltipFormatter.format(tool.getHelpText(), tool.shortcut, descriptors);
20 | };
21 | })();
22 |
--------------------------------------------------------------------------------
/src/js/tools/ToolsHelper.js:
--------------------------------------------------------------------------------
1 | var ns = $.namespace('pskl.tools');
2 |
3 | ns.ToolsHelper = {
4 | /**
5 | * Retrieve a list of frames containing either :
6 | * - only the current frame (useAllLayers = false, useAllFrames = false)
7 | * - only the frames of the current layer (useAllLayers = false, useAllFrames = true)
8 | * - only the frames at the currentIndex in each layer (useAllLayers = true, useAllFrames = false)
9 | * - all frames (useAllLayers = true, useAllFrames = true)
10 | *
11 | * @param {Boolean} useAllLayers true if frames from all layers should be returned
12 | * @param {Boolean} useAllFrames true if frames at any index should be returned
13 | * @return {Array[Frame]} list of Frame instances, can be empty
14 | */
15 | getTargetFrames : function (useAllLayers, useAllFrames) {
16 | var currentFrameIndex = pskl.app.piskelController.getCurrentFrameIndex();
17 | var layers = useAllLayers ? pskl.app.piskelController.getLayers() : [pskl.app.piskelController.getCurrentLayer()];
18 | return layers.reduce(function (previous, layer) {
19 | var frames = useAllFrames ? layer.getFrames() : [layer.getFrameAt(currentFrameIndex)];
20 | return previous.concat(frames);
21 | }, []);
22 | }
23 | };
24 |
--------------------------------------------------------------------------------
/src/js/tools/drawing/ColorPicker.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @provide pskl.tools.drawing.ColorPicker
3 | *
4 | * @require pskl.utils
5 | */
6 | (function() {
7 | var ns = $.namespace('pskl.tools.drawing');
8 |
9 | ns.ColorPicker = function() {
10 | this.toolId = 'tool-colorpicker';
11 | this.helpText = 'Color picker';
12 | this.shortcut = pskl.service.keyboard.Shortcuts.TOOL.COLORPICKER;
13 | };
14 |
15 | pskl.utils.inherit(ns.ColorPicker, ns.BaseTool);
16 |
17 | /**
18 | * @override
19 | */
20 | ns.ColorPicker.prototype.applyToolAt = function(col, row, frame, overlay, event) {
21 | if (frame.containsPixel(col, row)) {
22 | var sampledColor = pskl.utils.intToColor(frame.getPixel(col, row));
23 | if (pskl.app.mouseStateService.isLeftButtonPressed()) {
24 | $.publish(Events.SELECT_PRIMARY_COLOR, [sampledColor]);
25 | } else if (pskl.app.mouseStateService.isRightButtonPressed()) {
26 | $.publish(Events.SELECT_SECONDARY_COLOR, [sampledColor]);
27 | }
28 | }
29 | };
30 | })();
31 |
--------------------------------------------------------------------------------
/src/js/tools/drawing/DitheringTool.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @provide pskl.tools.drawing.DitheringTool
3 | *
4 | * @require pskl.utils
5 | */
6 | (function() {
7 | var ns = $.namespace('pskl.tools.drawing');
8 |
9 | ns.DitheringTool = function() {
10 | ns.SimplePen.call(this);
11 | this.toolId = 'tool-dithering';
12 | this.helpText = 'Dithering tool';
13 | this.shortcut = pskl.service.keyboard.Shortcuts.TOOL.DITHERING;
14 | };
15 |
16 | pskl.utils.inherit(ns.DitheringTool, ns.SimplePen);
17 |
18 | ns.DitheringTool.prototype.supportsDynamicPenSize = function() {
19 | return true;
20 | };
21 |
22 | /**
23 | * @override
24 | */
25 | ns.DitheringTool.prototype.applyToolAt = function(col, row, frame, overlay, event) {
26 | this.previousCol = col;
27 | this.previousRow = row;
28 |
29 | var penSize = pskl.app.penSizeService.getPenSize();
30 | var points = pskl.PixelUtils.resizePixel(col, row, penSize);
31 | points.forEach(function (point) {
32 | this.applyToolOnPixel(point[0], point[1], frame, overlay, event);
33 | }.bind(this));
34 | };
35 |
36 | ns.DitheringTool.prototype.applyToolOnPixel = function(col, row, frame, overlay, event) {
37 | var usePrimaryColor = (col + row) % 2;
38 |
39 | if (pskl.app.mouseStateService.isRightButtonPressed()) {
40 | usePrimaryColor = !usePrimaryColor;
41 | }
42 |
43 | var ditheringColor = usePrimaryColor ?
44 | pskl.app.selectedColorsService.getPrimaryColor() :
45 | pskl.app.selectedColorsService.getSecondaryColor();
46 |
47 | this.draw(ditheringColor, col, row, frame, overlay);
48 | };
49 |
50 | })();
51 |
--------------------------------------------------------------------------------
/src/js/tools/drawing/Eraser.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @provide pskl.tools.drawing.Eraser
3 | *
4 | * @require Constants
5 | * @require pskl.utils
6 | */
7 | (function() {
8 | var ns = $.namespace('pskl.tools.drawing');
9 |
10 | ns.Eraser = function() {
11 | this.superclass.constructor.call(this);
12 |
13 | this.toolId = 'tool-eraser';
14 | this.helpText = 'Eraser tool';
15 | this.shortcut = pskl.service.keyboard.Shortcuts.TOOL.ERASER;
16 | };
17 |
18 | pskl.utils.inherit(ns.Eraser, ns.SimplePen);
19 |
20 | /**
21 | * @override
22 | */
23 | ns.Eraser.prototype.getToolColor = function() {
24 | return Constants.TRANSPARENT_COLOR;
25 | };
26 | })();
27 |
--------------------------------------------------------------------------------
/src/js/tools/drawing/PaintBucket.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @provide pskl.tools.drawing.PaintBucket
3 | *
4 | * @require pskl.utils
5 | */
6 | (function() {
7 | var ns = $.namespace('pskl.tools.drawing');
8 |
9 | ns.PaintBucket = function() {
10 | this.toolId = 'tool-paint-bucket';
11 | this.helpText = 'Paint bucket tool';
12 | this.shortcut = pskl.service.keyboard.Shortcuts.TOOL.PAINT_BUCKET;
13 | };
14 |
15 | pskl.utils.inherit(ns.PaintBucket, ns.BaseTool);
16 |
17 | /**
18 | * @override
19 | */
20 | ns.PaintBucket.prototype.applyToolAt = function(col, row, frame, overlay, event) {
21 | var color = this.getToolColor();
22 | pskl.PixelUtils.paintSimilarConnectedPixelsFromFrame(frame, col, row, color);
23 |
24 | this.raiseSaveStateEvent({
25 | col : col,
26 | row : row,
27 | color : color
28 | });
29 | };
30 |
31 | ns.PaintBucket.prototype.replay = function (frame, replayData) {
32 | pskl.PixelUtils.paintSimilarConnectedPixelsFromFrame(frame, replayData.col, replayData.row, replayData.color);
33 | };
34 | })();
35 |
--------------------------------------------------------------------------------
/src/js/tools/drawing/Rectangle.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @provide pskl.tools.drawing.Rectangle
3 | *
4 | * @require pskl.utils
5 | */
6 | (function() {
7 | var ns = $.namespace('pskl.tools.drawing');
8 |
9 | ns.Rectangle = function() {
10 | ns.ShapeTool.call(this);
11 |
12 | this.toolId = 'tool-rectangle';
13 | this.helpText = 'Rectangle tool';
14 | this.shortcut = pskl.service.keyboard.Shortcuts.TOOL.RECTANGLE;
15 | };
16 |
17 | pskl.utils.inherit(ns.Rectangle, ns.ShapeTool);
18 |
19 | /**
20 | * @override
21 | */
22 | ns.Rectangle.prototype.draw = function (col, row, color, targetFrame, penSize) {
23 | var rectangle = pskl.PixelUtils.getOrderedRectangleCoordinates(this.startCol, this.startRow, col, row);
24 |
25 | for (var x = rectangle.x0; x <= rectangle.x1; x++) {
26 | for (var y = rectangle.y0; y <= rectangle.y1; y++) {
27 | if (
28 | x > rectangle.x1 - penSize ||
29 | x < rectangle.x0 + penSize ||
30 | y > rectangle.y1 - penSize ||
31 | y < rectangle.y0 + penSize
32 | ) {
33 | targetFrame.setPixel(x, y, color);
34 | }
35 | }
36 | }
37 | };
38 | })();
39 |
--------------------------------------------------------------------------------
/src/js/tools/drawing/selection/RectangleSelect.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @provide pskl.tools.drawing.selection.RectangleSelect
3 | *
4 | * @require pskl.utils
5 | */
6 | (function() {
7 | var ns = $.namespace('pskl.tools.drawing.selection');
8 |
9 | ns.RectangleSelect = function() {
10 | ns.AbstractDragSelect.call(this);
11 |
12 | this.toolId = 'tool-rectangle-select';
13 | this.helpText = 'Rectangle selection';
14 | this.shortcut = pskl.service.keyboard.Shortcuts.TOOL.RECTANGLE_SELECT;
15 |
16 | };
17 |
18 | pskl.utils.inherit(ns.RectangleSelect, ns.AbstractDragSelect);
19 |
20 | /** @override */
21 | ns.RectangleSelect.prototype.onDragSelectStart_ = function (col, row) {
22 | $.publish(Events.DRAG_START, [col, row]);
23 | };
24 |
25 | /**
26 | * When creating the rectangle selection, we clear the current overlayFrame and
27 | * redraw the current rectangle based on the origin coordinate and
28 | * the current mouse coordinate in sprite.
29 | * @override
30 | */
31 | ns.RectangleSelect.prototype.onDragSelect_ = function (col, row, frame, overlay) {
32 | overlay.clear();
33 | this.selection = new pskl.selection.RectangularSelection(this.startCol, this.startRow, col, row);
34 | $.publish(Events.SELECTION_CREATED, [this.selection]);
35 | this.drawSelectionOnOverlay_(overlay);
36 | };
37 |
38 | /** @override */
39 | ns.RectangleSelect.prototype.onDragSelectEnd_ = function (col, row, frame, overlay) {
40 | this.onSelect_(col, row, frame, overlay);
41 | $.publish(Events.DRAG_END);
42 | };
43 |
44 | })();
45 |
--------------------------------------------------------------------------------
/src/js/tools/drawing/selection/ShapeSelect.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @provide pskl.tools.drawing.selection.ShapeSelect
3 | *
4 | * @require pskl.utils
5 | */
6 | (function() {
7 | var ns = $.namespace('pskl.tools.drawing.selection');
8 |
9 | ns.ShapeSelect = function() {
10 | ns.BaseSelect.call(this);
11 |
12 | this.toolId = 'tool-shape-select';
13 | this.helpText = 'Shape selection';
14 | this.shortcut = pskl.service.keyboard.Shortcuts.TOOL.SHAPE_SELECT;
15 | };
16 |
17 | pskl.utils.inherit(ns.ShapeSelect, ns.BaseSelect);
18 |
19 | /**
20 | * For the shape select tool, you just need to click one time to create a selection.
21 | * So we just need to implement onSelectStart_ (no need for onSelect_ & onSelectEnd_)
22 | * @override
23 | */
24 | ns.ShapeSelect.prototype.onSelectStart_ = function (col, row, frame, overlay) {
25 | if (this.hasSelection) {
26 | this.hasSelection = false;
27 | this.commitSelection();
28 | } else {
29 | this.hasSelection = true;
30 | // From the pixel clicked, get shape using an algorithm similar to the paintbucket one:
31 | var pixels = pskl.PixelUtils.getSimilarConnectedPixelsFromFrame(frame, col, row);
32 | this.selection = new pskl.selection.ShapeSelection(pixels);
33 |
34 | $.publish(Events.SELECTION_CREATED, [this.selection]);
35 | this.drawSelectionOnOverlay_(overlay);
36 | }
37 | };
38 |
39 | })();
40 |
--------------------------------------------------------------------------------
/src/js/tools/transform/AbstractTransformTool.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.tools.transform');
3 |
4 | ns.AbstractTransformTool = function () {};
5 |
6 | pskl.utils.inherit(ns.AbstractTransformTool, pskl.tools.Tool);
7 |
8 | ns.AbstractTransformTool.prototype.applyTransformation = function (evt) {
9 | var allFrames = evt.shiftKey;
10 | var allLayers = pskl.utils.UserAgent.isMac ? evt.metaKey : evt.ctrlKey;
11 |
12 | this.applyTool_(evt.altKey, allFrames, allLayers);
13 |
14 | $.publish(Events.PISKEL_RESET);
15 |
16 | this.raiseSaveStateEvent({
17 | altKey : evt.altKey,
18 | allFrames : allFrames,
19 | allLayers : allLayers
20 | });
21 | };
22 |
23 | ns.AbstractTransformTool.prototype.applyTool_ = function (altKey, allFrames, allLayers) {
24 | var currentFrameIndex = pskl.app.piskelController.getCurrentFrameIndex();
25 | var layers = allLayers ? pskl.app.piskelController.getLayers() : [pskl.app.piskelController.getCurrentLayer()];
26 | layers.forEach(function (layer) {
27 | var frames = allFrames ? layer.getFrames() : [layer.getFrameAt(currentFrameIndex)];
28 | frames.forEach(function (frame) {
29 | this.applyToolOnFrame_(frame, altKey);
30 | }.bind(this));
31 | }.bind(this));
32 | };
33 |
34 | ns.AbstractTransformTool.prototype.replay = function (frame, replayData) {
35 | this.applyTool_(replayData.altKey, replayData.allFrames, replayData.allLayers);
36 | };
37 |
38 | })();
39 |
--------------------------------------------------------------------------------
/src/js/tools/transform/Center.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.tools.transform');
3 |
4 | ns.Center = function () {
5 | this.toolId = 'tool-center';
6 | this.helpText = 'Align image to the center';
7 | this.tooltipDescriptors = [
8 | {key : 'ctrl', description : 'Apply to all layers'},
9 | {key : 'shift', description : 'Apply to all frames'}
10 | ];
11 | };
12 |
13 | pskl.utils.inherit(ns.Center, ns.AbstractTransformTool);
14 |
15 | ns.Center.prototype.applyToolOnFrame_ = function (frame) {
16 | ns.TransformUtils.center(frame);
17 | };
18 |
19 | })();
20 |
--------------------------------------------------------------------------------
/src/js/tools/transform/Clone.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.tools.transform');
3 |
4 | ns.Clone = function () {
5 | this.toolId = 'tool-clone';
6 | this.helpText = 'Clone current layer to all frames';
7 | this.tooltipDescriptors = [];
8 | };
9 |
10 | pskl.utils.inherit(ns.Clone, ns.AbstractTransformTool);
11 |
12 | ns.Clone.prototype.applyTool_ = function (altKey, allFrames, allLayers) {
13 | var ref = pskl.app.piskelController.getCurrentFrame();
14 | var layer = pskl.app.piskelController.getCurrentLayer();
15 | layer.getFrames().forEach(function (frame) {
16 | if (frame !== ref) {
17 | frame.setPixels(ref.getPixels());
18 | }
19 | });
20 | };
21 | })();
22 |
--------------------------------------------------------------------------------
/src/js/tools/transform/Flip.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.tools.transform');
3 |
4 | ns.Flip = function () {
5 | this.toolId = 'tool-flip';
6 | this.helpText = 'Flip horizontally';
7 | this.tooltipDescriptors = [
8 | {key : 'alt', description : 'Flip vertically'},
9 | {key : 'ctrl', description : 'Apply to all layers'},
10 | {key : 'shift', description : 'Apply to all frames'}
11 | ];
12 | };
13 |
14 | pskl.utils.inherit(ns.Flip, ns.AbstractTransformTool);
15 |
16 | ns.Flip.prototype.applyToolOnFrame_ = function (frame, altKey) {
17 | var axis;
18 |
19 | if (altKey) {
20 | axis = ns.TransformUtils.HORIZONTAL;
21 | } else {
22 | axis = ns.TransformUtils.VERTICAL;
23 | }
24 |
25 | ns.TransformUtils.flip(frame, axis);
26 | };
27 |
28 | })();
29 |
--------------------------------------------------------------------------------
/src/js/tools/transform/Rotate.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.tools.transform');
3 |
4 | ns.Rotate = function () {
5 | this.toolId = 'tool-rotate';
6 | this.helpText = 'Counter-clockwise rotation';
7 | this.tooltipDescriptors = [
8 | {key : 'alt', description : 'Clockwise rotation'},
9 | {key : 'ctrl', description : 'Apply to all layers'},
10 | {key : 'shift', description : 'Apply to all frames'}];
11 | };
12 |
13 | pskl.utils.inherit(ns.Rotate, ns.AbstractTransformTool);
14 |
15 | ns.Rotate.prototype.applyToolOnFrame_ = function (frame, altKey) {
16 | var direction;
17 |
18 | if (altKey) {
19 | direction = ns.TransformUtils.CLOCKWISE;
20 | } else {
21 | direction = ns.TransformUtils.COUNTERCLOCKWISE;
22 | }
23 |
24 | ns.TransformUtils.rotate(frame, direction);
25 | };
26 |
27 | })();
28 |
--------------------------------------------------------------------------------
/src/js/utils/Array.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.utils');
3 |
4 | ns.Array = {
5 | find : function (array, filterFn) {
6 | var match = null;
7 | array = Array.isArray(array) ? array : [];
8 | var filtered = array.filter(filterFn);
9 | if (filtered.length) {
10 | match = filtered[0];
11 | }
12 | return match;
13 | },
14 |
15 | /**
16 | * Split a provided array in a given amount of chunks.
17 | * For instance [1,2,3,4] chunked in 2 parts will be [1,2] & [3,4].
18 | * @param {Array} array the array to chunk
19 | * @param {Number} chunksCount the number of chunks to create
20 | * @return {Array} array of arrays containing the items of the original array
21 | */
22 | chunk : function (array, chunksCount) {
23 | var chunks = [];
24 |
25 | // We cannot have more chunks than array items.
26 | chunksCount = Math.min(chunksCount, array.length);
27 |
28 | // chunksCount should be at least 1
29 | chunksCount = Math.max(1, chunksCount);
30 |
31 | var step = Math.round(array.length / chunksCount);
32 | for (var i = 0 ; i < chunksCount ; i++) {
33 | var isLast = i == chunksCount - 1;
34 | var end = isLast ? array.length : (i + 1) * step;
35 | chunks.push(array.slice(i * step, end));
36 | }
37 | return chunks;
38 | }
39 | };
40 |
41 | })();
42 |
--------------------------------------------------------------------------------
/src/js/utils/BlobUtils.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.utils');
3 |
4 | var BASE64_REGEX = /\s*;\s*base64\s*(?:;|$)/i;
5 |
6 | ns.BlobUtils = {
7 | dataToBlob : function(dataURI, type, callback) {
8 | var headerEnd = dataURI.indexOf(',');
9 | var data = dataURI.substring(headerEnd + 1);
10 | var isBase64 = BASE64_REGEX.test(dataURI.substring(0, headerEnd));
11 | var blob;
12 |
13 | if (Blob.fake) {
14 | // no reason to decode a data: URI that's just going to become a data URI again
15 | blob = new Blob();
16 | blob.encoding = isBase64 ? 'base64' : 'URI';
17 | blob.data = data;
18 | blob.size = data.length;
19 | } else if (Uint8Array) {
20 | var blobData = isBase64 ? pskl.utils.Base64.decode(data) : decodeURIComponent(data);
21 | blob = new Blob([blobData], {type: type});
22 | }
23 | callback(blob);
24 | },
25 |
26 | canvasToBlob : function (canvas, callback, type /*, ...args*/) {
27 | type = type || 'image/png';
28 |
29 | if (canvas.mozGetAsFile) {
30 | callback(canvas.mozGetAsFile('canvas', type));
31 | } else {
32 | var args = Array.prototype.slice.call(arguments, 2);
33 | var dataURI = canvas.toDataURL.apply(canvas, args);
34 | pskl.utils.BlobUtils.dataToBlob(dataURI, type, callback);
35 | }
36 | },
37 |
38 | stringToBlob : function (string, callback, type) {
39 | type = type || 'text/plain';
40 | pskl.utils.BlobUtils.dataToBlob('data:' + type + ',' + string, type, callback);
41 | }
42 | };
43 | })();
44 |
--------------------------------------------------------------------------------
/src/js/utils/ColorUtils.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.utils');
3 |
4 | ns.ColorUtils = {
5 | getUnusedColor : function (usedColors) {
6 | usedColors = usedColors || [];
7 | // create check map
8 | var colorMap = {};
9 | usedColors.forEach(function (color) {
10 | colorMap[color.toUpperCase()] = true;
11 | });
12 |
13 | // start with white
14 | var color = {
15 | r : 255,
16 | g : 255,
17 | b : 0
18 | };
19 | var match = null;
20 | while (true) {
21 | var hex = window.tinycolor(color).toHexString().toUpperCase();
22 |
23 | if (!colorMap[hex]) {
24 | match = hex;
25 | break;
26 | } else {
27 | // pick a non null component to decrease its value
28 | var component = (color.r && 'r') || (color.g && 'g') || (color.b && 'b');
29 | if (component) {
30 | color[component] = color[component] - 1;
31 | } else {
32 | // no component available, no match found
33 | break;
34 | }
35 | }
36 | }
37 |
38 | return match;
39 | }
40 | };
41 | })();
42 |
--------------------------------------------------------------------------------
/src/js/utils/DateUtils.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.utils');
3 |
4 | var pad = function (num) {
5 | if (num < 10) {
6 | return '0' + num;
7 | } else {
8 | return '' + num;
9 | }
10 | };
11 |
12 | ns.DateUtils = {
13 | format : function (date, format) {
14 | date = new Date(date);
15 | return pskl.utils.Template.replace(format, {
16 | Y : date.getFullYear(),
17 | M : pad(date.getMonth() + 1),
18 | D : pad(date.getDate()),
19 | H : pad(date.getHours()),
20 | m : pad(date.getMinutes()),
21 | s : pad(date.getSeconds())
22 | });
23 | }
24 | };
25 | })();
26 |
--------------------------------------------------------------------------------
/src/js/utils/Dom.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.utils');
3 |
4 | ns.Dom = {
5 | /**
6 | * Check if a given HTML element is nested inside another
7 | * @param {HTMLElement} node Element to test
8 | * @param {HTMLElement} parent Potential Ancestor for node
9 | * @param {Boolean} excludeParent set to true if the parent should be excluded from potential matches
10 | * @return {Boolean} true if parent was found amongst the parentNode chain of node
11 | */
12 | isParent : function (node, parent, excludeParent) {
13 | if (node && parent) {
14 |
15 | if (excludeParent) {
16 | node = node.parentNode;
17 | }
18 |
19 | while (node) {
20 | if (node === parent) {
21 | return true;
22 | }
23 | node = node.parentNode;
24 | }
25 | }
26 | return false;
27 | },
28 |
29 | getParentWithData : function (node, dataName) {
30 | while (node) {
31 | if (node.dataset && typeof node.dataset[dataName] !== 'undefined') {
32 | return node;
33 | }
34 | node = node.parentNode;
35 | }
36 | return null;
37 | },
38 |
39 | getData : function (node, dataName) {
40 | var parent = ns.Dom.getParentWithData(node, dataName);
41 | if (parent !== null) {
42 | return parent.dataset[dataName];
43 | }
44 | },
45 |
46 | removeClass : function (className, container) {
47 | container = container || document;
48 | var elements = container.querySelectorAll('.' + className);
49 | for (var i = 0 ; i < elements.length ; i++) {
50 | elements[i].classList.remove(className);
51 | }
52 | }
53 | };
54 | })();
55 |
--------------------------------------------------------------------------------
/src/js/utils/Environment.js:
--------------------------------------------------------------------------------
1 | /**
2 | * detection method from:
3 | * http://videlais.com/2014/08/23/lessons-learned-from-detecting-node-webkit/
4 | */
5 |
6 | (function () {
7 |
8 | var ns = $.namespace('pskl.utils');
9 |
10 | ns.Environment = {
11 | detectNodeWebkit : function () {
12 | var isNode = (typeof window.process !== 'undefined' && typeof window.require !== 'undefined');
13 | var isNodeWebkit = false;
14 | if (isNode) {
15 | try {
16 | isNodeWebkit = (typeof window.require('nw.gui') !== 'undefined');
17 | } catch (e) {
18 | isNodeWebkit = false;
19 | }
20 | }
21 | return isNodeWebkit;
22 | },
23 |
24 | isIntegrationTest : function () {
25 | return window.location.href.indexOf('integration-test') !== -1;
26 | },
27 |
28 | isDebug : function () {
29 | return window.location.href.indexOf('debug') !== -1;
30 | },
31 |
32 | isHttps : function () {
33 | return window.location.href.indexOf('https://') === 0;
34 | },
35 | };
36 |
37 | })();
38 |
--------------------------------------------------------------------------------
/src/js/utils/Event.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.utils');
3 |
4 | ns.Event = {};
5 |
6 | ns.Event.addEventListener = function (el, type, callback, scope, args) {
7 | if (typeof el === 'string') {
8 | el = document.querySelector(el);
9 | }
10 |
11 | var listener = {
12 | el : el,
13 | type : type,
14 | callback : callback,
15 | handler : args ? callback.bind(scope, args) : callback.bind(scope)
16 | };
17 |
18 | scope.__pskl_listeners = scope.__pskl_listeners || [];
19 | scope.__pskl_listeners.push(listener);
20 | el.addEventListener(type, listener.handler);
21 | };
22 |
23 | ns.Event.removeEventListener = function (el, type, callback, scope) {
24 | if (scope && scope.__pskl_listeners) {
25 | var listeners = scope.__pskl_listeners;
26 | for (var i = 0 ; i < listeners.length ; i++) {
27 | var listener = listeners[i];
28 | if (listener.callback === callback && listener.el === el && listener.type === type) {
29 | el.removeEventListener(type, listeners[i].handler);
30 | listeners.splice(i, 1);
31 | break;
32 | }
33 | }
34 | }
35 | };
36 |
37 | ns.Event.removeAllEventListeners = function (scope) {
38 | if (scope && scope.__pskl_listeners) {
39 | var listeners = scope.__pskl_listeners;
40 | for (var i = 0 ; i < listeners.length ; i++) {
41 | var listener = listeners[i];
42 | listener.el.removeEventListener(listener.type, listener.handler);
43 | }
44 | scope.__pskl_listeners = [];
45 | }
46 | };
47 | })();
48 |
--------------------------------------------------------------------------------
/src/js/utils/FileUtils.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.utils');
3 |
4 | var stopPropagation = function (e) {
5 | e.stopPropagation();
6 | };
7 |
8 | ns.FileUtils = {
9 | readFile : function (file, callback) {
10 | var reader = new FileReader();
11 | reader.addEventListener('loadend', function() {
12 | callback(reader.result);
13 | });
14 | reader.readAsDataURL(file);
15 | },
16 |
17 | readFileAsArrayBuffer : function (file, callback) {
18 | var reader = new FileReader();
19 | reader.addEventListener('loadend', function() {
20 | callback(reader.result);
21 | });
22 | reader.readAsArrayBuffer(file);
23 | },
24 |
25 | readImageFile : function (file, callback) {
26 | ns.FileUtils.readFile(file, function (content) {
27 | var image = new Image();
28 | image.onload = callback.bind(null, image);
29 | image.src = content;
30 | });
31 | },
32 |
33 | downloadAsFile : function (content, filename) {
34 | var saveAs = window.saveAs || (navigator.msSaveBlob && navigator.msSaveBlob.bind(navigator));
35 | if (saveAs) {
36 | saveAs(content, filename);
37 | } else {
38 | var downloadLink = document.createElement('a');
39 | content = window.URL.createObjectURL(content);
40 | downloadLink.setAttribute('href', content);
41 | downloadLink.setAttribute('download', filename);
42 | document.body.appendChild(downloadLink);
43 | downloadLink.addEventListener('click', stopPropagation);
44 | downloadLink.click();
45 | downloadLink.removeEventListener('click', stopPropagation);
46 | document.body.removeChild(downloadLink);
47 | }
48 | }
49 |
50 | };
51 | })();
52 |
--------------------------------------------------------------------------------
/src/js/utils/FunctionUtils.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.utils');
3 |
4 | ns.FunctionUtils = {
5 | /**
6 | * Returns a memoized version of the provided function.
7 | */
8 | memo : function (fn, cache, scope) {
9 | var memoized = function () {
10 | var key = Array.prototype.join.call(arguments, '-');
11 | if (!cache[key]) {
12 | cache[key] = fn.apply(scope, arguments);
13 | }
14 | return cache[key];
15 | };
16 | return memoized;
17 | },
18 |
19 | /**
20 | * Returns a throttled version of the provided method, that will be called at most
21 | * every X milliseconds, where X is the provided interval.
22 | */
23 | throttle : function (fn, interval) {
24 | var last;
25 | var timer;
26 | return function () {
27 | var now = Date.now();
28 | if (last && now < last + interval) {
29 | clearTimeout(timer);
30 | timer = setTimeout(function () {
31 | last = now;
32 | fn();
33 | }, interval);
34 | } else {
35 | last = now;
36 | fn();
37 | }
38 | };
39 | }
40 | };
41 | })();
42 |
--------------------------------------------------------------------------------
/src/js/utils/ImageResizer.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.utils');
3 |
4 | ns.ImageResizer = {
5 | scale : function (image, factor, smoothingEnabled) {
6 | return ns.ImageResizer.resize(image, image.width * factor, image.height * factor, smoothingEnabled);
7 | },
8 |
9 | resize : function (image, targetWidth, targetHeight, smoothingEnabled) {
10 | var canvas = pskl.utils.CanvasUtils.createCanvas(targetWidth, targetHeight);
11 | var context = canvas.getContext('2d');
12 | context.save();
13 |
14 | if (!smoothingEnabled) {
15 | pskl.utils.CanvasUtils.disableImageSmoothing(canvas);
16 | }
17 |
18 | context.translate(canvas.width / 2, canvas.height / 2);
19 | context.scale(targetWidth / image.width, targetHeight / image.height);
20 | context.drawImage(image, -image.width / 2, -image.height / 2);
21 | context.restore();
22 |
23 | return canvas;
24 | }
25 | };
26 | })();
27 |
--------------------------------------------------------------------------------
/src/js/utils/Math.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.utils');
3 |
4 | ns.Math = {
5 | minmax : function (val, min, max) {
6 | return Math.max(Math.min(val, max), min);
7 | },
8 |
9 | /**
10 | * Calculate the distance between {x0, y0} and {x1, y1}
11 | */
12 | distance : function (x0, x1, y0, y1) {
13 | var dx = Math.abs(x1 - x0);
14 | var dy = Math.abs(y1 - y0);
15 | return Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2));
16 | }
17 | };
18 | })();
19 |
--------------------------------------------------------------------------------
/src/js/utils/StringUtils.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.utils');
3 |
4 | ns.StringUtils = {
5 | leftPad : function (input, length, pad) {
6 | var padding = new Array(length).join(pad);
7 | return (padding + input).slice(-length);
8 | },
9 |
10 | formatSize : function (width, height) {
11 | return width + '\u00D7' + height;
12 | }
13 | };
14 | })();
15 |
--------------------------------------------------------------------------------
/src/js/utils/TooltipFormatter.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.utils');
3 |
4 | ns.TooltipFormatter = {};
5 |
6 | ns.TooltipFormatter.format = function(helpText, shortcut, descriptors) {
7 | var tpl = pskl.utils.Template.get('tooltip-container-template');
8 | shortcut = shortcut ? '(' + shortcut.getDisplayKey() + ')' : '';
9 | return pskl.utils.Template.replace(tpl, {
10 | helptext : helpText,
11 | shortcut : shortcut,
12 | // Avoid sanitization for descriptors (markup)
13 | '!descriptors!' : this.formatDescriptors_(descriptors)
14 | });
15 | };
16 |
17 | ns.TooltipFormatter.formatDescriptors_ = function(descriptors) {
18 | descriptors = descriptors || [];
19 | return descriptors.reduce(function (p, descriptor) {
20 | return p += this.formatDescriptor_(descriptor);
21 | }.bind(this), '');
22 | };
23 |
24 | ns.TooltipFormatter.formatDescriptor_ = function(descriptor) {
25 | var tpl;
26 | if (descriptor.key) {
27 | tpl = pskl.utils.Template.get('tooltip-modifier-descriptor-template');
28 | descriptor.key = descriptor.key.toUpperCase();
29 | if (pskl.utils.UserAgent.isMac) {
30 | descriptor.key = descriptor.key.replace('CTRL', 'CMD');
31 | descriptor.key = descriptor.key.replace('ALT', 'OPTION');
32 | }
33 | } else {
34 | tpl = pskl.utils.Template.get('tooltip-simple-descriptor-template');
35 | }
36 | return pskl.utils.Template.replace(tpl, descriptor);
37 | };
38 | })();
39 |
--------------------------------------------------------------------------------
/src/js/utils/Uuid.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.utils');
3 |
4 | var s4 = function () {
5 | return Math.floor((1 + Math.random()) * 0x10000)
6 | .toString(16)
7 | .substring(1);
8 | };
9 |
10 | ns.Uuid = {
11 | generate : function () {
12 | return 'ss-s-s-s-sss'.replace(/s/g, function () {
13 | return s4();
14 | });
15 | }
16 | };
17 | })();
18 |
--------------------------------------------------------------------------------
/src/js/utils/WorkerUtils.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.utils');
3 |
4 | var workers = {};
5 |
6 | ns.WorkerUtils = {
7 | createWorker : function (worker, workerId) {
8 | if (!workers[workerId]) {
9 | workers[workerId] = ns.WorkerUtils.createWorkerURL(worker);
10 | }
11 |
12 | return new Worker(workers[workerId]);
13 | },
14 |
15 | createWorkerURL : function (worker) {
16 | // remove "function () {" at the start of the worker string and the last "}" before the end
17 | var typedArray = [(worker + '').replace(/function\s*\(\)\s*\{/, '').replace(/\}[^}]*$/, '')];
18 | var blob = new Blob(typedArray, {type: 'application/javascript'});
19 | return window.URL.createObjectURL(blob);
20 | }
21 | };
22 | })();
23 |
--------------------------------------------------------------------------------
/src/js/utils/Xhr.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.utils');
3 | ns.Xhr = {
4 | get : function (url, success, error) {
5 | var xhr = ns.Xhr.xhr_(url, 'GET', success, error);
6 | xhr.send();
7 | },
8 |
9 | post : function (url, data, success, error) {
10 | var xhr = ns.Xhr.xhr_(url, 'POST', success, error);
11 | var formData = new FormData();
12 |
13 | if (typeof data == 'object') {
14 | for (var key in data) {
15 | if (data.hasOwnProperty(key)) {
16 | formData.append(key, data[key]);
17 | }
18 | }
19 | }
20 |
21 | xhr.send(formData);
22 | },
23 |
24 | xhr_ : function (url, method, success, error) {
25 | success = success || function () {};
26 | error = error || function () {};
27 |
28 | var xhr = new XMLHttpRequest();
29 | xhr.open(method, url, true);
30 |
31 | xhr.onload = function (e) {
32 | if (this.status == 200) {
33 | success(this);
34 | } else {
35 | this.onerror(this, e);
36 | }
37 | };
38 |
39 | xhr.onerror = function (e) {
40 | error(e, this);
41 | };
42 |
43 | return xhr;
44 | }
45 | };
46 | })();
47 |
--------------------------------------------------------------------------------
/src/js/utils/serialization/backward/Deserializer_v0.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.utils.serialization.backward');
3 |
4 | ns.Deserializer_v0 = function (data, callback) {
5 | this.data_ = data;
6 | this.callback_ = callback;
7 | };
8 |
9 | ns.Deserializer_v0.prototype.deserialize = function () {
10 | var pixelGrids = this.data_;
11 | var frames = pixelGrids.map(function (grid) {
12 | return pskl.model.Frame.fromPixelGrid(grid);
13 | });
14 | var descriptor = new pskl.model.piskel.Descriptor('Deserialized piskel', '');
15 | var layer = pskl.model.Layer.fromFrames('Layer 1', frames);
16 |
17 | this.callback_(pskl.model.Piskel.fromLayers([layer], Constants.DEFAULT.FPS, descriptor));
18 | };
19 | })();
20 |
--------------------------------------------------------------------------------
/src/js/utils/serialization/backward/Deserializer_v1.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.utils.serialization.backward');
3 |
4 | ns.Deserializer_v1 = function (data, callback) {
5 | this.callback_ = callback;
6 | this.data_ = data;
7 | };
8 |
9 | ns.Deserializer_v1.prototype.deserialize = function () {
10 | var piskelData = this.data_.piskel;
11 | var descriptor = new pskl.model.piskel.Descriptor('Deserialized piskel', '');
12 | var piskel = new pskl.model.Piskel(piskelData.width, piskelData.height, Constants.DEFAULT.FPS, descriptor);
13 |
14 | piskelData.layers.forEach(function (serializedLayer) {
15 | var layer = this.deserializeLayer(serializedLayer);
16 | piskel.addLayer(layer);
17 | }.bind(this));
18 |
19 | this.callback_(piskel);
20 | };
21 |
22 | ns.Deserializer_v1.prototype.deserializeLayer = function (layerString) {
23 | var layerData = JSON.parse(layerString);
24 | var layer = new pskl.model.Layer(layerData.name);
25 | layerData.frames.forEach(function (serializedFrame) {
26 | var frame = this.deserializeFrame(serializedFrame);
27 | layer.addFrame(frame);
28 | }.bind(this));
29 |
30 | return layer;
31 | };
32 |
33 | ns.Deserializer_v1.prototype.deserializeFrame = function (frameString) {
34 | var framePixelGrid = JSON.parse(frameString);
35 | return pskl.model.Frame.fromPixelGrid(framePixelGrid);
36 | };
37 | })();
38 |
--------------------------------------------------------------------------------
/src/js/widgets/SizePicker.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.widgets');
3 |
4 | ns.SizePicker = function (onChange) {
5 | this.onChange = onChange;
6 | };
7 |
8 | ns.SizePicker.prototype.init = function (container) {
9 | this.container = container;
10 | pskl.utils.Event.addEventListener(this.container, 'click', this.onSizeOptionClick_, this);
11 | };
12 |
13 | ns.SizePicker.prototype.destroy = function () {
14 | pskl.utils.Event.removeAllEventListeners(this);
15 | };
16 |
17 | ns.SizePicker.prototype.getSize = function () {
18 | var selectedOption = this.container.querySelector('.selected');
19 | return selectedOption ? selectedOption.dataset.size : null;
20 | };
21 |
22 | ns.SizePicker.prototype.setSize = function (size) {
23 | if (this.getSize() === size) {
24 | return;
25 | }
26 |
27 | pskl.utils.Dom.removeClass('labeled', this.container);
28 | pskl.utils.Dom.removeClass('selected', this.container);
29 | var selectedOption;
30 | selectedOption = this.container.querySelector('[data-size="' + size + '"]');
31 | if (!selectedOption) {
32 | selectedOption = this.container.querySelector('[data-size]:last-child');
33 | selectedOption.classList.add('labeled');
34 | selectedOption.setAttribute('real-size', size);
35 | }
36 | if (selectedOption) {
37 | selectedOption.classList.add('selected');
38 | }
39 | };
40 |
41 | ns.SizePicker.prototype.onSizeOptionClick_ = function (e) {
42 | var size = e.target.dataset.size;
43 | if (!isNaN(size)) {
44 | size = parseInt(size, 10);
45 | this.onChange(size);
46 | this.setSize(size);
47 | }
48 | };
49 | })();
50 |
--------------------------------------------------------------------------------
/src/js/widgets/SynchronizedInputs.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.widgets');
3 |
4 | ns.SynchronizedInputs = function (options) {
5 | this.leftInput = options.leftInput;
6 | this.rightInput = options.rightInput;
7 | this.synchronize = options.synchronize;
8 |
9 | this.syncEnabled = true;
10 | this.lastInput = this.leftInput;
11 |
12 | pskl.utils.Event.addEventListener(this.leftInput, 'input', this.onInput_, this);
13 | pskl.utils.Event.addEventListener(this.rightInput, 'input', this.onInput_, this);
14 | };
15 |
16 | ns.SynchronizedInputs.prototype.destroy = function () {
17 | pskl.utils.Event.removeAllEventListeners(this);
18 |
19 | this.leftInput = null;
20 | this.rightInput = null;
21 | this.lastInput = null;
22 | };
23 |
24 | ns.SynchronizedInputs.prototype.enableSync = function () {
25 | this.syncEnabled = true;
26 | this.synchronize(this.lastInput);
27 | };
28 |
29 | ns.SynchronizedInputs.prototype.disableSync = function () {
30 | this.syncEnabled = false;
31 | };
32 |
33 | ns.SynchronizedInputs.prototype.onInput_ = function (evt) {
34 | var target = evt.target;
35 | if (this.syncEnabled) {
36 | this.synchronize(target);
37 | }
38 | this.lastInput = target;
39 | };
40 | })();
41 |
--------------------------------------------------------------------------------
/src/js/worker/framecolors/FrameColors.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.worker.framecolors');
3 |
4 | ns.FrameColors = function (frame, onSuccess, onStep, onError) {
5 | this.pixels = frame.pixels;
6 |
7 | this.onStep = onStep;
8 | this.onSuccess = onSuccess;
9 | this.onError = onError;
10 |
11 | this.worker = pskl.utils.WorkerUtils.createWorker(ns.FrameColorsWorker, 'frame-colors');
12 | this.worker.onmessage = this.onWorkerMessage.bind(this);
13 | };
14 |
15 | ns.FrameColors.prototype.process = function () {
16 | this.worker.postMessage([
17 | pskl.utils.colorToInt(Constants.TRANSPARENT_COLOR),
18 | Constants.MAX_WORKER_COLORS, this.pixels
19 | ]);
20 | };
21 |
22 | ns.FrameColors.prototype.onWorkerMessage = function (event) {
23 | if (event.data.type === 'STEP') {
24 | this.onStep(event);
25 | } else if (event.data.type === 'SUCCESS') {
26 | this.onSuccess(event);
27 | this.worker.terminate();
28 | } else if (event.data.type === 'ERROR') {
29 | this.onError(event);
30 | this.worker.terminate();
31 | }
32 | };
33 | })();
34 |
--------------------------------------------------------------------------------
/src/js/worker/hash/Hash.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.worker.hash');
3 |
4 | ns.Hash = function (str, onSuccess, onStep, onError) {
5 | this.str = str;
6 |
7 | this.onStep = onStep;
8 | this.onSuccess = onSuccess;
9 | this.onError = onError;
10 |
11 | this.worker = pskl.utils.WorkerUtils.createWorker(ns.HashWorker, 'hash');
12 | this.worker.onmessage = this.onWorkerMessage.bind(this);
13 | };
14 |
15 | ns.Hash.prototype.process = function () {
16 | this.worker.postMessage({
17 | str : this.str
18 | });
19 | };
20 |
21 | ns.Hash.prototype.onWorkerMessage = function (event) {
22 | if (event.data.type === 'STEP') {
23 | this.onStep(event);
24 | } else if (event.data.type === 'SUCCESS') {
25 | this.onSuccess(event);
26 | this.worker.terminate();
27 | } else if (event.data.type === 'ERROR') {
28 | this.onError(event);
29 | this.worker.terminate();
30 | }
31 | };
32 | })();
33 |
--------------------------------------------------------------------------------
/src/js/worker/hash/HashWorker.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.worker.hash');
3 |
4 | ns.HashWorker = function () {
5 | var hashCode = function(str) {
6 | var hash = 0;
7 | if (str.length !== 0) {
8 | for (var i = 0, l = str.length; i < l; i++) {
9 | var chr = str.charCodeAt(i);
10 | hash = ((hash << 5) - hash) + chr;
11 | hash |= 0; // Convert to 32bit integer
12 | }
13 | }
14 | return hash;
15 | };
16 |
17 | this.onmessage = function(event) {
18 | try {
19 | var data = event.data;
20 | var str = data.str;
21 | var hash = hashCode(str);
22 | this.postMessage({
23 | type : 'SUCCESS',
24 | hash : hash
25 | });
26 | } catch (e) {
27 | this.postMessage({
28 | type : 'ERROR',
29 | message : e.message
30 | });
31 | }
32 | };
33 | };
34 | })();
35 |
--------------------------------------------------------------------------------
/src/js/worker/imageprocessor/ImageProcessor.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var ns = $.namespace('pskl.worker.imageprocessor');
3 |
4 | ns.ImageProcessor = function (image, onSuccess, onStep, onError) {
5 | this.image = image;
6 |
7 | this.onStep = onStep;
8 | this.onSuccess = onSuccess;
9 | this.onError = onError;
10 |
11 | this.worker = pskl.utils.WorkerUtils.createWorker(ns.ImageProcessorWorker, 'image-colors-processor');
12 | this.worker.onmessage = this.onWorkerMessage.bind(this);
13 | };
14 |
15 | ns.ImageProcessor.prototype.process = function () {
16 | var canvas = pskl.utils.CanvasUtils.createFromImage(this.image);
17 | var imageData = pskl.utils.CanvasUtils.getImageDataFromCanvas(canvas);
18 | this.worker.postMessage({
19 | imageData : imageData,
20 | width : this.image.width,
21 | height : this.image.height
22 | });
23 | };
24 |
25 | ns.ImageProcessor.prototype.onWorkerMessage = function (event) {
26 | if (event.data.type === 'STEP') {
27 | this.onStep(event);
28 | } else if (event.data.type === 'SUCCESS') {
29 | this.onSuccess(event);
30 | this.worker.terminate();
31 | } else if (event.data.type === 'ERROR') {
32 | this.onError(event);
33 | this.worker.terminate();
34 | }
35 | };
36 | })();
37 |
--------------------------------------------------------------------------------
/src/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piskelapp/piskel/b988fb256a6273d52f6804bbf167dfd20f2c6ef2/src/logo.png
--------------------------------------------------------------------------------
/src/piskel-style-list.js:
--------------------------------------------------------------------------------
1 | // This list is used both by the grunt build and index.html (in debug mode)
2 |
3 | (typeof exports != "undefined" ? exports : pskl_exports).styles = [
4 | "css/variables.css",
5 | "css/reset.css",
6 | "css/style.css",
7 | "css/animations.css",
8 | "css/layout.css",
9 | "css/font-icon.css",
10 | "css/forms.css",
11 | "css/settings.css",
12 | "css/settings-application.css",
13 | "css/settings-export.css",
14 | "css/settings-import.css",
15 | "css/settings-resize.css",
16 | "css/settings-save.css",
17 | "css/tools.css",
18 | "css/icons.css",
19 | "css/color-picker-slider.css",
20 | "css/dialogs.css",
21 | "css/dialogs-browse-backups.css",
22 | "css/dialogs-browse-local.css",
23 | "css/dialogs-cheatsheet.css",
24 | "css/dialogs-create-palette.css",
25 | "css/dialogs-import.css",
26 | "css/dialogs-performance-info.css",
27 | "css/dialogs-unsupported-browser.css",
28 | "css/notifications.css",
29 | "css/toolbox.css",
30 | "css/toolbox-layers-list.css",
31 | "css/toolbox-palettes-list.css",
32 | "css/toolbox-animated-preview.css",
33 | "css/transformations.css",
34 | "css/spectrum/spectrum.css",
35 | "css/spectrum/spectrum-overrides.css",
36 | "css/bootstrap/bootstrap.css",
37 | "css/bootstrap/bootstrap-tooltip-custom.css",
38 | "css/frames-list.css",
39 | "css/minimap.css",
40 | "css/widgets-anchor.css",
41 | "css/widgets-frame-picker.css",
42 | "css/widgets-size-picker.css",
43 | "css/widgets-tabs.css",
44 | "css/widgets-wizard.css"
45 | ];
46 |
--------------------------------------------------------------------------------
/src/templates/debug-header.html:
--------------------------------------------------------------------------------
1 |
41 |
--------------------------------------------------------------------------------
/src/templates/dialogs/browse-local.html:
--------------------------------------------------------------------------------
1 |
21 |
22 |
--------------------------------------------------------------------------------
/src/templates/dialogs/unsupported-browser.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/templates/frames-list.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/templates/misc-templates.html:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
17 |
18 |
19 |
25 |
26 |
27 |
32 |
--------------------------------------------------------------------------------
/src/templates/palettes-list.html:
--------------------------------------------------------------------------------
1 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/src/templates/preview.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
1x
7 |
8 |
Full
9 |
10 |
12 |
14 |
15 |
16 |
19 |
20 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/src/templates/settings.html:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
14 |
15 |
16 |
21 |
22 |
23 |
28 |
29 |
30 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/src/templates/settings/export.html:
--------------------------------------------------------------------------------
1 |
26 |
--------------------------------------------------------------------------------
/src/templates/settings/export/gif.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/templates/settings/export/misc.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/templates/settings/export/zip.html:
--------------------------------------------------------------------------------
1 |
22 |
--------------------------------------------------------------------------------
/src/templates/settings/preferences.html:
--------------------------------------------------------------------------------
1 |
13 |
--------------------------------------------------------------------------------
/src/templates/settings/preferences/tile.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/templates/transformations.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/test/casperjs/DrawingTest.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var tests = require('../drawing/DrawingTests.casper.js').tests;
3 |
4 | // Polyfill for Object.assign (missing in PhantomJS)
5 | casper.options.clientScripts.push('./node_modules/phantomjs-polyfill-object-assign/object-assign-polyfill.js');
6 |
7 | var baseUrl = casper.cli.get('baseUrl')+"?debug";
8 | var resultSelector = '#drawing-test-result';
9 |
10 |
11 | casper.test.begin('Drawing Tests', tests.length, function(test) {
12 | casper.start();
13 |
14 | var runTest = function (index) {
15 | var currentTest = 'drawing/tests/' + tests[index];
16 |
17 | casper.open(baseUrl + "&test-run=" + currentTest);
18 |
19 | casper.then(function () {
20 | this.echo('Running test : ' + currentTest);
21 | this.echo('... Waiting for test result : ' + resultSelector);
22 | this.waitForSelector(resultSelector, function () {
23 | // then
24 | var result = this.getHTML(resultSelector);
25 | this.echo('... Test finished : ' + result);
26 | test.assertEquals(result, 'OK');
27 | }, function () {
28 | // onTimeout
29 | test.fail('Test timed out');
30 | }, 30 * 1000);
31 | })
32 | .run(function () {
33 | if (tests[index+1]) {
34 | runTest(index+1);
35 | } else {
36 | test.done();
37 | }
38 | });
39 | };
40 |
41 | runTest(0);
42 | });
43 | })();
44 |
--------------------------------------------------------------------------------
/test/casperjs/integration/IntegrationSuite.js:
--------------------------------------------------------------------------------
1 | (typeof exports != "undefined" ? exports : pskl_exports).tests = [
2 | 'palettes/test-tiny-palettes.js',
3 | 'preview/test-toggle-grid.js',
4 | 'settings/test-preferences-main.js',
5 | 'settings/test-export-gif.js',
6 | 'settings/test-export-gif-scale.js',
7 | 'settings/test-export-gif-simple.js',
8 | 'settings/test-export-png.js',
9 | 'settings/test-export-png-scale.js',
10 | 'settings/test-import-image.js',
11 | 'settings/test-import-image-empty.js',
12 | 'settings/test-import-image-twice.js',
13 | 'settings/test-resize-complete.js',
14 | 'settings/test-resize-content-complete.js',
15 | 'settings/test-resize-default-size.js',
16 | 'settings/test-resize-input-synchronization.js',
17 | 'settings/test-resize.js',
18 | 'settings/test-resize-origin.js',
19 | 'settings/test-settings-open-panels-on-click.js',
20 | ];
21 |
--------------------------------------------------------------------------------
/test/drawing/DrawingTests.browser.js:
--------------------------------------------------------------------------------
1 | {"tests" : [
2 | "pen.drawing.json",
3 | "bucket.drawing.json",
4 | "color.picker.2.json",
5 | "color.picker.json",
6 | "frames.fun.json",
7 | "history.basic.json",
8 | "layers.duplicate.json",
9 | "layers.fun.json",
10 | "layers.merge.json",
11 | "layers.top.bottom.json",
12 | "lighten.darken.json",
13 | "move.json",
14 | "move-alllayers-allframes.json",
15 | "pen.mirror.pensize.json",
16 | "pen.secondary.color.json",
17 | "selection.rectangular.json",
18 | "squares.circles.json",
19 | "stroke.json",
20 | "verticalpen.drawing.json",
21 | "dithering.basic.json",
22 | "transform.center.json",
23 | "transform.clone.once.json",
24 | "transform.clone.twice.undo.once.json",
25 | "transform.crop.json",
26 | "transform.crop.selection.json",
27 | "transform.rotate.once.alt.json",
28 | "transform.rotate.twice.undo.once.json",
29 | "transform.rotate.alt.twice.undo.once.json",
30 | "transform.flip.once.alt.json",
31 | "transform.flip.twice.undo.once.json",
32 | "transform.flip.thrice.undo.all.redo.all.json",
33 | "selection.lasso.json",
34 | "swapcolor.twice.undo.once.json",
35 | "swapcolor.alllayers.allframes.twice.undo.once.json"
36 | ]}
--------------------------------------------------------------------------------
/test/drawing/DrawingTests.casper.js:
--------------------------------------------------------------------------------
1 | (typeof exports != "undefined" ? exports : pskl_exports).tests = [
2 | "pen.drawing.json",
3 | "color.picker.2.json",
4 | "color.picker.json",
5 | "frames.fun.json",
6 | "history.basic.json",
7 | "layers.duplicate.json",
8 | "layers.fun.json",
9 | "layers.merge.json",
10 | "layers.top.bottom.json",
11 | "move.json",
12 | "move-alllayers-allframes.json",
13 | "pen.mirror.pensize.json",
14 | "pen.secondary.color.json",
15 | "selection.rectangular.json",
16 | "squares.circles.json",
17 | "stroke.json",
18 | "verticalpen.drawing.json",
19 | "dithering.basic.json",
20 | "transform.center.json",
21 | "transform.clone.once.json",
22 | "transform.clone.twice.undo.once.json",
23 | "transform.crop.json",
24 | "transform.crop.selection.json",
25 | "transform.rotate.once.alt.json",
26 | "transform.rotate.twice.undo.once.json",
27 | "transform.rotate.alt.twice.undo.once.json",
28 | "transform.flip.once.alt.json",
29 | "transform.flip.twice.undo.once.json",
30 | "transform.flip.thrice.undo.all.redo.all.json",
31 | "selection.lasso.json",
32 | "swapcolor.twice.undo.once.json",
33 | "swapcolor.alllayers.allframes.twice.undo.once.json"
34 | ];
--------------------------------------------------------------------------------
/test/drawing/DrawingTests.pensize.js:
--------------------------------------------------------------------------------
1 | {"tests" : [
2 | "pensize.circle.basic.json",
3 | "pensize.circle.undo.json",
4 | "pensize.eraser.basic.json",
5 | "pensize.eraser.undo.json",
6 | "pensize.pen.basic.json",
7 | "pensize.pen.undo.json",
8 | "pensize.rectangle.basic.json",
9 | "pensize.rectangle.undo.json",
10 | "pensize.stroke.basic.json",
11 | "pensize.stroke.undo.json"
12 | ]}
--------------------------------------------------------------------------------
/test/drawing/DrawingTests.perf.js:
--------------------------------------------------------------------------------
1 | {"tests" : [
2 | "perf.512.layers.undo.json",
3 | "perf.1024.pen.bucket.json"
4 | ]}
--------------------------------------------------------------------------------
/test/drawing/tests/pensize.pen.undo.json:
--------------------------------------------------------------------------------
1 | {"events":[{"event":{"type":"mousedown","button":0,"shiftKey":false,"altKey":false,"ctrlKey":false},"coords":{"x":0,"y":0},"type":"mouse-event"},{"event":{"type":"mouseup","button":0,"shiftKey":false,"altKey":false,"ctrlKey":false},"coords":{"x":0,"y":0},"type":"mouse-event"},{"type":"pensize-event","penSize":2},{"event":{"type":"mousedown","button":0,"shiftKey":false,"altKey":false,"ctrlKey":false},"coords":{"x":1,"y":0},"type":"mouse-event"},{"event":{"type":"mouseup","button":0,"shiftKey":false,"altKey":false,"ctrlKey":false},"coords":{"x":1,"y":0},"type":"mouse-event"},{"type":"pensize-event","penSize":3},{"event":{"type":"mousedown","button":0,"shiftKey":false,"altKey":false,"ctrlKey":false},"coords":{"x":4,"y":1},"type":"mouse-event"},{"event":{"type":"mouseup","button":0,"shiftKey":false,"altKey":false,"ctrlKey":false},"coords":{"x":4,"y":1},"type":"mouse-event"},{"type":"pensize-event","penSize":4},{"event":{"type":"mousedown","button":0,"shiftKey":false,"altKey":false,"ctrlKey":false},"coords":{"x":5,"y":5},"type":"mouse-event"},{"event":{"type":"mousemove","button":0,"shiftKey":false,"altKey":false,"ctrlKey":false},"coords":{"x":5,"y":5},"type":"mouse-event"},{"event":{"type":"mouseup","button":0,"shiftKey":false,"altKey":false,"ctrlKey":false},"coords":{"x":5,"y":5},"type":"mouse-event"},{"type":"keyboard-event","event":{"which":90,"shiftKey":false,"altKey":false,"ctrlKey":true,"target":{"nodeName":"BODY"}}}],"initialState":{"size":{"width":8,"height":8},"primaryColor":"#000000","secondaryColor":"rgba(0, 0, 0, 0)","selectedTool":"tool-pen","penSize":1},"png":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAK0lEQVQYV2NkYGD4z4AdMIKEwQQORSgKYGYgm4ZVAYZlMCtwOAPhBhoqAACekgMJrfUpnQAAAABJRU5ErkJggg=="}
--------------------------------------------------------------------------------
/test/drawing/tests/transform.flip.once.alt.json:
--------------------------------------------------------------------------------
1 | {"events":[{"event":{"type":"mousedown","button":0,"shiftKey":false,"altKey":false,"ctrlKey":false},"coords":{"x":0,"y":0},"type":"mouse-event"},{"event":{"type":"mouseup","button":0,"shiftKey":false,"altKey":false,"ctrlKey":false},"coords":{"x":0,"y":0},"type":"mouse-event"},{"type":"transformtool-event","toolId":"tool-flip","event":{"shiftKey":false,"altKey":true,"ctrlKey":false}}],"initialState":{"size":{"width":2,"height":2},"primaryColor":"#000000","secondaryColor":"rgba(0, 0, 0, 0)","selectedTool":"tool-pen"},"png":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAFElEQVQIW2NkgAJGBgaG/wwMDIwABSkBAyQtNbwAAAAASUVORK5CYII="}
--------------------------------------------------------------------------------
/test/drawing/tests/transform.rotate.once.alt.json:
--------------------------------------------------------------------------------
1 | {"events":[{"event":{"type":"mousedown","button":0,"shiftKey":false,"altKey":false,"ctrlKey":false},"coords":{"x":0,"y":0},"type":"mouse-event"},{"event":{"type":"mouseup","button":0,"shiftKey":false,"altKey":false,"ctrlKey":false},"coords":{"x":0,"y":0},"type":"mouse-event"},{"type":"transformtool-event","toolId":"tool-rotate","event":{"shiftKey":false,"altKey":true,"ctrlKey":false}}],"initialState":{"size":{"width":2,"height":2},"primaryColor":"#ff0000","secondaryColor":"rgba(0, 0, 0, 0)","selectedTool":"tool-stroke"},"png":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAFUlEQVQIW2NkYGBg+M/A8J8RxAABABcWAgFMzp95AAAAAElFTkSuQmCC"}
--------------------------------------------------------------------------------
/test/js/model/LayerTest.js:
--------------------------------------------------------------------------------
1 | describe("Layer model test", function() {
2 |
3 | beforeEach(function() {});
4 | afterEach(function() {});
5 |
6 | it("has proper defaults", function() {
7 | var layer = new pskl.model.Layer('layerName');
8 |
9 | expect(layer.getOpacity()).toBe(1);
10 | expect(layer.getFrames().length).toBe(0);
11 | expect(layer.getName()).toBe('layerName');
12 | });
13 |
14 | it("can set opacity", function() {
15 | var layer = new pskl.model.Layer('layerName');
16 |
17 | layer.setOpacity(0.5);
18 | expect(layer.getOpacity()).toBe(0.5);
19 | });
20 |
21 | it("ignores bad opacity", function() {
22 | var layer = new pskl.model.Layer('layerName');
23 |
24 | layer.setOpacity(0.3);
25 | expect(layer.getOpacity()).toBe(0.3);
26 |
27 | layer.setOpacity('Yep I\'m an opacity, let me in !');
28 | expect(layer.getOpacity()).toBe(0.3);
29 |
30 | layer.setOpacity(9000);
31 | expect(layer.getOpacity()).toBe(0.3);
32 |
33 | layer.setOpacity(-1);
34 | expect(layer.getOpacity()).toBe(0.3);
35 |
36 | layer.setOpacity(null);
37 | expect(layer.getOpacity()).toBe(0.3);
38 | });
39 | });
--------------------------------------------------------------------------------
/test/js/model/PaletteTest.js:
--------------------------------------------------------------------------------
1 | describe("Palette", function() {
2 |
3 | beforeEach(function() {});
4 | afterEach(function() {});
5 |
6 | it("moves colors correctly", function() {
7 | // when
8 | var colors = [
9 | '#000000',
10 | '#111111',
11 | '#222222'
12 | ];
13 | var palette = new pskl.model.Palette('id', 'name', colors);
14 |
15 | // then
16 | palette.move(2,0);
17 |
18 | // verify
19 | expect(palette.get(0)).toBe('#222222');
20 | expect(palette.get(1)).toBe('#000000');
21 | expect(palette.get(2)).toBe('#111111');
22 | });
23 | });
--------------------------------------------------------------------------------
/test/js/rendering/CanvasRendererTest.js:
--------------------------------------------------------------------------------
1 | describe("Canvas Renderer test", function() {
2 | var BLACK = '#000000';
3 | var WHITE = '#ffffff';
4 | var TRANS = Constants.TRANSPARENT_COLOR;
5 |
6 | beforeEach(function() {});
7 | afterEach(function() {});
8 |
9 | it("draws transparent as white by default", function() {
10 | // create frame
11 | var frame = pskl.model.Frame.fromPixelGrid(test.testutils.toFrameGrid([
12 | [BLACK, TRANS],
13 | [TRANS, BLACK]
14 | ]));
15 |
16 | var renderer = new pskl.rendering.CanvasRenderer(frame, 1);
17 | var canvas = renderer.render();
18 |
19 | var frameFromCanvas = pskl.utils.FrameUtils.createFromImage(canvas);
20 |
21 | test.testutils.colorEqualsColor(frameFromCanvas.getPixel(0,0), BLACK);
22 | test.testutils.colorEqualsColor(frameFromCanvas.getPixel(0,1), WHITE);
23 | test.testutils.colorEqualsColor(frameFromCanvas.getPixel(1,0), WHITE);
24 | test.testutils.colorEqualsColor(frameFromCanvas.getPixel(1,1), BLACK);
25 | });
26 | });
--------------------------------------------------------------------------------
/test/js/service/SelectedColorsServiceTest.js:
--------------------------------------------------------------------------------
1 |
2 | describe("SelectedColorsService test suite", function() {
3 | it("returns the default selected colors initially", function() {
4 | var service = new pskl.service.SelectedColorsService();
5 |
6 | expect(service.getPrimaryColor()).toBe(Constants.DEFAULT_PEN_COLOR);
7 | expect(service.getSecondaryColor()).toBe(Constants.TRANSPARENT_COLOR);
8 | });
9 |
10 | it("reacts to PRIMARY_COLOR_SELECTED event", function() {
11 | var service = new pskl.service.SelectedColorsService();
12 | service.init();
13 |
14 | var expectedColor = "#123456";
15 | $.publish(Events.PRIMARY_COLOR_SELECTED, [expectedColor]);
16 |
17 | expect(service.getPrimaryColor()).toBe(expectedColor);
18 | expect(service.getSecondaryColor()).toBe(Constants.TRANSPARENT_COLOR);
19 | });
20 |
21 | it("reacts to SECONDARY_COLOR_SELECTED event", function() {
22 | var service = new pskl.service.SelectedColorsService();
23 | service.init();
24 |
25 | var expectedColor = "#123456";
26 | $.publish(Events.SECONDARY_COLOR_SELECTED, [expectedColor]);
27 |
28 | expect(service.getPrimaryColor()).toBe(Constants.DEFAULT_PEN_COLOR);
29 | expect(service.getSecondaryColor()).toBe(expectedColor);
30 | });
31 | });
--------------------------------------------------------------------------------
/test/js/utils/ColorUtilsTest.js:
--------------------------------------------------------------------------------
1 | describe("Color utils", function() {
2 |
3 | beforeEach(function() {});
4 | afterEach(function() {});
5 |
6 | it("returns a color when provided with array of colors", function() {
7 | // when/then
8 | var unusedColor = pskl.utils.ColorUtils.getUnusedColor(['#ffff00', '#feff00', '#fdff00']);
9 | // verify
10 | expect(unusedColor).toBe('#FCFF00');
11 |
12 | // when/then
13 | unusedColor = pskl.utils.ColorUtils.getUnusedColor(['#fcff00', '#feff00', '#fdff00']);
14 | // verify
15 | expect(unusedColor).toBe('#FFFF00');
16 | });
17 |
18 | it("returns a color for an empty array", function() {
19 | // when/then
20 | var unusedColor = pskl.utils.ColorUtils.getUnusedColor([]);
21 | // verify
22 | expect(unusedColor).toBe('#FFFF00');
23 |
24 | // when/then
25 | unusedColor = pskl.utils.ColorUtils.getUnusedColor();
26 | // verify
27 | expect(unusedColor).toBe('#FFFF00');
28 | });
29 | });
--------------------------------------------------------------------------------
/test/js/utils/CoreTest.js:
--------------------------------------------------------------------------------
1 | describe("Core utils tests", function() {
2 |
3 | beforeEach(function() {});
4 | afterEach(function() {});
5 |
6 | it("colorToInt parses red", function() {
7 | var RED = 4278190335;
8 |
9 | expect(pskl.utils.colorToInt("red")).toBe(RED);
10 | expect(pskl.utils.colorToInt("rgb(255,0,0)")).toBe(RED);
11 | expect(pskl.utils.colorToInt("rgba(255,0,0,1)")).toBe(RED);
12 | expect(pskl.utils.colorToInt("#FF0000")).toBe(RED);
13 | expect(pskl.utils.colorToInt("#ff0000")).toBe(RED);
14 | expect(pskl.utils.colorToInt("#f00")).toBe(RED);
15 | expect(pskl.utils.colorToInt("#f00")).toBe(RED);
16 | });
17 |
18 | it("colorToInt parses white", function() {
19 | var WHITE = 4294967295;
20 |
21 | expect(pskl.utils.colorToInt("white")).toBe(WHITE);
22 | expect(pskl.utils.colorToInt("rgb(255,255,255)")).toBe(WHITE);
23 | expect(pskl.utils.colorToInt("rgba(255,255,255,1)")).toBe(WHITE);
24 | expect(pskl.utils.colorToInt("#FFFFFF")).toBe(WHITE);
25 | expect(pskl.utils.colorToInt("#ffffff")).toBe(WHITE);
26 | expect(pskl.utils.colorToInt("#FFF")).toBe(WHITE);
27 | expect(pskl.utils.colorToInt("#fff")).toBe(WHITE);
28 | });
29 |
30 | it("colorToInt parses transparent", function() {
31 | var TRANSPARENT = 0;
32 |
33 | expect(pskl.utils.colorToInt("transparent")).toBe(TRANSPARENT);
34 | expect(pskl.utils.colorToInt("rgba(100,120,150, 0)")).toBe(TRANSPARENT);
35 | expect(pskl.utils.colorToInt("rgba(255,255,255,0)")).toBe(TRANSPARENT);
36 | });
37 | });
--------------------------------------------------------------------------------
/test/js/utils/PixelUtilsTest_visit_connected.js:
--------------------------------------------------------------------------------
1 | describe("PixelUtils visitor methods tests", function() {
2 | var black = '#000000';
3 | var red = '#ff0000';
4 | var transparent = Constants.TRANSPARENT_COLOR;
5 | var B = black, R = red, T = transparent;
6 |
7 | beforeEach(function() {});
8 | afterEach(function() {});
9 |
10 | var containsPixel = function (pixels, col, row) {
11 | return pixels.some(function (p) {
12 | return p.col === col && p.row === row;
13 | });
14 | };
15 |
16 | it("getSimilarConnectedPixelsFromFrame works", function() {
17 | var frame = pskl.model.Frame.fromPixelGrid(test.testutils.toFrameGrid([
18 | [T, T, B],
19 | [B, T, B],
20 | [T, T, B],
21 | ]));
22 |
23 | var pixels = pskl.PixelUtils.getSimilarConnectedPixelsFromFrame(frame, 0, 0);
24 | expect(pixels.length).toBe(5);
25 | expect(containsPixel(pixels, 0, 0)).toBe(true);
26 | expect(containsPixel(pixels, 1, 0)).toBe(true);
27 | expect(containsPixel(pixels, 1, 1)).toBe(true);
28 | expect(containsPixel(pixels, 0, 2)).toBe(true);
29 | expect(containsPixel(pixels, 1, 2)).toBe(true);
30 |
31 | pixels = pskl.PixelUtils.getSimilarConnectedPixelsFromFrame(frame, -1, -1);
32 | expect(Array.isArray(pixels)).toBe(true);
33 | expect(pixels.length).toBe(0);
34 |
35 | pixels = pskl.PixelUtils.getSimilarConnectedPixelsFromFrame(frame, 0, 1);
36 | expect(pixels.length).toBe(1);
37 | expect(containsPixel(pixels, 0, 1)).toBe(true);
38 |
39 | pixels = pskl.PixelUtils.getSimilarConnectedPixelsFromFrame(frame, 2, 1);
40 | expect(pixels.length).toBe(3);
41 | expect(containsPixel(pixels, 2, 0)).toBe(true);
42 | expect(containsPixel(pixels, 2, 1)).toBe(true);
43 | expect(containsPixel(pixels, 2, 2)).toBe(true);
44 | });
45 | });
46 |
--------------------------------------------------------------------------------
/test/js/utils/UuidTest.js:
--------------------------------------------------------------------------------
1 | describe("UUID Generator", function() {
2 |
3 | beforeEach(function() {});
4 | afterEach(function() {});
5 |
6 | it("returns valid uuids", function() {
7 | // when
8 |
9 | // then
10 | var uuid1 = pskl.utils.Uuid.generate();
11 | var uuid2 = pskl.utils.Uuid.generate();
12 |
13 | // verify
14 | expect(typeof uuid1).toBe("string");
15 | expect(uuid1.length).toBe(36);
16 | var splits = uuid1.split('-');
17 | expect(splits.length).toBe(5);
18 |
19 | expect(splits[0].length).toBe(8);
20 | expect(splits[1].length).toBe(4);
21 | expect(splits[2].length).toBe(4);
22 | expect(splits[3].length).toBe(4);
23 | expect(splits[4].length).toBe(12);
24 |
25 | expect(uuid1).not.toBe(uuid2);
26 | });
27 | });
--------------------------------------------------------------------------------
/test/js/utils/serialization/Deserializer_v0Test.js:
--------------------------------------------------------------------------------
1 | describe("Deserializer v0 test", function() {
2 |
3 | var black = '#000000';
4 | var transparent = Constants.TRANSPARENT_COLOR;
5 | var data = [
6 | [
7 | ["#000000", "TRANSPARENT"],
8 | ["TRANSPARENT", "#000000"]
9 | ]
10 | ];
11 |
12 | it("deserializes data serialized for model v0 correctly", function (done) {
13 | var deserializer = pskl.utils.serialization.Deserializer;
14 | deserializer.deserialize(data, function (p) {
15 | // Check the frame has been properly deserialized
16 | expect(p.getLayerAt(0).getFrames().length).toBe(1);
17 | var frame = p.getLayerAt(0).getFrameAt(0);
18 | test.testutils.frameEqualsGrid(frame, [
19 | [black, transparent],
20 | [transparent, black]
21 | ]);
22 | done();
23 | });
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/test/js/utils/serialization/Deserializer_v1Test.js:
--------------------------------------------------------------------------------
1 | describe("Deserializer v1 test", function() {
2 | var B = '#000000';
3 | var T = Constants.TRANSPARENT_COLOR;
4 | var data = {
5 | "modelVersion": 1,
6 | "piskel": {
7 | "height": 2,
8 | "width": 2,
9 | "layers": [
10 | "{\"name\":\"Layer 1\",\"frames\":[\"[[\\\"#000000\\\",\\\"TRANSPARENT\\\"],[\\\"TRANSPARENT\\\",\\\"#000000\\\"]]\"]}"
11 | ]
12 | }
13 | };
14 |
15 | it("deserializes data serialized for model v0 correctly", function (done) {
16 | var deserializer = pskl.utils.serialization.Deserializer;
17 | deserializer.deserialize(data, function (p) {
18 | // Check the frame has been properly deserialized
19 | expect(p.getLayerAt(0).getFrames().length).toBe(1);
20 | var frame = p.getLayerAt(0).getFrameAt(0);
21 | test.testutils.frameEqualsGrid(frame, [
22 | [B, T],
23 | [T, B]
24 | ]);
25 | done();
26 | });
27 | });
28 | });
29 |
--------------------------------------------------------------------------------