Full Code of OliBridgman/gravit for AI

master 2105bdbdd483 cached
366 files
2.6 MB
690.1k tokens
444 symbols
1 requests
Download .txt
Showing preview only (2,757K chars total). Download the full file or copy to clipboard to get everything.
Repository: OliBridgman/gravit
Branch: master
Commit: 2105bdbdd483
Files: 366
Total size: 2.6 MB

Directory structure:
gitextract_2b0_wtzl/

├── .bowerrc
├── .gitignore
├── Gruntfile.js
├── LICENSE
├── README.md
├── assets/
│   └── cursor/
│       ├── cross.cur
│       ├── hand-closed.cur
│       ├── hand-open.cur
│       ├── lasso.cur
│       ├── pen-close.cur
│       ├── pen-drag.cur
│       ├── pen-end.cur
│       ├── pen-minus.cur
│       ├── pen-modify.cur
│       ├── pen-plus.cur
│       ├── pen-start.cur
│       ├── pen.cur
│       ├── pipette.cur
│       ├── select-arrow-only.cur
│       ├── select-cross.cur
│       ├── select-dot-inverse.cur
│       ├── select-dot.cur
│       ├── select-inverse.cur
│       ├── select-resize-horiz.cur
│       ├── select-resize-vert.cur
│       ├── select-rot-bc.cur
│       ├── select-rot-bl.cur
│       ├── select-rot-br.cur
│       ├── select-rot-lc.cur
│       ├── select-rot-rc.cur
│       ├── select-rot-tc.cur
│       ├── select-rot-tl.cur
│       ├── select-rot-tr.cur
│       ├── select-skew-horiz.cur
│       ├── select-skew-vert.cur
│       ├── select-upleft-downright.cur
│       ├── select-upright-downleft.cur
│       ├── select.cur
│       ├── zoom-minus.cur
│       ├── zoom-none.cur
│       └── zoom-plus.cur
├── bower.json
├── package.json
├── shell/
│   ├── browser/
│   │   ├── index.html
│   │   └── shell.js
│   ├── chrome/
│   │   ├── background.js
│   │   ├── filestorage.js
│   │   ├── index.html
│   │   ├── manifest.json
│   │   └── shell.js
│   └── system/
│       ├── Info.plist
│       ├── appicon.icns
│       ├── doc.icns
│       ├── filestorage.js
│       ├── index.html
│       ├── package/
│       │   └── osx/
│       │       ├── background.tiff
│       │       └── dmg.json
│       ├── package.json
│       ├── shell.js
│       └── winstate.js
├── src/
│   ├── application/
│   │   ├── application.js
│   │   ├── bootstrap.js
│   │   ├── component/
│   │   │   ├── autoedit.js
│   │   │   ├── blendmode.js
│   │   │   ├── colorbutton.js
│   │   │   ├── colorpanel.js
│   │   │   ├── cornertype.js
│   │   │   ├── gradienteditor.js
│   │   │   ├── menu.js
│   │   │   ├── menubar.js
│   │   │   ├── menubutton.js
│   │   │   ├── menuitem.js
│   │   │   ├── overlay.js
│   │   │   ├── panel.js
│   │   │   ├── patterntarget.js
│   │   │   ├── pivot.js
│   │   │   ├── stylepanel.js
│   │   │   ├── swatchpanel.js
│   │   │   └── unit.js
│   │   ├── document.js
│   │   ├── extension/
│   │   │   ├── action.js
│   │   │   ├── colormatcher.js
│   │   │   ├── exporter.js
│   │   │   ├── module.js
│   │   │   ├── palette.js
│   │   │   ├── panel.js
│   │   │   ├── properties.js
│   │   │   ├── sidebar.js
│   │   │   ├── storage.js
│   │   │   ├── styleentry.js
│   │   │   ├── transformer.js
│   │   │   └── view.js
│   │   ├── i18n/
│   │   │   ├── i18n_de.js
│   │   │   └── i18n_en.js
│   │   ├── shell.js
│   │   ├── util/
│   │   │   ├── ciede2000.js
│   │   │   ├── image.js
│   │   │   └── selectors.js
│   │   └── workspace/
│   │       ├── header.js
│   │       ├── palettes.js
│   │       ├── panels.js
│   │       ├── sidebars.js
│   │       ├── toolbar.js
│   │       ├── window.js
│   │       └── windows.js
│   ├── development/
│   │   ├── bootstrap.js
│   │   ├── development.js
│   │   ├── test/
│   │   │   ├── clone_scene.js
│   │   │   ├── create_multiple_pages.js
│   │   │   ├── create_rect_grid_page.js
│   │   │   ├── deserialize_scene.js
│   │   │   └── serialize_scene.js
│   │   └── testaction.js
│   ├── gravit/
│   │   ├── action/
│   │   │   ├── addlayeraction.js
│   │   │   ├── addpageaction.js
│   │   │   ├── alignaction.js
│   │   │   ├── arrangeaction.js
│   │   │   ├── cloneaction.js
│   │   │   ├── closeaction.js
│   │   │   ├── closeallaction.js
│   │   │   ├── copyaction.js
│   │   │   ├── copyattributesaction.js
│   │   │   ├── cutaction.js
│   │   │   ├── deleteaction.js
│   │   │   ├── deletelayeraction.js
│   │   │   ├── deletepageaction.js
│   │   │   ├── distributeaction.js
│   │   │   ├── duplicateaction.js
│   │   │   ├── fitallaction.js
│   │   │   ├── fitcurrentlayeraction.js
│   │   │   ├── fitcurrentpageaction.js
│   │   │   ├── fitselectionaction.js
│   │   │   ├── groupaction.js
│   │   │   ├── invertselectionaction.js
│   │   │   ├── layertypeaction.js
│   │   │   ├── magnificationaction.js
│   │   │   ├── newaction.js
│   │   │   ├── newwindowaction.js
│   │   │   ├── openaction.js
│   │   │   ├── originalviewaction.js
│   │   │   ├── paintmodeaction.js
│   │   │   ├── pasteaction.js
│   │   │   ├── pasteattributesaction.js
│   │   │   ├── pasteinplaceaction.js
│   │   │   ├── pasteinsideaction.js
│   │   │   ├── pixelpreviewaction.js
│   │   │   ├── placeimageaction.js
│   │   │   ├── redoaction.js
│   │   │   ├── saveaction.js
│   │   │   ├── saveallaction.js
│   │   │   ├── saveasaction.js
│   │   │   ├── selectallaction.js
│   │   │   ├── showallpagesaction.js
│   │   │   ├── showgridaction.js
│   │   │   ├── showrulersaction.js
│   │   │   ├── slicefromselection.js
│   │   │   ├── snapunitaction.js
│   │   │   ├── transformaction.js
│   │   │   ├── undoaction.js
│   │   │   ├── ungroupaction.js
│   │   │   ├── zoominaction.js
│   │   │   └── zoomoutaction.js
│   │   ├── colormatcher/
│   │   │   ├── analogousmatcher.js
│   │   │   └── complementarymatcher.js
│   │   ├── exporter/
│   │   │   └── imagexporter.js
│   │   ├── gravit.js
│   │   ├── i18n/
│   │   │   ├── i18n_de.js
│   │   │   └── i18n_en.js
│   │   ├── palette/
│   │   │   ├── exportpalette.js
│   │   │   └── stylepalette.js
│   │   ├── panel/
│   │   │   ├── propertiespanel.js
│   │   │   └── transformpanel.js
│   │   ├── properties/
│   │   │   ├── documentproperties.js
│   │   │   ├── ellipseproperties.js
│   │   │   ├── imageproperties.js
│   │   │   ├── infoproperties.js
│   │   │   ├── pageproperties.js
│   │   │   ├── pathproperties.js
│   │   │   ├── polygonproperties.js
│   │   │   ├── rectangleproperties.js
│   │   │   ├── sliceproperties.js
│   │   │   └── textproperties.js
│   │   ├── sidebar/
│   │   │   ├── pageslayerssidebar.js
│   │   │   └── stylesswatchessidebar.js
│   │   ├── styleentry/
│   │   │   ├── areapaintentry.js
│   │   │   ├── blurfilterentry.js
│   │   │   ├── fillpaintentry.js
│   │   │   ├── offsetveffectentry.js
│   │   │   ├── patternpaintentry.js
│   │   │   ├── shadoweffectentry.js
│   │   │   └── strokepaintentry.js
│   │   └── transformer/
│   │       ├── adjusttransformer.js
│   │       └── aligntransformer.js
│   ├── index.html
│   ├── infinity/
│   │   ├── core/
│   │   │   ├── cursor.js
│   │   │   ├── key.js
│   │   │   ├── locale.js
│   │   │   ├── math.js
│   │   │   ├── object.js
│   │   │   ├── system.js
│   │   │   └── util.js
│   │   ├── event/
│   │   │   ├── event.js
│   │   │   ├── eventtarget.js
│   │   │   ├── inputevent.js
│   │   │   ├── keyevent.js
│   │   │   └── mouseevent.js
│   │   ├── geometry/
│   │   │   ├── length.js
│   │   │   ├── point.js
│   │   │   ├── rect.js
│   │   │   └── transform.js
│   │   ├── i18n/
│   │   │   ├── i18n_de.js
│   │   │   └── i18n_en.js
│   │   ├── paint/
│   │   │   ├── annotation.js
│   │   │   ├── bitmap.js
│   │   │   ├── color.js
│   │   │   ├── colorprofile.js
│   │   │   ├── colorspace.js
│   │   │   ├── dirtylist.js
│   │   │   ├── font.js
│   │   │   ├── gradient.js
│   │   │   ├── paintcanvas.js
│   │   │   ├── paintconfiguration.js
│   │   │   ├── paintcontext.js
│   │   │   └── pattern.js
│   │   ├── platform.js
│   │   ├── scene/
│   │   │   ├── block.js
│   │   │   ├── element.js
│   │   │   ├── item.js
│   │   │   ├── node.js
│   │   │   ├── scene.js
│   │   │   ├── scenepaintconfiguration.js
│   │   │   ├── selector.js
│   │   │   ├── shape/
│   │   │   │   ├── ellipse.js
│   │   │   │   ├── image.js
│   │   │   │   ├── path.js
│   │   │   │   ├── pathbase.js
│   │   │   │   ├── polygon.js
│   │   │   │   ├── rectangle.js
│   │   │   │   ├── shape.js
│   │   │   │   ├── shapeset.js
│   │   │   │   └── text.js
│   │   │   ├── structure/
│   │   │   │   ├── layer.js
│   │   │   │   ├── page.js
│   │   │   │   ├── slice.js
│   │   │   │   └── swatch.js
│   │   │   └── style/
│   │   │       ├── appliedstyle.js
│   │   │       ├── effect/
│   │   │       │   └── shadoweffect.js
│   │   │       ├── effectentry.js
│   │   │       ├── filter/
│   │   │       │   └── blurfilter.js
│   │   │       ├── filterentry.js
│   │   │       ├── inlinestyle.js
│   │   │       ├── linkedstyle.js
│   │   │       ├── paint/
│   │   │       │   ├── areapaint.js
│   │   │       │   ├── fillpaint.js
│   │   │       │   ├── patternpaint.js
│   │   │       │   └── strokepaint.js
│   │   │       ├── paintentry.js
│   │   │       ├── sharedstyle.js
│   │   │       ├── style.js
│   │   │       ├── styleentry.js
│   │   │       ├── styleset.js
│   │   │       ├── veffect/
│   │   │       │   └── offsetveffect.js
│   │   │       └── veffectentry.js
│   │   ├── vertex/
│   │   │   ├── vertex.js
│   │   │   ├── vertexcontainer.js
│   │   │   ├── vertexinfo.js
│   │   │   ├── vertexoffsetter.js
│   │   │   ├── vertexpixelaligner.js
│   │   │   ├── vertexsource.js
│   │   │   ├── vertextarget.js
│   │   │   └── vertextransformer.js
│   │   └── view/
│   │       ├── scenestage.js
│   │       ├── stage.js
│   │       ├── view.js
│   │       └── widget.js
│   ├── infinity-editor/
│   │   ├── editor.js
│   │   ├── editorpaintconfiguration.js
│   │   ├── guide/
│   │   │   ├── gridguide.js
│   │   │   ├── guide.js
│   │   │   ├── guides.js
│   │   │   ├── pageguide.js
│   │   │   ├── shapeboxguide.js
│   │   │   └── unitguide.js
│   │   ├── i18n/
│   │   │   ├── i18n_de.js
│   │   │   └── i18n_en.js
│   │   ├── scene/
│   │   │   ├── blockeditor.js
│   │   │   ├── elementeditor.js
│   │   │   ├── sceneeditor.js
│   │   │   ├── shape/
│   │   │   │   ├── ellipseeditor.js
│   │   │   │   ├── imageeditor.js
│   │   │   │   ├── pathbaseeditor.js
│   │   │   │   ├── patheditor.js
│   │   │   │   ├── polygoneditor.js
│   │   │   │   ├── rectangleeditor.js
│   │   │   │   ├── shapeeditor.js
│   │   │   │   ├── shapeseteditor.js
│   │   │   │   └── texteditor.js
│   │   │   ├── structure/
│   │   │   │   ├── layereditor.js
│   │   │   │   ├── pageeditor.js
│   │   │   │   └── sliceeditor.js
│   │   │   └── transformbox.js
│   │   ├── tool/
│   │   │   ├── bezigontool.js
│   │   │   ├── ellipsetool.js
│   │   │   ├── handtool.js
│   │   │   ├── lassotool.js
│   │   │   ├── layertool.js
│   │   │   ├── linetool.js
│   │   │   ├── marqueetool.js
│   │   │   ├── pagetool.js
│   │   │   ├── pathtool.js
│   │   │   ├── pentool.js
│   │   │   ├── pointertool.js
│   │   │   ├── polygontool.js
│   │   │   ├── rectangletool.js
│   │   │   ├── selecttool.js
│   │   │   ├── shapetool.js
│   │   │   ├── slicetool.js
│   │   │   ├── subselecttool.js
│   │   │   ├── texttool.js
│   │   │   ├── tool.js
│   │   │   ├── toolmanager.js
│   │   │   ├── transformtool.js
│   │   │   └── zoomtool.js
│   │   └── view/
│   │       ├── editorbackstage.js
│   │       ├── editorfrontstage.js
│   │       ├── editorscenestage.js
│   │       ├── editortoolstage.js
│   │       └── editorview.js
│   └── package.json
├── style/
│   ├── _base.scss
│   ├── _contenteditable.scss
│   ├── _cursors.scss
│   ├── _fonts.scss
│   ├── _input.scss
│   ├── _variables.scss
│   ├── component/
│   │   ├── _colorpanel.scss
│   │   ├── _colorswatch.scss
│   │   ├── _form.scss
│   │   ├── _gradienteditor.scss
│   │   ├── _menu.scss
│   │   ├── _overlay.scss
│   │   ├── _panel.scss
│   │   ├── _pivot.scss
│   │   ├── _stylepanel.scss
│   │   ├── _swatchpanel.scss
│   │   └── _tree.scss
│   ├── gravit.scss
│   ├── module/
│   │   ├── palette/
│   │   │   ├── _export.scss
│   │   │   └── _style.scss
│   │   ├── panel/
│   │   │   ├── _properties.scss
│   │   │   └── _transform.scss
│   │   └── sidebar/
│   │       └── _pageslayerssidebar.scss
│   └── workspace/
│       ├── _header.scss
│       ├── _palettes.scss
│       ├── _panels.scss
│       ├── _sidebars.scss
│       ├── _toolbar.scss
│       ├── _windows.scss
│       └── _workspace.scss
└── test/
    ├── index.html
    ├── lib/
    │   ├── chai.js
    │   ├── expect.js
    │   └── mocha/
    │       ├── mocha.css
    │       └── mocha.js
    └── spec/
        ├── infinity/
        │   └── pathmodel.js
        └── test.js

================================================
FILE CONTENTS
================================================

================================================
FILE: .bowerrc
================================================
{
    "directory" : "bower_components"
}

================================================
FILE: .gitignore
================================================
.idea
.tmp
.sass-cache
node_modules
node-webkit
bower_components
build
dist
tmp

================================================
FILE: Gruntfile.js
================================================
var exec = require('child_process').exec;

module.exports = function (grunt) {
    require('load-grunt-tasks')(grunt);

    var pgk = grunt.file.readJSON('package.json');

    var cfg = {
        build: 'build',
        dist: 'dist',
        tmp: 'tmp',
        macBundleId: 'com.quasado.gravit',
        macSignIdentity: '1269B5CE3B0DCC676DA70011A618EB6FA95F8F50'
    };

    grunt.initConfig({
        cfg: cfg,
        pkg: pgk,

        watch: {
            compass: {
                files: ['style/{,*/}*.{scss,sass}'],
                tasks: ['compass']
            },
            livereload: {
                options: {
                    livereload: '<%= connect.options.livereload %>'
                },
                files: [
                    'src/*.html',
                    '<%= cfg.tmp %>/{,*/}*.css',
                    '{<%= cfg.tmp %>,src/{,*/}*.js,test/{,*/}*.js',
                    'assets/image/{,*/}*.{png,jpg,jpeg,gif,webp,svg}'
                ]
            }
        },
        connect: {
            options: {
                port: 8999,
                livereload: 35728,
                hostname: 'localhost'
            },
            livereload: {
                options: {
                    open: true,
                    base: [
                        '<%= cfg.tmp %>',
                        'assets',
                        'src',
                        '.'
                    ]
                }
            },
            test: {
                options: {
                    base: [
                        '<%= cfg.tmp %>',
                        'assets',
                        'test',
                        'src',
                        '.'
                    ]
                }
            }
        },
        clean: {
            dev: '<%= cfg.tmp %>',
            build: '<%= cfg.build %>',
            dist: '<%= cfg.dist %>'
        },
        mocha: {
            all: {
                options: {
                    run: true,
                    urls: ['http://<%= connect.test.options.hostname %>:<%= connect.test.options.port %>/index.html']
                }
            }
        },
        compass: {
            options: {
                sassDir: 'style',
                cssDir: '<%= cfg.tmp %>',
                generatedImagesDir: '<%= cfg.tmp %>/image/generated',
                imagesDir: 'assets/image/images',
                javascriptsDir: 'src',
                fontsDir: '<%= cfg.tmp %>/font',
                httpImagesPath: '/image',
                httpGeneratedImagesPath: '/image/generated',
                httpFontsPath: '/font',
                relativeAssets: false
            },
            dev: {
                options: {
                    debugInfo: true
                }
            },
            build: {
                options: {
                    debugInfo: false,
                    generatedImagesDir: '<%= cfg.build %>/source/image/generated'
                }
            }
        },
        concat: {
            build: {
                files: {
                    '<%= cfg.build %>/browser/gravit-shell.js': ['shell/browser/*.js'],
                    '<%= cfg.build %>/chrome/gravit-shell.js': ['shell/chrome/*.js', '!shell/chrome/background.js'],
                    '<%= cfg.build %>/system/gravit-shell.js': ['shell/system/*.js']
                }
            }
        },
        uglify: {
            build: {
                files: {
                    '<%= cfg.build %>/browser/gravit-shell.js': ['<%= cfg.build %>/browser/gravit-shell.js'],
                    '<%= cfg.build %>/chrome/gravit-shell.js': ['<%= cfg.build %>/chrome/gravit-shell.js'],
                    '<%= cfg.build %>/system/gravit-shell.js': ['<%= cfg.build %>/system/gravit-shell.js']
                }
            }
        },
        copy: {
            dev: {
                files: [
                    {
                        expand: true,
                        dot: true,
                        cwd: 'bower_components/font-awesome/fonts',
                        dest: '<%= cfg.tmp %>/font/',
                        src: '{,*/}*.*'
                    }
                ]
            },
            preBuild: {
                files: [
                    // Source Assets
                    {
                        expand: true,
                        dot: true,
                        cwd: 'bower_components/font-awesome/fonts',
                        dest: '<%= cfg.build %>/source/font/',
                        src: '{,*/}*.*'
                    },
                    {
                        expand: true,
                        dot: true,
                        cwd: 'assets/cursor',
                        dest: '<%= cfg.build %>/source/cursor/',
                        src: '{,*/}*.*'
                    },
                    {
                        expand: true,
                        dot: true,
                        cwd: 'assets/font',
                        dest: '<%= cfg.build %>/source/font/',
                        src: '{,*/}*.*'
                    },

                    // Chrome Assets
                    {
                        expand: true,
                        dot: true,
                        cwd: 'bower_components/jquery/dist/',
                        dest: '<%= cfg.build %>/chrome/',
                        src: 'jquery.min.js'
                    },

                    // System assets
                    {
                        expand: true,
                        dot: true,
                        cwd: 'bower_components/jquery/dist/',
                        dest: '<%= cfg.build %>/system/',
                        src: 'jquery.min.js'
                    }
                ]
            },
            postBuild: {
                files: [
                    // Copy some files for mac binary
                    {
                        expand: true,
                        cwd: '<%= cfg.build %>/system/',
                        dest: '<%= cfg.build %>/system-binaries/Gravit/osx/Gravit.app/Contents/',
                        src: ['Info.plist']
                    },
                    {
                        expand: true,
                        cwd: 'shell/system/',
                        dest: '<%= cfg.build %>/system-binaries/Gravit/osx/Gravit.app/Contents/Resources/',
                        src: ['doc.icns']
                    }
                ]
            },
            build: {
                files: [
                    // Browser
                    {
                        expand: true,
                        cwd: '<%= cfg.build %>/source/',
                        dest: '<%= cfg.build %>/browser/',
                        src: '{,*/}*.*'
                    },
                    {
                        expand: true,
                        cwd: 'assets/icon/',
                        dest: '<%= cfg.build %>/browser/icon',
                        src: ['**']
                    },
                    {
                        expand: true,
                        cwd: 'shell/browser/',
                        dest: '<%= cfg.build %>/browser/',
                        src: ['index.html']
                    },

                    // Infinity
                    {
                        expand: true,
                        cwd: '<%= cfg.build %>/source/',
                        dest: '<%= cfg.build %>/infinity/',
                        src: ['cursor/*.*', 'infinity-**.js']
                    },

                    // Chrome
                    {
                        expand: true,
                        cwd: '<%= cfg.build %>/chrome/',
                        dest: '<%= cfg.build %>/chrome/',
                        src: ['**']
                    },
                    {
                        expand: true,
                        cwd: '<%= cfg.build %>/source/',
                        dest: '<%= cfg.build %>/chrome/',
                        src: ['**']
                    },
                    {
                        expand: true,
                        cwd: 'shell/chrome/',
                        dest: '<%= cfg.build %>/chrome/',
                        src: ['index.html', 'manifest.json', 'background.js']
                    },
                    {
                        expand: true,
                        cwd: 'assets/icon/',
                        dest: '<%= cfg.build %>/chrome/icon',
                        src: ['**']
                    },

                    // System
                    {
                        expand: true,
                        cwd: '<%= cfg.build %>/source/',
                        dest: '<%= cfg.build %>/system/',
                        src: ['**']
                    },
                    {
                        expand: true,
                        cwd: 'shell/system/',
                        dest: '<%= cfg.build %>/system/',
                        src: ['index.html', 'package.json', 'Info.plist']
                    }
                ]
            },
            dist: {
                files: [
                    // Browser
                    {
                        expand: true,
                        cwd: '<%= cfg.build %>/browser/',
                        dest: '<%= cfg.dist %>/browser/',
                        src: ['**']
                    },

                    // Infinity
                    {
                        expand: true,
                        cwd: '<%= cfg.build %>/infinity/',
                        dest: '<%= cfg.dist %>/infinity/',
                        src: ['**']
                    }
                ]
            }
        },
        replace: {
            build: {
                src: ['<%= cfg.build %>/system/package.json', '<%= cfg.build %>/system/Info.plist', '<%= cfg.build %>/chrome/manifest.json'],
                overwrite: true,
                replacements: [
                    {
                        from: '%name%',
                        to: '<%= pkg.name %>'
                    },
                    {
                        from: '%description%',
                        to: '<%= pkg.description %>'
                    },
                    {
                        from: '%version%',
                        to: '<%= pkg.version %>'
                    },
                    {
                        from: '%mac-bundle-id%',
                        to: '<%= cfg.macBundleId %>'
                    }
                ]
            }
        },
        useminPrepare: {
            options: {
                dest: '<%= cfg.build %>/source'
            },
            html: 'src/index.html'
        },
        usemin: {
            options: {
                dirs: ['<%= cfg.build %>/source']
            },
            html: ['<%= cfg.build %>/source/{,*/}*.html'],
            css: ['<%= cfg.build %>/source/{,*/}*.css']
        },
        nodewebkit: {
            options: {
                version: '0.10.1',
                platforms: ['win', 'osx', 'linux64'],
                cacheDir: './node-webkit',
                buildDir: '<%= cfg.build %>/system-binaries',
                macIcns: 'shell/system/appicon.icns',
                macZip: false,
                winIco: 'shell/system/appicon.ico'
            },
            src: '<%= cfg.build %>/system/**/*'
        }
    });

    // Private tasks
    grunt.registerTask('_dist_osx', function () {
        var done = this.async();

        var gravitAppDir = cfg.build + '/system-binaries/Gravit/osx/Gravit.app';

        var commands = [
            // sign
            'codesign --deep -f -v -s ' + cfg.macSignIdentity + ' -i ' + cfg.macBundleId + ' "' + gravitAppDir + '/Contents/Frameworks/node-webkit Helper.app"',
            'codesign --deep -f -v -s ' + cfg.macSignIdentity + ' -i ' + cfg.macBundleId + ' "' + gravitAppDir + '/Contents/Frameworks/node-webkit Helper EH.app"',
            'codesign --deep -f -v -s ' + cfg.macSignIdentity + ' -i ' + cfg.macBundleId + ' "' + gravitAppDir + '/Contents/Frameworks/node-webkit Helper NP.app"',
            'codesign --deep -f -v -s ' + cfg.macSignIdentity + ' -i ' + cfg.macBundleId + ' "' + gravitAppDir + '"',

            // verify
            'spctl --assess -vvvv "' + gravitAppDir + '/Contents/Frameworks/node-webkit Helper.app"',
            'spctl --assess -vvvv "' + gravitAppDir + '/Contents/Frameworks/node-webkit Helper EH.app"',
            'spctl --assess -vvvv "' + gravitAppDir + '/Contents/Frameworks/node-webkit Helper NP.app"',
            'spctl --assess -vvvv "' + gravitAppDir + '"',

            // package
            'test -f ./dist/gravit-osx.dmg && rm ./dist/gravit-osx.dmg',
            'mkdir ./dist',
            './node_modules/appdmg/bin/appdmg ./shell/system/package/osx/dmg.json ' + cfg.dist + '/gravit-osx.dmg'
        ];

        console.log('Sign & Package for OS-X');

        var index = 0;

        var _exec = function () {
            exec(commands[index], function (error, stdout, stderr) {
                if (stdout) console.log(stdout);
                if (stderr) console.log(stderr);
                if (error !== null) {
                    console.log('exec error: ' + error);
                }

                if (++index < commands.length) {
                    _exec();
                } else {
                    done();
                }
            });
        }

        _exec();
    })

    grunt.registerTask('_dist_win', function () {
        // TODO : Build installer
        var done = this.async();

        exec('zip -r -X ../../../../' + cfg.dist + '/gravit-windows.zip *', {cwd: cfg.build + '/system-binaries/Gravit/win'}, function (error, stdout, stderr) {
            if (stdout) console.log(stdout);
            if (stderr) console.log(stderr);
            if (error !== null) {
                console.log('exec error: ' + error);
            }
            done();
        });
    })

    grunt.registerTask('_dist_linux', function () {
        var done = this.async();

        exec('zip -r -X ../../../../' + cfg.dist + '/gravit-linux64.zip *', {cwd: cfg.build + '/system-binaries/Gravit/linux64'}, function (error, stdout, stderr) {
            if (stdout) console.log(stdout);
            if (stderr) console.log(stderr);
            if (error !== null) {
                console.log('exec error: ' + error);
            }
            done();
        });
    })

    grunt.registerTask('_dist_chrome', function () {
        var done = this.async();

        exec('zip -r -X ../../' + cfg.dist + '/gravit-chrome.zip *', {cwd: cfg.build + '/chrome'}, function (error, stdout, stderr) {
            if (stdout) console.log(stdout);
            if (stderr) console.log(stderr);
            if (error !== null) {
                console.log('exec error: ' + error);
            }
            done();
        });
    })

    // Public tasks
    grunt.registerTask('dev', function (target) {
        grunt.task.run([
            'clean:dev',
            'compass:dev',
            'copy:dev',
            'connect:livereload',
            'watch'
        ]);
    });

    grunt.registerTask('test', function (target) {
        grunt.task.run([
            'clean:dev',
            'compass:dev',
            'copy:dev',
            'connect:test',
            'mocha'
        ]);
    });

    grunt.registerTask('build', function (target) {
        grunt.task.run([
            'clean:build',
            'useminPrepare',
            'compass:build',
            'concat',
            'cssmin',
            'uglify',
            'usemin',
            'copy:preBuild',
            'copy:build',
            'replace:build',
            'nodewebkit',
            'copy:postBuild'
        ]);
    });

    grunt.registerTask('dist', function (target) {
        grunt.task.run([
            'test',
            'build',
            'clean:dist',
            'copy:dist',
            '_dist_osx',
            '_dist_linux',
            '_dist_win',
            '_dist_chrome'
        ]);
    });


    // By default we'll run the development task
    grunt.registerTask('default', [
        'dev'
    ]);
};

================================================
FILE: LICENSE
================================================
Gravit - The versatile, cross-platform design tool
Copyright (C) 2012-2014 Quasado GmbH, Quasado e.K.

The name Gravit and the Gravit Logo as well as all related logos are
exclusive trademarks of Quasado GmbH, Quasado e.K. and may not be used
without prior written permission.

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

In addition to the GPL, complete source code means all the source code for
all modules it contains made available as non-obfuscated source code.

================================================
FILE: README.md
================================================
## Introduction

Gravit is a design tool for Mac, Windows, Linux, ChromeOS and the Browser made
in the spirit for Freehand and Fireworks. It is completely written in HTML5,
Javascript and CSS3. Gravit consists of the core engine called "Infinity", the
actual Application and the core Module called "Gravit".

We'd like to encourage everyone in getting involved with this project. You can
develop new features or take a ticket and fix it. Or if you're a UX/Designer, you
could help designing new icons or improving the UI. To get started contributing,
read the [GitHub Guide](https://guides.github.com/activities/contributing-to-open-source/)

## Prerequisites

* NodeJS + NPM
* Grunt Client
* Bower
* SASS + Compass

## Quick Start

Install all prerequisites and make sure they're available on your path.

Then run `npm install` to install all nodejs dependencies
Then run `bower install` to install all client javascript libraries

Finally run `grunt`. You can then open Gravit in your
webbrowser at http://127.0.0.1:8999/.

We recommend using Chrome as this is the browser also used for the standalone
version.

## Quick Overview

+ assets - contains all relevant assets like fonts, images, etc.
+ shell - contains platform-specific code for standalone version
+ src - contains all source code
  + application - contains the application framework
  + development - contains the development addon automatically loaded when developing
  + gravit - contains the core module that is loaded by the application and provides all UI of Gravit
  + infinity - contains the core rendering engine as well as core classes used everywhere else
  + infinity-editor - contains editors, tools, guides and more based on infinity
+ style - contains all styling files for the application
+ test - contains all test files

## Community

Issues are being tracked here on GitHub.

## License

`Gravit`'s code in this repo uses the GPL license, see our `LICENSE` file for detailed information.
The name Gravit and the Gravit Logo as well as all related logos are exclusive trademarks of
Quasado GmbH, Quasado e.K. and may not be used without prior written permission.

`Gravit`'s code is also available as a commercial license. For more information, contact us.

================================================
FILE: bower.json
================================================
{
    "name": "gravit",
    "version": "0.0.1",
    "dependencies": {
        "jquery": "~2.1.1",
        "mousetrap": "latest",
        "font-awesome": "latest",
        "jqtree": "latest",
        "opentype.js": "git://github.com/nodebox/opentype.js.git#latest",
        "pako": "latest",
        "rangy": "latest",
        "color-thief": "latest",
        "uri.js": "latest",
        "Blob.js": "git://github.com/eligrey/Blob.js.git#latest",
        "canvas-toBlob.js": "git://github.com/eligrey/canvas-toBlob.js.git#latest"
    },
    "devDependencies": {},
    "private": true
}


================================================
FILE: package.json
================================================
{
    "name": "Gravit",
    "description" : "Gravit, the versatile Design Tool",
    "author" : "Quasado",
    "version": "0.0.3.1",
    "private": true,
    "devDependencies": {
        "grunt": "~0.4.1",
        "grunt-contrib-copy": "~0.5.0",
        "grunt-contrib-concat": "~0.3.0",
        "grunt-contrib-uglify": "~0.2.0",
        "grunt-contrib-compass": "~0.5.0",
        "grunt-contrib-jshint": "~0.6.3",
        "grunt-contrib-cssmin": "~0.6.0",
        "grunt-contrib-connect": "~0.5.0",
        "grunt-contrib-clean": "~0.5.0",
        "grunt-contrib-htmlmin": "~0.1.3",
        "grunt-bower-install": "~0.5.0",
        "grunt-contrib-imagemin": "~0.2.0",
        "grunt-contrib-watch": "~0.5.2",
        "grunt-rev": "~0.1.0",
        "grunt-autoprefixer": "~0.2.0",
        "grunt-usemin": "~0.1.10",
        "grunt-mocha": "~0.4.0",
        "grunt-svgmin": "~0.2.0",
        "grunt-concurrent": "~0.3.0",
        "grunt-text-replace": "~0.3.12",
        "grunt-node-webkit-builder": "0.2.0",
        "load-grunt-tasks": "~0.1.0",
        "time-grunt": "~0.1.1",
        "appdmg": "^0.2.0"
    }
}

================================================
FILE: shell/browser/index.html
================================================
<!doctype html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>Gravit</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"/>
    <meta name="apple-mobile-web-app-capable" content="yes"/>
    <meta name="apple-mobile-web-app-title" content="Gravit">
    <link rel="shortcut icon" href="icon/icon_16x16.ico" type="image/x-icon"/>
    <link rel="apple-touch-icon-precomposed" sizes="144x144" href="icon/icon_144x144.png">
    <link rel="apple-touch-icon-precomposed" sizes="114×114" href="icon/icon_114x114.png">
    <link rel="apple-touch-icon-precomposed" sizes="72×72" href="icon/icon_72x72.png">
    <link rel="apple-touch-icon-precomposed" href="icon/icon_57x57.png">
    <link rel="stylesheet" href="gravit.css">
</head>
<body>
<script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
<script src="infinity-libraries.js"></script>
<script src="infinity-core.js"></script>
<script src="infinity-editor.js"></script>
<script src="gravit.js"></script>
<script src="gravit-shell.js"></script>
</body>
</html>


================================================
FILE: shell/browser/shell.js
================================================
(function (_) {
    /**
     * The browser shell
     * @class GBrowserShell
     * @extends GShell
     * @constructor
     */
    function GBrowserShell() {
        this._menuBar = new GMenuBar();
        this._clipboardMimeTypes = {};
    };
    IFObject.inherit(GBrowserShell, GShell);

    /**
     * @type {GMenuBar}
     * @private
     */
    GBrowserShell.prototype._menuBar = null;

    /**
     * @type {*}
     * @private
     */
    GBrowserShell.prototype._clipboardMimeTypes = null;

    /** @override */
    GBrowserShell.prototype.isDevelopment = function () {
        return document.location.hostname === 'localhost' || document.location.hostname === '127.0.0.1';
    };

    /** @override */
    GBrowserShell.prototype.prepare = function () {
        // Append our menu bar element as first child of header
        var menuElement = this._menuBar._htmlElement;
        menuElement
            .css('height', '100%')
            .prependTo($('#header'));
    };

    /** @override */
    GBrowserShell.prototype.addMenu = function (parentMenu, title, callback) {
        parentMenu = parentMenu || this._menuBar.getMenu();
        var item = new GMenuItem(GMenuItem.Type.Menu);
        item.setCaption(title);
        parentMenu.addItem(item);

        if (callback) {
            item.getMenu().addEventListener(GMenu.OpenEvent, callback);
        }

        return item.getMenu();
    };

    /** @override */
    GBrowserShell.prototype.addMenuSeparator = function (parentMenu) {
        var item = new GMenuItem(GMenuItem.Type.Divider);
        parentMenu.addItem(item);
        return item;
    };

    /** @override */
    GBrowserShell.prototype.addMenuItem = function (parentMenu, title, checkable, shortcut, callback) {
        var item = new GMenuItem(GMenuItem.Type.Item);
        if (callback) {
            item.addEventListener(GMenuItem.ActivateEvent, callback);
        }

        if (shortcut) {
            gApp.registerShortcut(shortcut, function () {
                callback();
            }.bind(this));

            item.setShortcutHint(shortcut);
        }

        this.updateMenuItem(item, title, true, false);
        parentMenu.addItem(item);
        return item;
    };

    /** @override */
    GBrowserShell.prototype.updateMenuItem = function (item, title, enabled, checked) {
        item.setCaption(title);
        item.setEnabled(enabled);
        item.setChecked(checked);
    };

    /** @override */
    GBrowserShell.prototype.removeMenuItem = function (parentMenu, child) {
        parentMenu.removeItem(parentMenu.indexOf(child));
    };

    /** @override */
    GBrowserShell.prototype.getClipboardMimeTypes = function () {
        return this._clipboardMimeTypes ? Object.keys(this._clipboardMimeTypes) : null;
    };

    /** @override */
    GBrowserShell.prototype.getClipboardContent = function (mimeType) {
        if (this._clipboardMimeTypes && this._clipboardMimeTypes.hasOwnProperty(mimeType)) {
            return this._clipboardMimeTypes[mimeType];
        }
        return null;
    };

    /** @override */
    GBrowserShell.prototype.setClipboardContent = function (mimeType, content) {
        this._clipboardMimeTypes[mimeType] = content;
    };

    _.gShell = new GBrowserShell;

    $(document).ready(function () {
        gShellReady();
    });

    $(window).load(function () {
        gShellFinished();
    });
})(this);


================================================
FILE: shell/chrome/background.js
================================================
// TODO : Load/Store window state
chrome.app.runtime.onLaunched.addListener(function() {
    chrome.app.window.create('index.html', {
        'bounds': {
            'width': 1024,
            'height': 768
        }
    });
});

================================================
FILE: shell/chrome/filestorage.js
================================================
(function (_) {
    /**
     * The file storage class for chrome
     * @constructor
     */
    function GFileStorage() {
        this._urlEntryMap = {};
    };
    IFObject.inherit(GFileStorage, GStorage);

    /**
     * @type {*}
     * @private
     */
    GFileStorage.prototype._urlEntryMap = null;

    /** @override */
    GFileStorage.prototype.isAvailable = function () {
        return true;
    };

    /** @override */
    GFileStorage.prototype.isSaving = function () {
        return true;
    };

    /** @override */
    GFileStorage.prototype.isPrompting = function () {
        return true;
    };

    /** @override */
    GFileStorage.prototype.getProtocol = function () {
        return 'file';
    };

    /** @override */
    GFileStorage.prototype.getExtensions = function () {
        return null;
    };

    /** @override */
    GFileStorage.prototype.getName = function () {
        // TODO : I18N
        return 'File';
    };

    /** @override */
    GFileStorage.prototype.openResourcePrompt = function (reference, extensions, done) {
        chrome.fileSystem.chooseEntry(
            {
                type: 'openWritableFile',
                acceptsAllTypes: !extensions,
                accepts: [
                    {
                        extensions: extensions
                    },
                ]
            },
            function (entry) {
                if (entry) {
                    this._addEntryMapping(entry, done);
                }
            }.bind(this));
    };

    /** @override */
    GFileStorage.prototype.saveResourcePrompt = function (reference, proposedName, extension, done) {
        chrome.fileSystem.chooseEntry(
            {
                type: 'saveFile',
                suggestedName: proposedName,
                acceptsAllTypes: !extension,
                accepts: [
                    {
                        extensions: [extension]
                    },
                ]
            },
            function (entry) {
                if (entry) {
                    this._addEntryMapping(entry, done);
                }
            }.bind(this));
    };

    /** @override */
    GFileStorage.prototype.load = function (url, binary, done) {
        if (!this._urlEntryMap.hasOwnProperty(url)) {
            throw new Error('No file-entry for url ' + url);
        }

        var entry = this._urlEntryMap[url].entry;
        var name = this._extractFileName(url);

        entry.file(function (file) {
            var reader = new FileReader();

            reader.onerror = function (e) {
                console.log('read_error on ' + url);
                console.log(e);
            }
            reader.onloadend = function (e) {
                done(e.target.result, name);
            };

            if (binary) {
                reader.readAsArrayBuffer(file);
            } else {
                reader.readAsText(file);
            }
        });
    };

    /** @override */
    GFileStorage.prototype.save = function (url, data, binary, done) {
        if (!this._urlEntryMap.hasOwnProperty(url)) {
            throw new Error('No file-entry for url ' + url);
        }

        var entry = this._urlEntryMap[url].entry;
        var name = this._extractFileName(url);

        entry.createWriter(function (writer) {
            writer.onerror = function (e) {
                console.log('write_error on ' + url);
                console.log(e);
            }
            writer.onwriteend = function (e) {
                if (done) {
                    done(name);
                }
            };
            writer.write(new Blob([data], {type: binary ? 'text/plain' : 'text/plain'}));
        }, function (e) {
            console.log('create_writer_error on ' + url);
        });
    };

    /** @override */
    GFileStorage.prototype.releaseUrl = function (url) {
        if (this._urlEntryMap.hasOwnProperty(url)) {
            if (--this._urlEntryMap[url].usage === 0) {
                delete this._urlEntryMap[url];
            }
        }
    };

    /** @override */
    GFileStorage.prototype.resolveUrl = function (url, resolved) {
        // Our file:/// protocol is understandable by the browser
        // so just use the source url
        resolved(url);
    };

    /**
     * @private
     */
    GFileStorage.prototype._extractFileName = function (path) {
        var lastSlash = path.lastIndexOf('/');
        if (lastSlash < 0) {
            lastSlash = path.lastIndexOf('\\');
        }
        if (lastSlash >= 0) {
            var lastDot = path.lastIndexOf('.');
            if (lastDot > 0) {
                return path.substr(lastSlash + 1, lastDot - lastSlash - 1);
            } else {
                return path.substr(lastSlash + 1);
            }
        }
    };

    /** @private */
    GFileStorage.prototype._addEntryMapping = function (entry, done) {
        chrome.fileSystem.getDisplayPath(entry, function (path) {
            var url = this.getProtocol() + '://' + ifUtil.replaceAll(path, '\\', '/');

            if (!this._urlEntryMap.hasOwnProperty(url)) {
                this._urlEntryMap[url] = {
                    usage: 1,
                    entry: entry
                };
            } else {
                this._urlEntryMap[url].usage++;
            }

            done(url);
        }.bind(this));
    };

    _.GFileStorage = GFileStorage;
})(this);


================================================
FILE: shell/chrome/index.html
================================================
<!doctype html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>Gravit</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"/>
    <meta name="apple-mobile-web-app-capable" content="yes"/>
    <meta name="apple-mobile-web-app-title" content="Gravit">
    <link rel="shortcut icon" href="icon/icon_16x16.ico" type="image/x-icon"/>
    <link rel="apple-touch-icon-precomposed" sizes="144x144" href="icon/icon_144x144.png">
    <link rel="apple-touch-icon-precomposed" sizes="114×114" href="icon/icon_114x114.png">
    <link rel="apple-touch-icon-precomposed" sizes="72×72" href="icon/icon_72x72.png">
    <link rel="apple-touch-icon-precomposed" href="icon/icon_57x57.png">
    <link rel="stylesheet" href="gravit.css">
</head>
<body>
<script src="jquery.min.js"></script>
<script src="infinity-libraries.js"></script>
<script src="infinity-core.js"></script>
<script src="infinity-editor.js"></script>
<script src="gravit.js"></script>
<script src="gravit-shell.js"></script>
</body>
</html>


================================================
FILE: shell/chrome/manifest.json
================================================
{
    "name": "%name%",
    "description": "%description%",
    "version": "%version%",
    "manifest_version": 2,
    "minimum_chrome_version": "36",
    "app": {
        "background": {
            "scripts": ["background.js"]
        }
    },
    "icons": {
        "16": "icon/icon_16x16.png",
        "128": "icon/icon_128x128.png"
    },
    "permissions": [
        "clipboardRead",
        "clipboardWrite",
        {
            "fileSystem": ["write", "retainEntries", "directory"]
        },
        "storage"
    ]
}

================================================
FILE: shell/chrome/shell.js
================================================
(function (_) {
    /**
     * The chrome shell
     * @class GChromeShell
     * @extends GShell
     * @constructor
     */
    function GChromeShell() {
        this._menuBar = new GMenuBar();
        this._clipboardMimeTypes = {};
    };
    IFObject.inherit(GChromeShell, GShell);

    /**
     * @type {GMenuBar}
     * @private
     */
    GChromeShell.prototype._menuBar = null;

    /**
     * @type {*}
     * @private
     */
    GChromeShell.prototype._clipboardMimeTypes = null;

    /** @override */
    GChromeShell.prototype.isDevelopment = function () {
        return document.location.hostname === 'localhost' || document.location.hostname === '127.0.0.1';
    };

    /** @override */
    GChromeShell.prototype.prepare = function () {
        // Append our menu bar element as first child of header
        var menuElement = this._menuBar._htmlElement;
        menuElement
            .css('height', '100%')
            .prependTo($('#header'));
    };

    /** @override */
    GChromeShell.prototype.addMenu = function (parentMenu, title, callback) {
        parentMenu = parentMenu || this._menuBar.getMenu();
        var item = new GMenuItem(GMenuItem.Type.Menu);
        item.setCaption(title);
        parentMenu.addItem(item);

        if (callback) {
            item.getMenu().addEventListener(GMenu.OpenEvent, callback);
        }

        return item.getMenu();
    };

    /** @override */
    GChromeShell.prototype.addMenuSeparator = function (parentMenu) {
        var item = new GMenuItem(GMenuItem.Type.Divider);
        parentMenu.addItem(item);
        return item;
    };

    /** @override */
    GChromeShell.prototype.addMenuItem = function (parentMenu, title, checkable, shortcut, callback) {
        var item = new GMenuItem(GMenuItem.Type.Item);
        if (callback) {
            item.addEventListener(GMenuItem.ActivateEvent, callback);
        }

        if (shortcut) {
            gApp.registerShortcut(shortcut, function () {
                callback();
            }.bind(this));

            item.setShortcutHint(shortcut);
        }

        this.updateMenuItem(item, title, true, false);
        parentMenu.addItem(item);
        return item;
    };

    /** @override */
    GChromeShell.prototype.updateMenuItem = function (item, title, enabled, checked) {
        item.setCaption(title);
        item.setEnabled(enabled);
        item.setChecked(checked);
    };

    /** @override */
    GChromeShell.prototype.removeMenuItem = function (parentMenu, child) {
        parentMenu.removeItem(parentMenu.indexOf(child));
    };

    /** @override */
    GChromeShell.prototype.getClipboardMimeTypes = function () {
        return this._clipboardMimeTypes ? Object.keys(this._clipboardMimeTypes) : null;
    };

    /** @override */
    GChromeShell.prototype.getClipboardContent = function (mimeType) {
        if (this._clipboardMimeTypes && this._clipboardMimeTypes.hasOwnProperty(mimeType)) {
            return this._clipboardMimeTypes[mimeType];
        }
        return null;
    };

    /** @override */
    GChromeShell.prototype.setClipboardContent = function (mimeType, content) {
        this._clipboardMimeTypes[mimeType] = content;
    };

    _.gShell = new GChromeShell;

    $(document).ready(function () {
        gShellReady();
    });

    $(window).load(function () {
        gravit.storages.push(new GFileStorage());
        gShellFinished();
    });
})(this);


================================================
FILE: shell/system/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>CFBundleDevelopmentRegion</key>
	<string>en</string>
	<key>CFBundleName</key>
	<string>Gravit</string>
	<key>CFBundleDisplayName</key>
	<string>%name%</string>
	<key>LSApplicationCategoryType</key>
	<string>public.app-category.graphics-design</string>
	<key>CFBundleDocumentTypes</key>
    <array>
    	<dict>
    		<key>CFBundleTypeExtensions</key>
    		<array>
    			<string>gravit</string>
    		</array>
    		<key>CFBundleTypeIconFile</key>
    		<string>doc.icns</string>
    		<key>CFBundleTypeName</key>
    		<string>Gravit Design</string>
    		<key>CFBundleTypeOSTypes</key>
    		<array>
    			<string>????</string>
    		</array>
    		<key>CFBundleTypeRole</key>
    		<string>Editor</string>
    	</dict>
    </array>
    <key>CFBundleExecutable</key>
	<string>node-webkit</string>
	<key>CFBundleIconFile</key>
	<string>nw.icns</string>
	<key>CFBundleIdentifier</key>
	<string>%mac-bundle-id%</string>
	<key>CFBundleInfoDictionaryVersion</key>
	<string>6.0</string>
	<key>CFBundlePackageType</key>
	<string>APPL</string>
	<key>CFBundleShortVersionString</key>
	<string>Version 0.0.1</string>
	<key>CFBundleVersion</key>
	<string>%version%</string>
	<key>LSFileQuarantineEnabled</key>
	<true/>
	<key>LSMinimumSystemVersion</key>
	<string>10.6.0</string>
	<key>NSPrincipalClass</key>
	<string>NSApplication</string>
	<key>NSSupportsAutomaticGraphicsSwitching</key>
	<true/>
</dict>
</plist>


================================================
FILE: shell/system/filestorage.js
================================================
(function (_) {
    var fs = require('fs');

    /**
     * The file storage class for the system
     * @constructor
     */
    function GFileStorage() {
        this._fileInput = $('<input/>')
            .css('display', 'none')
            .attr('type', 'file')
            .on('change', function (evt) {
                var files = this._fileInput[0].files;
                if (files && files.length > 0) {
                    var file = this._fileInput[0].files[0];
                    var location = ifUtil.replaceAll(file.path, '\\', '/');

                    if (this._fileInputMode === 'open_resource') {
                        this._fileInputCallback(this.getProtocol() + '://' + location);
                    } else if (this._fileInputMode === 'save_resource') {
                        var extension = this._fileInput.attr('data-extension');
                        if (extension && !location.match("\\." + extension + "$")) {
                            location += "." + extension;
                        }
                        this._fileInputCallback(this.getProtocol() + '://' + location);
                    } else if (this._fileInputMode === 'open_directory' || this._fileInputMode === 'save_directory') {
                        // Make sure location ends with a slash
                        if (location.charAt(location.length - 1) !== '/') {
                            location += '/';
                        }
                        this._fileInputCallback(this.getProtocol() + '://' + location);
                    }
                }
            }.bind(this))
            .appendTo($('body'));
    };
    IFObject.inherit(GFileStorage, GStorage);

    /**
     * @type {String}
     * @private
     */
    GFileStorage.prototype._fileInputMode = null;

    /**
     * @type {Function}
     * @private
     */
    GFileStorage.prototype._fileInputCallback = null;

    /** @override */
    GFileStorage.prototype.isAvailable = function () {
        return true;
    };

    /** @override */
    GFileStorage.prototype.isSaving = function () {
        return true;
    };

    /** @override */
    GFileStorage.prototype.isPrompting = function () {
        return true;
    };

    /** @override */
    GFileStorage.prototype.isDirectory = function () {
        return true;
    };

    /** @override */
    GFileStorage.prototype.getProtocol = function () {
        return 'file';
    };

    /** @override */
    GFileStorage.prototype.getExtensions = function () {
        return null;
    };

    /** @override */
    GFileStorage.prototype.getName = function () {
        // TODO : I18N
        return 'File';
    };

    /** @override */
    GFileStorage.prototype.openResourcePrompt = function (reference, extensions, done) {
        var filter = "*.*";
        if (extensions) {
            filter = "";
            for (var i = 0; i < extensions.length; ++i) {
                if (i > 0) {
                    filter += ",";
                }
                filter += "." + extensions[i];
            }
        }

        this._fileInputMode = 'open_resource';
        this._fileInputCallback = done;
        this._prepareInput(reference);
        this._fileInput
            .attr('accept', filter ? filter : '')
            .trigger('click');
    };

    /** @override */
    GFileStorage.prototype.saveResourcePrompt = function (reference, proposedName, extension, done) {
        this._fileInputMode = 'save_resource';
        this._fileInputCallback = done;
        this._prepareInput(reference);
        this._fileInput
            .attr('accept', extension ? '.' + extension : '')
            .attr('nwsaveas', proposedName ? proposedName + (extension ? '.' + extension : '') : '')
            .attr('data-extension', extension)
            .trigger('click');
    };

    /** @override */
    GFileStorage.prototype.openDirectoryPrompt = function (reference, done) {
        this._fileInputMode = 'open_directory';
        this._fileInputCallback = done;
        this._prepareInput(reference);
        this._fileInput
            .attr('nwdirectory', '')
            .trigger('click');
    };

    /** @override */
    GFileStorage.prototype.saveDirectoryPrompt = function (reference, done) {
        this._fileInputMode = 'save_directory';
        this._fileInputCallback = done;
        this._prepareInput(reference);
        this._fileInput
            .attr('nwdirectory', '')
            .trigger('click');
    };

    /** @override */
    GFileStorage.prototype.load = function (url, binary, done) {
        var location = new URI(url).path();
        var buffer = fs.readFileSync(location, binary ? null : 'utf8');

        if (buffer) {
            if (binary) {
                var ab = new ArrayBuffer(buffer.length);
                var view = new Uint8Array(ab);
                for (var i = 0; i < buffer.length; ++i) {
                    view[i] = buffer[i];
                }
                buffer = ab;
            }

            done(buffer, this._extractFileName(location));
        }
    };

    /** @override */
    GFileStorage.prototype.save = function (url, data, binary, done) {
        var location = new URI(url).path();

        if (binary) {
            data = new Buffer(new Uint8Array(data));
        }

        fs.writeFileSync(location, data, binary ? null : 'utf8');

        if (done) {
            done(this._extractFileName(location));
        }
    };

    /** @override */
    GFileStorage.prototype.resolveUrl = function (url, resolved) {
        // Our file:/// protocol is understandable by the browser
        // so just use the source url
        resolved(url);
    };

    /** @private */
    GFileStorage.prototype._prepareInput = function (reference) {
        var workingDir = null;
        if (reference && reference !== '') {
            var directory = new URI(reference).directory();
            if (directory && directory !== '') {
                if (ifSystem.operatingSystem === IFSystem.OperatingSystem.Windows) {
                    directory = ifUtil.replaceAll(directory, '/', '\\');
                }
                workingDir = directory;
            }
        }

        this._fileInput
            .removeAttr('accept')
            .removeAttr('nwsaveas')
            .removeAttr('nwdirectory')
            .removeAttr('nwworkingdir')
            .removeAttr('data-extension')
            .val('');

        if (workingDir && workingDir !== '') {
            this._fileInput
                .attr('nwworkingdir', workingDir);
        } else {
            this._fileInput
                .removeAttr('nwworkingdir');
        }
    };

    /**
     * @private
     */
    GFileStorage.prototype._extractFileName = function (path) {
        var lastSlash = path.lastIndexOf('/');
        if (lastSlash < 0) {
            lastSlash = path.lastIndexOf('\\');
        }
        if (lastSlash >= 0) {
            var lastDot = path.lastIndexOf('.');
            if (lastDot > 0) {
                return path.substr(lastSlash + 1, lastDot - lastSlash - 1);
            } else {
                return path.substr(lastSlash + 1);
            }
        }
    };

    _.GFileStorage = GFileStorage;
})(this);


================================================
FILE: shell/system/index.html
================================================
<!doctype html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>Gravit</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"/>
    <link rel="stylesheet" href="gravit.css">
</head>
<body>
<script src="jquery.min.js"></script>
<script src="infinity-libraries.js"></script>
<script src="infinity-core.js"></script>
<script src="infinity-editor.js"></script>
<script src="gravit.js"></script>
<script src="gravit-shell.js"></script>
</body>
</html>


================================================
FILE: shell/system/package/osx/dmg.json
================================================
{
    "title": "Gravit",
    "icon": "../../appicon.icns",
    "background": "background.tiff",
    "icon-size": 120,
    "contents": [
        { "x": 284, "y": 20, "type": "file", "path": "../../../../build/system-binaries/Gravit/osx/Gravit.app" },
        { "x": 483, "y": 20, "type": "link", "path": "/Applications" }
    ]
}

================================================
FILE: shell/system/package.json
================================================
{
    "name": "%name%",
    "description": "%description%",
    "version": "%version%",
    "main": "index.html",
    "window": {
        "toolbar": false,
        "resizable": true,
        "show_in_taskbar": true,
        "frame": true,
        "width": 1024,
        "height": 786,
        "show": true
    }
}

================================================
FILE: shell/system/shell.js
================================================
(function (_) {
    var gui = require('nw.gui');

    gui.App.on('open', function (cmdline) {
        if (cmdline && cmdline.length > 0) {
            gApp.openDocument('file://' + cmdline);
        }
    });

    /**
     * The system shell
     * @class GSystemShell
     * @extends GShell
     * @constructor
     */
    function GSystemShell() {
        this._menuBar = new gui.Menu({ type: "menubar" });

        if (process.platform === 'darwin') {
            this._menuBar.createMacBuiltin("Gravit", {
                hideEdit: true,
                hideWindow: true
            });
        }

        this._clipboardMimeTypes = {};
    };
    IFObject.inherit(GSystemShell, GShell);

    /**
     * @type {gui.Menu}
     * @private
     */
    GSystemShell.prototype._menuBar = null;

    /**
     * @type {*}
     * @private
     */
    GSystemShell.prototype._clipboardMimeTypes = null;

    /** @override */
    GSystemShell.prototype.isDevelopment = function () {
        var argv = gui.App.argv;
        return argv.indexOf('-dev') >= 0;
    };

    /** @override */
    GSystemShell.prototype.start = function () {
        var win = gui.Window.get();
        win.menu = _.gShell._menuBar;
        win.show();
        win.focus();

        var hasOpenedDocuments = false;
        var argv = gui.App.argv;
        if (argv && argv.length) {
            for (var i = 0; i < argv.length; ++i) {
                if (argv[i].charAt(0) !== '-') {
                    gApp.openDocument('file://' + argv[i]);
                    hasOpenedDocuments = true;
                }
            }
        }

        if (!hasOpenedDocuments) {
            GShell.prototype.start.call(this);
        }
    };

    /** @override */
    GSystemShell.prototype.addMenu = function (parentMenu, title, callback) {
        parentMenu = parentMenu || this._menuBar;
        var item = new gui.MenuItem({
            label: title,
            submenu: new gui.Menu()
        });
        parentMenu.append(item);

        //if (callback) {
        //    item.getMenu().addEventListener(GMenu.OpenEvent, callback);
        //}

        return item.submenu;
    };

    /** @override */
    GSystemShell.prototype.addMenuSeparator = function (parentMenu) {
        var item = new gui.MenuItem({ type: 'separator' });
        parentMenu.append(item);
        return item;
    };

    /** @override */
    GSystemShell.prototype.addMenuItem = function (parentMenu, title, checkable, shortcut, callback) {
        var shortcut = shortcut ? this._shortcutToShellShortcut(shortcut) : null;

        var item = new gui.MenuItem({
            type: checkable ? 'checkbox' : 'normal',
            label: title,
            key: shortcut ? shortcut.key : null,
            modifiers: shortcut ? shortcut.modifiers : null,
            click: callback
        });

        parentMenu.append(item);
        return item;
    };

    /** @override */
    GSystemShell.prototype.updateMenuItem = function (item, title, enabled, checked) {
        item.label = title;
        item.enabled = enabled;
        item.checked = checked;
    };

    /** @override */
    GSystemShell.prototype.removeMenuItem = function (parentMenu, child) {
        parentMenu.remove(child);
    };

    /** @override */
    GSystemShell.prototype.getClipboardMimeTypes = function () {
        return this._clipboardMimeTypes ? Object.keys(this._clipboardMimeTypes) : null;
    };

    /** @override */
    GSystemShell.prototype.getClipboardContent = function (mimeType) {
        if (this._clipboardMimeTypes && this._clipboardMimeTypes.hasOwnProperty(mimeType)) {
            return this._clipboardMimeTypes[mimeType];
        }
        return null;
    };

    /** @override */
    GSystemShell.prototype.setClipboardContent = function (mimeType, content) {
        this._clipboardMimeTypes[mimeType] = content;
    };

    /**
     * Convert internal key into a shell-compatible key
     * @param {Array<*>} shortcut
     * @returns {{key: String, modifiers: String}}
     */
    GSystemShell.prototype._shortcutToShellShortcut = function (shortcut) {
        var result = {
            key: null,
            modifiers: ''
        };

        for (var i = 0; i < shortcut.length; ++i) {
            var key = shortcut[i];

            if (typeof key == 'number') {
                // we want a system-translated key here
                var key = ifKey.transformKey(key);
                switch (key) {
                    // Modifiers
                    case IFKey.Constant.CONTROL:
                        result.modifiers = result.modifiers + (result.modifiers ? '-' : '') + 'ctrl';
                        break;
                    case IFKey.Constant.SHIFT:
                        result.modifiers = result.modifiers + (result.modifiers ? '-' : '') + 'shift';
                        break;
                    case IFKey.Constant.ALT:
                        result.modifiers = result.modifiers + (result.modifiers ? '-' : '') + 'alt';
                        break;
                    case IFKey.Constant.COMMAND:
                        result.modifiers = result.modifiers + (result.modifiers ? '-' : '') + 'cmd';
                        break;

                    // Regular Keys
                    case IFKey.Constant.SPACE:
                        // TODO
                        result.key = " ";
                        break;
                    case IFKey.Constant.ENTER:
                        // TODO
                        result.key = "\r";
                        break;
                    case IFKey.Constant.TAB:
                        // TODO
                        result.key = "\t";
                        break;
                    case IFKey.Constant.BACKSPACE:
                        // TODO
                        result.key = "\b";
                        break;

                    case IFKey.Constant.LEFT:
                        if (process.platform === 'darwin') {
                            result.key = String.fromCharCode(0xF702);
                        } else {
                            result.key = "LEFT";
                        }
                        break;
                    case IFKey.Constant.UP:
                        if (process.platform === 'darwin') {
                            result.key = String.fromCharCode(0xF700);
                        } else {
                            result.key = "UP";
                        }
                        break;
                    case IFKey.Constant.RIGHT:
                        if (process.platform === 'darwin') {
                            result.key = String.fromCharCode(0xF703);
                        } else {
                            result.key = "RIGHT";
                        }
                        break;
                    case IFKey.Constant.DOWN:
                        if (process.platform === 'darwin') {
                            result.key = String.fromCharCode(0xF701);
                        } else {
                            result.key = "DOWN";
                        }
                        break;
                    case IFKey.Constant.PAGE_UP:
                        if (process.platform === 'darwin') {
                            result.key = String.fromCharCode(0xF72C);
                        } else {
                            result.key = "PAGEUP";
                        }
                        break;
                    case IFKey.Constant.PAGE_DOWN:
                        if (process.platform === 'darwin') {
                            result.key = String.fromCharCode(0xF72D);
                        } else {
                            result.key = "PAGEDOWN";
                        }
                        break;
                    case IFKey.Constant.HOME:
                        if (process.platform === 'darwin') {
                            result.key = String.fromCharCode(0xF729);
                        } else {
                            result.key = "HOME";
                        }
                        break;
                    case IFKey.Constant.END:
                        if (process.platform === 'darwin') {
                            result.key = String.fromCharCode(0xF72B);
                        } else {
                            result.key = "END";
                        }
                        break;
                    case IFKey.Constant.INSERT:
                        if (process.platform === 'darwin') {
                            result.key = String.fromCharCode(0xF727);
                        } else {
                            result.key = "INSERT";
                        }
                        break;
                    case IFKey.Constant.DELETE:
                        if (process.platform === 'darwin') {
                            result.key = String.fromCharCode(0xF728);
                        } else {
                            result.key = "DELETE";
                        }
                        break;
                    case IFKey.Constant.F1:
                        if (process.platform === 'darwin') {
                            result.key = String.fromCharCode(0xF704);
                        } else {
                            result.key = "F1";
                        }
                        break;
                    case IFKey.Constant.F2:
                        if (process.platform === 'darwin') {
                            result.key = String.fromCharCode(0xF705);
                        } else {
                            result.key = "F2";
                        }
                        break;
                    case IFKey.Constant.F3:
                        if (process.platform === 'darwin') {
                            result.key = String.fromCharCode(0xF706);
                        } else {
                            result.key = "F3";
                        }
                        break;
                    case IFKey.Constant.F4:
                        if (process.platform === 'darwin') {
                            result.key = String.fromCharCode(0xF707);
                        } else {
                            result.key = "F4";
                        }
                        break;
                    case IFKey.Constant.F5:
                        if (process.platform === 'darwin') {
                            result.key = String.fromCharCode(0xF708);
                        } else {
                            result.key = "F5";
                        }
                        break;
                    case IFKey.Constant.F6:
                        if (process.platform === 'darwin') {
                            result.key = String.fromCharCode(0xF709);
                        } else {
                            result.key = "F6";
                        }
                        break;
                    case IFKey.Constant.F7:
                        if (process.platform === 'darwin') {
                            result.key = String.fromCharCode(0xF70A);
                        } else {
                            result.key = "F7";
                        }
                        break;
                    case IFKey.Constant.F8:
                        if (process.platform === 'darwin') {
                            result.key = String.fromCharCode(0xF70B);
                        } else {
                            result.key = "F8";
                        }
                        break;
                    case IFKey.Constant.F9:
                        if (process.platform === 'darwin') {
                            result.key = String.fromCharCode(0xF70C);
                        } else {
                            result.key = "F9";
                        }
                        break;
                    case IFKey.Constant.F10:
                        if (process.platform === 'darwin') {
                            result.key = String.fromCharCode(0xF70D);
                        } else {
                            result.key = "F10";
                        }
                        break;
                    case IFKey.Constant.F11:
                        if (process.platform === 'darwin') {
                            result.key = String.fromCharCode(0xF70E);
                        } else {
                            result.key = "F11";
                        }
                        break;
                    case IFKey.Constant.F12:
                        if (process.platform === 'darwin') {
                            result.key = String.fromCharCode(0xF70F);
                        } else {
                            result.key = "F12";
                        }
                        break;
                    default:
                        throw new Error("Unknown key code");
                }
            } else {
                result.key = key.toLowerCase();
            }
        }

        if (result.modifiers === '') {
            result.modifiers = null;
        }

        return result.key !== null ? result : null;
    };

    _.gShell = new GSystemShell;

    $(document).ready(function () {
        // Open dev console if desired
        var argv = gui.App.argv;
        if (_.gShell.isDevelopment() || argv.indexOf('-console') >= 0) {
            win.showDevTools();
        }

        gShellReady();
    });

    $(window).load(function () {
        gravit.storages.push(new GFileStorage());
        gShellFinished();
    });
})(this);


================================================
FILE: shell/system/winstate.js
================================================
/**
 * Cross-platform window state preservation.
 * Yes this code is quite complicated, but this is the best I came up with for
 * current state of node-webkit Window API (v0.7.3 and later).
 *
 * Known issues:
 * - Unmaximization not always sets the window (x, y) in the lastly used coordinates.
 * - Unmaximization animation sometimes looks wierd.
 * - Extra height added to window, at least in linux x64 gnome-shell env. It seems that
 *   when we read height then it returns it with window frame, but if we resize window
 *   then it applies dimensions only to internal document without external frame.
 *   Need to test in other environments with different visual themes.
 *
 * Change log:
 * 2013-12-01
 * - Workaround of extra height in gnome-shell added.
 *
 * 2014-03-22
 * - Repared workaround (from 2013-12-01) behaviour when use frameless window.
 *   Now it works correctly.
 */

var gui = require('nw.gui');
var win = gui.Window.get();
var winState;
var currWinMode;
var resizeTimeout;
var isMaximizationEvent = false;

// extra height added in linux x64 gnome-shell env, use it as workaround
var deltaHeight = (function () {
    // use deltaHeight only in windows with frame enabled
    if (gui.App.manifest.window.frame) return true; else return 'disabled';
})();


function initWindowState() {
    winState = JSON.parse(localStorage.windowState || 'null');

    if (winState) {
        currWinMode = winState.mode;
        if (currWinMode === 'maximized') {
            win.maximize();
        } else {
            restoreWindowState();
        }
    } else {
        currWinMode = 'normal';
        if (deltaHeight !== 'disabled') deltaHeight = 0;
        dumpWindowState();
    }
}

function dumpWindowState() {
    if (!winState) {
        winState = {};
    }

    // we don't want to save minimized state, only maximized or normal
    if (currWinMode === 'maximized') {
        winState.mode = 'maximized';
    } else {
        winState.mode = 'normal';
    }

    // when window is maximized you want to preserve normal
    // window dimensions to restore them later (even between sessions)
    if (currWinMode === 'normal') {
        winState.x = win.x;
        winState.y = win.y;
        winState.width = win.width;
        winState.height = win.height;

        // save delta only of it is not zero
        if (deltaHeight !== 'disabled' && deltaHeight !== 0 && currWinMode !== 'maximized') {
            winState.deltaHeight = deltaHeight;
        }
    }
}

function restoreWindowState() {
    console.log('restore_window_state');
    // deltaHeight already saved, so just restore it and adjust window height
    if (deltaHeight !== 'disabled' && typeof winState.deltaHeight !== 'undefined') {
        deltaHeight = winState.deltaHeight
        winState.height = winState.height - deltaHeight
    }

    win.resizeTo(winState.width, winState.height);
    win.moveTo(winState.x, winState.y);
}

function saveWindowState() {
    dumpWindowState();
    localStorage.windowState = JSON.stringify(winState);
}

initWindowState();

win.on('maximize', function () {
    isMaximizationEvent = true;
    currWinMode = 'maximized';
});

win.on('unmaximize', function () {
    currWinMode = 'normal';
    restoreWindowState();
});

win.on('minimize', function () {
    currWinMode = 'minimized';
});

win.on('restore', function () {
    currWinMode = 'normal';
});

win.window.addEventListener('resize', function () {
    // resize event is fired many times on one resize action,
    // this hack with setTiemout forces it to fire only once
    clearTimeout(resizeTimeout);
    resizeTimeout = setTimeout(function () {

        // on MacOS you can resize maximized window, so it's no longer maximized
        if (isMaximizationEvent) {
            // first resize after maximization event should be ignored
            isMaximizationEvent = false;
        } else {
            if (currWinMode === 'maximized') {
                currWinMode = 'normal';
            }
        }

        // there is no deltaHeight yet, calculate it and adjust window size
        if (deltaHeight !== 'disabled' && deltaHeight === false) {
            deltaHeight = win.height - winState.height;

            // set correct size
            if (deltaHeight !== 0) {
                win.resizeTo(winState.width, win.height - deltaHeight);
            }
        }

        dumpWindowState();

    }, 500);
}, false);

win.on('close', function () {
    saveWindowState();
    this.close(true);
});

================================================
FILE: src/application/application.js
================================================
(function (_) {
    /**
     * The global application class
     * @class GApplication
     * @extends GEventTarget
     * @constructor
     * @version 1.0
     */
    function GApplication() {
        this._actions = [];
        this._toolManager = new IFToolManager();
        this._documents = [];
        this._windowMenuMap = [];

        document.addEventListener("touchstart", this._touchHandler, true);
        document.addEventListener("touchmove", this._touchHandler, true);
        document.addEventListener("touchend", this._touchHandler, true);
        document.addEventListener("touchcancel", this._touchHandler, true);

        // This is a hack to focus our active window
        // whenever a key is hit down (in capture phase) and
        // if not an editable element is active!
        document.addEventListener('keydown', function (evt) {
            var activeWindow = this._windows.getActiveWindow();
            if (activeWindow && (!document.activeElement || !$(document.activeElement).is(":editable"))) {
                activeWindow.getView().focus();
            }
        }.bind(this), false);

        // Prevent context-menu globally except for editable elements
        document.addEventListener('contextmenu', function (evt) {
            if (!$(evt.target).is(':editable')) {
                evt.preventDefault();
                return false;
            } else {
                // Stop propagation to let browser handle the event
                evt.stopPropagation();
                return true;
            }
        }, true);
    };
    IFObject.inherit(GApplication, GEventTarget);

    // Constants for pre-defined action categories
    GApplication.CATEGORY_FILE = new IFLocale.Key(GApplication, "category.file");
    GApplication.CATEGORY_EDIT = new IFLocale.Key(GApplication, "category.edit");
    GApplication.CATEGORY_MODIFY = new IFLocale.Key(GApplication, "category.modify");
    GApplication.CATEGORY_MODIFY_ARRANGE = new IFLocale.Key(GApplication, "category.modify.arrange");
    GApplication.CATEGORY_MODIFY_ALIGN = new IFLocale.Key(GApplication, "category.modify.align");
    GApplication.CATEGORY_MODIFY_TRANSFORM = new IFLocale.Key(GApplication, "category.modify.transform");
    GApplication.CATEGORY_MODIFY_PAGE = new IFLocale.Key(GApplication, "category.modify.page");
    GApplication.CATEGORY_MODIFY_LAYER = new IFLocale.Key(GApplication, "category.modify.layer");
    GApplication.CATEGORY_VIEW = new IFLocale.Key(GApplication, "category.view");
    GApplication.CATEGORY_VIEW_MAGNIFICATION = new IFLocale.Key(GApplication, "category.view.magnification");
    GApplication.CATEGORY_WINDOW = new IFLocale.Key(GApplication, "category.window");
    GApplication.CATEGORY_HELP = new IFLocale.Key(GApplication, "category.help");

    // Constants for pre-defined tool categories
    GApplication.TOOL_CATEGORY_SELECT = new IFLocale.Key(GApplication, "tool-category.select");
    GApplication.TOOL_CATEGORY_IMAGE = new IFLocale.Key(GApplication, "tool-category.image");
    GApplication.TOOL_CATEGORY_VECTOR = new IFLocale.Key(GApplication, "tool-category.vector");
    GApplication.TOOL_CATEGORY_OTHER = new IFLocale.Key(GApplication, "tool-category.other");
    GApplication.TOOL_CATEGORY_COLOR = new IFLocale.Key(GApplication, "tool-category.color");
    GApplication.TOOL_CATEGORY_VIEW = new IFLocale.Key(GApplication, "tool-category.view");

    /**
     * Visual parts of the application
     */
    GApplication.Part = {
        Header: {
            id: "header"
        },
        Toolbar: {
            id: "toolbar"
        },
        Panels: {
            id: "panels"
        },
        Sidebars: {
            id: "sidebars"
        },
        Windows: {
            id: "windows"
        },
        Palettes: {
            id: "palettes"
        }
    };

    // -----------------------------------------------------------------------------------------------------------------
    // GApplication.DocumentEvent Event
    // -----------------------------------------------------------------------------------------------------------------
    /**
     * An event whenever a document event occurrs
     * @class GApplication.DocumentEvent
     * @extends GEvent
     * @constructor
     */
    GApplication.DocumentEvent = function (type, document) {
        this.type = type;
        this.document = document;
    };
    IFObject.inherit(GApplication.DocumentEvent, GEvent);

    /**
     * Enumeration of view event types
     * @enum
     */
    GApplication.DocumentEvent.Type = {
        Added: 0,
        Removed: 1,
        Deactivated: 10,
        Activated: 11,
        UrlUpdated: 12
    };

    /**
     * @type {GApplication.DocumentEvent.Type}
     */
    GApplication.DocumentEvent.prototype.type = null;

    /**
     * The affected document
     * @type {GDocument}
     */
    GApplication.DocumentEvent.prototype.document = null;

    /** @override */
    GApplication.DocumentEvent.prototype.toString = function () {
        return "[Object GApplication.DocumentEvent]";
    };

    // -----------------------------------------------------------------------------------------------------------------
    // GApplication Class
    // -----------------------------------------------------------------------------------------------------------------

    /**
     * @type {boolean}
     * @private
     */
    GApplication.prototype._initialized = false;

    /**
     * @type {IFToolManager}
     * @private
     */
    GApplication.prototype._toolManager = null;

    /**
     * @type {number}
     * @private
     */
    GApplication.prototype._documentUntitledCount = 0;

    /**
     * @type {Array<GDocument>}
     * @private
     */
    GApplication.prototype._documents = null;

    /**
     * @type {GDocument}
     * @private
     */
    GApplication.prototype._activeDocument = null;

    /**
     * @type {JQuery}
     * @private
     */
    GApplication.prototype._view = null;

    /**
     * @type {GHeader}
     * @private
     */
    GApplication.prototype._header = null;

    /**
     * @type {GToolbar}
     * @private
     */
    GApplication.prototype._toolbar = null;

    /**
     * @type {GPanels}
     * @private
     */
    GApplication.prototype._panels = null;

    /**
     * @type {GSidebars}
     * @private
     */
    GApplication.prototype._sidebars = null;

    /**
     * @type {GWindows}
     * @private
     */
    GApplication.prototype._windows = null;

    /**
     * @type {GPalettes}
     * @private
     */
    GApplication.prototype._palettes = null;

    /**
     * @type {Number}
     * @private
     */
    GApplication.prototype._resizeTimerId = null;


    /**
     * Array of registered actions
     * @type {Array<GAction>}
     * @private
     */
    GApplication.prototype._actions = null;

    /**
     * Application window shell menu
     * @type {*}
     * @private
     */
    GApplication.prototype._windowMenu = null;

    /**
     * @type {Array<{window: GWindow, item: *}>}
     * @private
     */
    GApplication.prototype._windowMenuMap = null;

    /**
     * @returns {IFToolManager}
     */
    GApplication.prototype.getToolManager = function () {
        return this._toolManager;
    };

    /**
     * Returns a list of all opened documents
     * @return {Array<GDocument>}
     */
    GApplication.prototype.getDocuments = function () {
        return this._documents;
    };

    /**
     * Returns the currently active document
     * @return {GDocument}
     */
    GApplication.prototype.getActiveDocument = function () {
        return this._activeDocument ? this._activeDocument : null;
    };

    /**
     * Return access to the header
     * @returns {GHeader}
     */
    GApplication.prototype.getHeader = function () {
        return this._header;
    };

    /**
     * Return access to the toolbar
     * @returns {GToolbar}
     */
    GApplication.prototype.getToolbar = function () {
        return this._toolbar;
    };

    /**
     * Return access to the panels
     * @returns {GPanels}
     */
    GApplication.prototype.getPanels = function () {
        return this._panels;
    };

    /**
     * Return access to the sidebars
     * @returns {GSidebars}
     */
    GApplication.prototype.getSidebars = function () {
        return this._sidebars;
    };

    /**
     * Return access to the window container
     * @returns {GWindows}
     */
    GApplication.prototype.getWindows = function () {
        return this._windows;
    };

    /**
     * Return access to the palettes container
     * @returns {GPalettes}
     */
    GApplication.prototype.getPalettes = function () {
        return this._palettes;
    };

    /**
     * Checks if a given part is visible or not
     * @param {GApplication.Part} part the part to check for
     * @returns {boolean} true if part is visible, false if not
     */
    GApplication.prototype.isPartVisible = function (part) {
        return this.getPart(part).css('display') !== 'none';
    };

    /**
     * Make a given part visible or not
     * @param {GApplication.Part} part the part
     * @param visible whether to make the part visible or not
     */
    GApplication.prototype.setPartVisible = function (part, visible) {
        if (visible != this.isPartVisible(part)) {
            this.getPart(part).css('display', (visible ? 'block' : 'none'));
            this.relayout();
        }
    };

    /**
     * Return reference to a given part
     * @param {GApplication.Part} part
     * @returns {JQuery}
     */
    GApplication.prototype.getPart = function (part) {
        return this._view.find('#' + part.id);
    };

    /**
     * Get a list of all registered actions
     * @return {Array<GAction>} list of registered actions
     */
    GApplication.prototype.getActions = function () {
        return this._actions;
    };

    /**
     * Get an action instance by it's given id
     * @param {String} id
     */
    GApplication.prototype.getAction = function (id) {
        for (var i = 0; i < this._actions.length; ++i) {
            if (this._actions[i].getId() === id) {
                return this._actions[i];
            }
        }
        return null;
    };

    /**
     * Returns the storage for a given url by it's protocol
     * @param {String} url
     * @return {GStorage}
     */
    GApplication.prototype.getStorage = function (url) {
        var protocol = new URI(url).protocol();
        if (protocol && protocol.length) {
            for (var i = 0; i < gravit.storages.length; ++i) {
                if (gravit.storages[i].getProtocol() === protocol) {
                    return gravit.storages[i];
                }
            }
        }
        return null;
    };

    /**
     * Tries to find the best matching storage for the given parameters
     * @param {Boolean} prompt if true, the storage must support prompting
     * @param {Boolean} save if true, the storage must support saving
     * @param {String} [extension] if set, the storage must support the given
     * extension (ignored if directory is true), defaults to null which ignores this
     * @param {Boolean} [directory] if true, the storage must support directories,
     * defaults to false
     * @param {GStorage} [preferredStorage] if provided, will prefer this storage
     * when it fills all requirements. Defaults to null.
     */
    GApplication.prototype.getMatchingStorage = function (prompt, save, extension, directory, preferredStorage) {
        var storages = [];

        // Put preferred storage on top if any
        if (preferredStorage) {
            storages.push(preferredStorage);
        }

        // Add all storages to check
        for (var i = 0; i < gravit.storages.length; ++i) {
            var storage = gravit.storages[i];
            if (storage !== preferredStorage) {
                storages.push(storage);
            }
        }

        // Now iterate and find the best candidate if any
        for (var i = 0; i < storages.length; ++i) {
            var storage = storages[i];

            if (prompt && !storage.isPrompting()) {
                continue;
            }

            if (save && !storage.isSaving()) {
                continue;
            }

            if (extension && extension !== '') {
                var extensions = storage.getExtensions();
                if (extensions && extensions.length && extensions.indexOf(extension) < 0) {
                    continue;
                }
            }

            if (directory && !storage.isDirectory()) {
                continue;
            }

            return storage;
        }

        return null;
    };

    /**
     * Create a new document and add it
     */
    GApplication.prototype.createNewDocument = function () {
        // Create scene, add it and call add page to insert a default page
        var scene = new IFScene();
        scene.setProperty('unit', IFLength.Unit.PX);
        var document = this.addDocument(scene);
        document.createNewPage(true/*no-undo*/);
    };

    /**
     * Add a new document and open up a window for it
     * and mark the view as being active
     * @param {IFScene} scene the scene to add the document from it
     * @param {String} [temporaryTitle] optional temporary title to be used
     * for the document if no url is assigned, defaults to null to use
     * the default naming scheme
     */
    GApplication.prototype.addDocument = function (scene, temporaryTitle) {
        // TODO : I18N
        var document = new GDocument(scene, null, temporaryTitle ? temporaryTitle : 'Untitled-' + (++this._documentUntitledCount).toString());
        this._addDocument(document);
        return document;
    };

    /**
     * Open a document and open up a window for it
     * and mark the view as being active
     * @param {String} url the url to open the document from
     */
    GApplication.prototype.openDocument = function (url) {
        // Iterate all documents first and look if the given
        // url is already opened and if so, activate the
        // document's last view
        var documentAlreadyOpened = false;
        for (var i = 0; i < this._documents.length; ++i) {
            var document = this._documents[i];
            if (document.getUrl() === url) {
                this.activateDocument(document);
                documentAlreadyOpened = true;
            }
        }

        if (!documentAlreadyOpened) {
            var storage = this.getStorage(url);
            if (storage) {
                storage.load(url, true, function (data, name) {
                    var _readDocument = function (source) {
                        var scene = new IFScene();
                        var document = new GDocument(scene, url, name);
                        try {
                            var blob = JSON.parse(source);
                            if (!scene.restore(blob)) {
                                throw new Error('Failure.');
                            }
                        } catch (e) {
                            document.close();
                            scene = null;
                            document = null;
                            console.log(e);
                            alert('An error has ocurred while trying to open the document.');
                        }

                        if (document) {
                            this._addDocument(document);
                        }
                    }.bind(this);


                    var uint8Array = new Uint8Array(data);

                    // Test for gzip
                    if (uint8Array[0] === 0x1F && uint8Array[1] === 0x8B && uint8Array[2] === 0x08) {
                        var source = pako.ungzip(uint8Array, { to: 'string' });
                        _readDocument(source);
                    } else {
                        // Assume plain string
                        var f = new FileReader();
                        f.onload = function (e) {
                            _readDocument(e.target.result);
                        }
                        f.readAsText(new Blob([data]));
                    }
                }.bind(this));
            }
        }
    };

    /**
     * Prompt to open a document
     * @param {GStorage} storage
     */
    GApplication.prototype.openDocumentFrom = function (storage) {
        var url = gApp.getActiveDocument() ? gApp.getActiveDocument().getUrl() : null;
        storage.openResourcePrompt(url && url !== '' ? url : null, ['gravit'], function (url) {
            gApp.openDocument(url);
        });
    };

    /**
     * Prompt to save a document under a new target
     * @param {GStorage} storage
     * @param {GDocument} [document] the document to save as, if
     * not provided takes the currently active one
     */
    GApplication.prototype.saveDocumentAs = function (storage, document) {
        var document = document || this.getActiveDocument();

        if (document) {
            // TODO : Set first parameter 'reference'
            storage.saveResourcePrompt(null, document.getTitle(), 'gravit', function (url) {
                document.setUrl(url)
                document.save();

                // Update all view window menu items
                var windows = document.getWindows();
                for (var i = 0; i < windows.length; ++i) {
                    this._updateWindowMenuItem(windows[i]);
                }

                // Trigger event
                if (this.hasEventListeners(GApplication.DocumentEvent)) {
                    this.trigger(new GApplication.DocumentEvent(GApplication.DocumentEvent.Type.UrlUpdated, this));
                }
            }.bind(this));
        }
    };

    /**
     * Mark a given document as being the active one and activates
     * the first window for the document as well
     * @param {GDocument} document may be null to only deactivate the current one
     * @param {boolean} [noWindowActivation] optional param that, if set, avoids
     * activating the corresponding window when the document gets activated
     */
    GApplication.prototype.activateDocument = function (document, noWindowActivation) {
        if (document != this._activeDocument) {
            // Deactivate previous one if any
            if (this._activeDocument) {
                if (this._activeDocument) {
                    this._activeDocument.deactivate();

                    if (this.hasEventListeners(GApplication.DocumentEvent)) {
                        this.trigger(new GApplication.DocumentEvent(GApplication.DocumentEvent.Type.Deactivated, this._activeDocument));
                    }
                }

                this._activeDocument = null;
            }

            // Activate new one if any
            if (document) {
                // Activate lastly activated window of document
                if (!noWindowActivation) {
                    this._windows.activateWindow(document.getActiveWindow());
                }

                document.activate();

                // Now assign the active document
                this._activeDocument = document;

                if (this.hasEventListeners(GApplication.DocumentEvent)) {
                    this.trigger(new GApplication.DocumentEvent(GApplication.DocumentEvent.Type.Activated, document));
                }
            }
        }
    };

    /**
     * Closes and removes a document and all of it's views
     * @param {GDocument} document
     */
    GApplication.prototype.closeDocument = function (document) {
        if (document._windows.length) {
            // Document has windows so remove them first which
            // will then trigger this function again
            while (document._windows.length > 0) {
                this._windows.closeWindow(document._windows[0]);
            }
        } else {
            // Remove active document if this is the active one
            if (document === this.getActiveDocument()) {
                this.activateDocument(null);
            }

            // Release document
            document.release();

            // Remove and trigger event
            this._documents.splice(this._documents.indexOf(document), 1);

            if (this.hasEventListeners(GApplication.DocumentEvent)) {
                this.trigger(new GApplication.DocumentEvent(GApplication.DocumentEvent.Type.Removed, document));
            }
        }
    };

    /**
     * Checks whether a given action can be executed or not
     * @param {String} id id of the action to check
     * @param {*} [args] optional args to be supplied to the action
     * @return {Boolean}
     */
    GApplication.prototype.canExecuteAction = function (id, args) {
        var actionInstance = this.getAction(id);

        if (actionInstance) {
            return actionInstance.isAvailable() && actionInstance.isEnabled.apply(actionInstance, args);
        }

        return false;
    };

    /**
     * Execute a given action
     * @param {String} id id of the action to execute
     * @param {*} [args] optional args to be supplied to the action
     * @return {*} the result of the action if any
     */
    GApplication.prototype.executeAction = function (id, args) {
        var actionInstance = this.getAction(id);

        if (!actionInstance) {
            throw new Error("Unable to execute action '" + id + "' - not registered.");
        }

        if (actionInstance.isAvailable() && actionInstance.isEnabled.apply(actionInstance, args)) {
            var result = actionInstance.execute.apply(actionInstance, args);
            if (typeof result !== 'undefined') {
                return result;
            }
            return true;
        }

        return false;
    };

    /**
     * Called to initialize the application
     */
    GApplication.prototype.init = function () {
        var body = $('body');

        // Iterate modules and let each one initialize
        for (var i = 0; i < gravit.modules.length; ++i) {
            var module = gravit.modules[i];
            console.log("Init module <" + module.toString() + ">");
            module.init();
        }

        this._view = $("<div></div>")
            .attr('id', 'workspace')
            .css('display', 'none')
            .prependTo(body);

        // Windows-Part
        var windowsPart = $("<div></div>")
            .attr('id', GApplication.Part.Windows.id)
            .appendTo(this._view);

        this._windows = new GWindows(windowsPart);

        // Header-Part
        var headerPart = $("<div></div>")
            .attr('id', GApplication.Part.Header.id)
            .appendTo(this._view);

        this._header = new GHeader(headerPart);

        // Toolbar-Part
        var toolbarPart = $("<div></div>")
            .attr('id', GApplication.Part.Toolbar.id)
            .appendTo(this._view);

        this._toolbar = new GToolbar(toolbarPart);

        // Panels-Part
        var panelsPart = $("<div></div>")
            .attr('id', GApplication.Part.Panels.id)
            .appendTo(this._view);

        this._panels = new GPanels(panelsPart);

        // Sidebars-Part
        var sidebarsPart = $("<div></div>")
            .attr('id', GApplication.Part.Sidebars.id)
            .appendTo(this._view);

        this._sidebars = new GSidebars(sidebarsPart);

        // Palettes-Part
        var palettesPart = $("<div></div>")
            .attr('id', GApplication.Part.Palettes.id)
            .appendTo(this._view);

        this._palettes = new GPalettes(palettesPart);

        // Append the corresponding hardware class to our body
        switch (ifSystem.hardware) {
            case IFSystem.Hardware.Desktop:
                body.addClass('g-desktop');
                break;
            case IFSystem.Hardware.Tablet:
                body.addClass('g-touch');
                body.addClass('g-tablet');
                break;
            case IFSystem.Hardware.Phone:
                body.addClass('g-touch');
                body.addClass('g-phone');
                break;
        }

        // Subscribe to window resize to relayout
        $(window).resize(function () {
            if (this._resizeTimerId != null) {
                clearTimeout(this._resizeTimerId);
                this._resizeTimerId = null;
            }

            this._windows.relayout();

            this._resizeTimerId = setTimeout(function () {
                this.relayout();
                this._resizeTimerId = null;
            }.bind(this), 200);
        }.bind(this));

        // TODO : Order our available palettes by group
        // TODO : Order our available tools by group

        // -- Register Actions
        this._actions = gravit.actions.slice();
        this._createMainMenu();

        // Add all available tools to toolmanager and register
        // their activation character(s) if any as shortcuts
        if (gravit.tools) {
            var _createToolActivateAction = function (instance) {
                return function () {
                    this._toolManager.activateTool(instance);
                }.bind(this);
            }.bind(this);

            for (var i = 0; i < gravit.tools.length; ++i) {
                var tool = gravit.tools[i];

                // Register tool instance
                this._toolManager.addTool(tool.instance);

                // Register activation characters
                if (tool.keys && tool.keys.length > 0) {
                    var action = _createToolActivateAction(tool.instance);
                    for (var c = 0; c < tool.keys.length; ++c) {
                        this.registerShortcut([tool.keys[c]], action);
                    }
                }
            }
        }

        this._header.init();
        this._toolbar.init();
        this._panels.init();
        this._sidebars.init();
        this._windows.init();
        this._palettes.init();

        // Hide sidebars by default - TODO : Load & save view configuration here
        this.setPartVisible(GApplication.Part.Sidebars, false);

        // Make workspace visible & make initial layout
        this._view.css('display', '');

        // Mark initialized
        this._initialized = true;

        // Subscribe to window events
        this._windows.addEventListener(GWindows.WindowEvent, this._windowEvent, this);
    };

    /**
     * Called to relayout the application
     */
    GApplication.prototype.relayout = function () {
        if (!this._initialized) {
            // ignore
            return;
        }

        setTimeout(function () {
            var topOffset = 0;
            var leftOffset = 0;
            var rightOffset = 0;
            var bottomOffset = 0;

            var headerPart = this.getPart(GApplication.Part.Header);
            topOffset += this.isPartVisible(GApplication.Part.Header) ? headerPart.outerHeight() : 0;

            var toolbarPart = this.getPart(GApplication.Part.Toolbar);
            toolbarPart.css('top', topOffset.toString() + 'px');
            toolbarPart.height(this._view.height() - topOffset);
            leftOffset += this.isPartVisible(GApplication.Part.Toolbar) ? toolbarPart.outerWidth() : 0;

            var sidebarsPart = this.getPart(GApplication.Part.Sidebars);
            sidebarsPart.css('top', topOffset.toString() + 'px');
            sidebarsPart.css('left', leftOffset.toString() + 'px');
            sidebarsPart.height(this._view.height() - topOffset);
            leftOffset += this.isPartVisible(GApplication.Part.Sidebars) ? sidebarsPart.outerWidth() : 0;

            var palettesPart = this.getPart(GApplication.Part.Palettes);
            palettesPart.css('top', topOffset.toString() + 'px');
            palettesPart.height(this._view.height() - topOffset);
            rightOffset += this.isPartVisible(GApplication.Part.Palettes) ? palettesPart.outerWidth() : 0;

            var panelsPart = this.getPart(GApplication.Part.Panels);
            panelsPart.css('left', leftOffset.toString() + 'px');
            panelsPart.css('width', (this._view.width() - leftOffset - rightOffset).toString() + 'px');
            bottomOffset += this.isPartVisible(GApplication.Part.Panels) ? panelsPart.outerHeight() : 0;

            this._header.relayout();
            this._toolbar.relayout();
            this._panels.relayout();
            this._sidebars.relayout();
            this._windows.relayout([leftOffset, topOffset, rightOffset, bottomOffset]);
            this._palettes.relayout();
        }.bind(this), 0);
    };

    /**
     * Register a shortcut that'll execute a given function
     * @param {Array<*>} shortcut the shortcut for the action
     * @param {Function} action an action to be executed when the
     * shortcut is called
     */
    GApplication.prototype.registerShortcut = function (shortcut, action) {
        Mousetrap.bind(this._shortcutToMouseTrapShortcut(shortcut), function () {
            action();
            return false;
        }.bind(this));
    };

    /**
     * Add a new document
     * @param {GDocument} document
     * @private
     */
    GApplication.prototype._addDocument = function (document) {
        // Send an event
        if (this.hasEventListeners(GApplication.DocumentEvent)) {
            this.trigger(new GApplication.DocumentEvent(GApplication.DocumentEvent.Type.Added, document));
        }

        // Add a window for the document making it activated by default
        this._windows.addWindow(document);
    };

    /**
     * Create the main menu based on actions
     * @param {Array<GAction>} actions
     * @private
     */
    GApplication.prototype._createMainMenu = function () {
        // Create our menu structure based on actions
        // TODO : Order given actions by category & group

        var itemToGroupArray = [];
        var treeRoot = {
            items: []
        };

        var _getGroupForItem = function (item) {
            for (var i = 0; i < itemToGroupArray.length; ++i) {
                if (itemToGroupArray[i].item === item) {
                    return itemToGroupArray[i].group;
                }
            }
        };

        var _addItemGroupAndDivider = function (menu, item, group) {
            if (menu.items.length > 0) {
                var lastGroup = _getGroupForItem(menu.items[menu.items.length - 1]);
                if (lastGroup !== group) {
                    menu.items.push({
                        type: 'divider'
                    });
                }
            }
            itemToGroupArray.push({
                item: item,
                group: group
            });
        };

        for (var i = 0; i < this._actions.length; ++i) {
            var action = this._actions[i];

            if (!action.isAvailable()) {
                continue;
            }

            var category = ifLocale.get(action.getCategory());
            var group = action.getGroup();
            var categories = category ? category.split('/') : null;
            var groups = group ? [""].concat(group.split('/')) : null;

            if (groups && categories && categories.length !== groups.length - 1) {
                throw new Error("Number of categories different thant number of groups.");
            }

            // Build up our structure by iterating our categories
            var currentTree = treeRoot;
            if (categories) {
                for (var k = 0; k < categories.length; ++k) {
                    var category = categories[k];
                    var group = groups ? groups[k] : null;

                    var item = null;
                    for (var l = 0; l < currentTree.items.length; ++l) {
                        if (category == currentTree.items[l].caption) {
                            item = currentTree.items[l];
                        }
                    }

                    if (!item) {
                        item = {
                            type: 'menu',
                            caption: category,
                            items: [],
                            windowMenu: GApplication.CATEGORY_WINDOW === action.getCategory() &&
                                currentTree === treeRoot
                        };
                        _addItemGroupAndDivider(currentTree, item, group);

                        currentTree.items.push(item);
                    }
                    currentTree = item;
                }
            }

            // Add our action item now
            var actionItem = {
                type: 'item',
                action: action
            };
            _addItemGroupAndDivider(currentTree, actionItem, groups ? groups[groups.length - 1] : null);

            currentTree.items.push(actionItem);
        }

        var _createMenuItem = function (item, parentMenu) {
            if (item.type === 'menu') {
                item.menu = _createMenu(item, parentMenu);
            } else if (item.type === 'divider') {
                item.separator = gShell.addMenuSeparator(parentMenu);
            } else if (item.type === 'item') {
                item.item = gShell.addMenuItem(parentMenu, ifLocale.get(item.action.getTitle()), item.action.isCheckable(), item.action.getShortcut(),
                    function () {
                        this.executeAction(item.action.getId());
                    }.bind(this));
            }
        }.bind(this);

        // Initiate our menu structure now using our shell
        var _createMenu = function (tree, parentMenu) {
            var menu = gShell.addMenu(parentMenu, tree.caption, function () {
                for (var i = 0; i < tree.items.length; ++i) {
                    var item = tree.items[i];
                    if (item.type === 'item') {
                        gShell.updateMenuItem(item.item, ifLocale.get(item.action.getTitle()),
                            item.action.isEnabled(), item.action.isCheckable() ? item.action.isChecked() : false);
                    }
                }
            });

            for (var i = 0; i < tree.items.length; ++i) {
                _createMenuItem(tree.items[i], menu);
            }

            return menu;
        };

        for (var i = 0; i < treeRoot.items.length; ++i) {
            var item = treeRoot.items[i];
            var menu = _createMenu(treeRoot.items[i], null);

            // Save window menu
            if (item.windowMenu) {
                this._windowMenu = menu;

                // Make sure to append an ending divider for windows
                gShell.addMenuSeparator(this._windowMenu);
            }
        }
    };

    /**
     * @param {GWindows.WindowEvent} evt
     * @private
     */
    GApplication.prototype._windowEvent = function (evt) {
        switch (evt.type) {
            case GWindows.WindowEvent.Type.Added:
                this._addWindowMenuItem(evt.window);
                break;
            case GWindows.WindowEvent.Type.Removed:
                this._removeWindowMenuItem(evt.window);
                break;
            case GWindows.WindowEvent.Type.Activated:
                this._updateWindowMenuItem(evt.window);
                this._toolManager.setView(evt.window.getView());
                break;
            case GWindows.WindowEvent.Type.Deactivated:
                this._updateWindowMenuItem(evt.window);
                this._toolManager.setView(null);
                break;
            default:
                break;
        }
    };

    /**
     * @param {GWindow} window
     * @private
     */
    GApplication.prototype._addWindowMenuItem = function (window) {
        this._windowMenuMap.push({
            window: window,
            item: gShell.addMenuItem(this._windowMenu, window.getTitle(), true, null, function () {
                this._windows.activateWindow(window);
            }.bind(this))
        });
        this._updateWindowMenuItem(window);
    };

    /**
     * @param {GWindow} window
     * @private
     */
    GApplication.prototype._removeWindowMenuItem = function (window) {
        for (var i = 0; i < this._windowMenuMap.length; ++i) {
            var map = this._windowMenuMap[i];
            if (map.window === window) {
                gShell.removeMenuItem(this._windowMenu, map.item);
                this._windowMenuMap.splice(i, 1);
                break;
            }
        }
        this._updateTitle();
    };

    /**
     * @param {GWindow} window
     * @private
     */
    GApplication.prototype._updateWindowMenuItem = function (window) {
        for (var i = 0; i < this._windowMenuMap.length; ++i) {
            var map = this._windowMenuMap[i];
            if (map.window === window) {
                gShell.updateMenuItem(map.item, map.window.getTitle(), true, map.window === this._windows.getActiveWindow());
                break;
            }
        }
        this._updateTitle();
    };

    GApplication.prototype._updateTitle = function () {
        var title = 'Gravit';
        var window = this.getWindows().getActiveWindow();
        if (window) {
            title += ' - ' + window.getTitle();
        }
        document.title = title;
    }

    /**
     * Handle touch events by converting them into mouse events and stopping
     * every further propagation to guarantee mouse events not being fired twice
     * @private
     */
    GApplication.prototype._touchHandler = function (event) {
        // allow default multi-touch gestures to work
        if (event.touches.length > 1) {
            return;
        }

        function dispatchEventFromTouch(eventType, touch) {
            var simulatedEvent = document.createEvent("MouseEvent");
            simulatedEvent.initMouseEvent(eventType, true, true, window, 1,
                touch.screenX, touch.screenY,
                touch.clientX, touch.clientY, false,
                false, false, false, 0/*left*/, null);

            touch.target.dispatchEvent(simulatedEvent);
        };

        var touch = event.changedTouches[0];
        switch (event.type) {
            case "touchstart":
                dispatchEventFromTouch("mousedown", touch);
                break;
            case "touchmove":
                dispatchEventFromTouch("mousemove", touch);
                break;
            case "touchend":
                dispatchEventFromTouch("mouseup", touch);
                dispatchEventFromTouch("click", touch);
                break;
            default:
                return;
        }

        // Prevent any further processing
        event.preventDefault();
        event.stopPropagation();
    };

    /**
     * Convert internal key into a mousetrap-compatible key
     * @param {Array<*>} shortcut
     * @returns {String}
     */
    GApplication.prototype._shortcutToMouseTrapShortcut = function (shortcut) {
        var result = "";
        for (var i = 0; i < shortcut.length; ++i) {
            if (i > 0) {
                result += "+";
            }

            var key = shortcut[i];
            if (typeof key == 'number') {
                switch (key) {
                    case IFKey.Constant.META:
                        result += "meta";
                        break;
                    case IFKey.Constant.OPTION:
                        result += "option";
                        break;
                    case IFKey.Constant.REMOVE:
                        result += "del";
                        break;
                    case IFKey.Constant.SPACE:
                        result += "space";
                        break;
                    case IFKey.Constant.ENTER:
                        result += "enter";
                        break;
                    case IFKey.Constant.TAB:
                        result += "tab";
                        break;
                    case IFKey.Constant.BACKSPACE:
                        result += "backspace";
                        break;
                    case IFKey.Constant.CONTROL:
                        result += "ctrl";
                        break;
                    case IFKey.Constant.SHIFT:
                        result += "shift";
                        break;
                    case IFKey.Constant.ALT:
                        result += "alt";
                        break;
                    case IFKey.Constant.LEFT:
                        result += "left";
                        break;
                    case IFKey.Constant.UP:
                        result += "up";
                        break;
                    case IFKey.Constant.RIGHT:
                        result += "right";
                        break;
                    case IFKey.Constant.DOWN:
                        result += "down";
                        break;
                    case IFKey.Constant.PAGE_UP:
                        result += "pageup";
                        break;
                    case IFKey.Constant.PAGE_DOWN:
                        result += "pagedown";
                        break;
                    case IFKey.Constant.HOME:
                        result += "home";
                        break;
                    case IFKey.Constant.END:
                        result += "end";
                        break;
                    case IFKey.Constant.INSERT:
                        result += "ins";
                        break;
                    case IFKey.Constant.DELETE:
                        result += "del";
                        break;
                    case IFKey.Constant.COMMAND:
                        result += "meta";
                        break;
                    case IFKey.Constant.F1:
                        result += "f1";
                        break;
                    case IFKey.Constant.F2:
                        result += "f2";
                        break;
                    case IFKey.Constant.F3:
                        result += "f3";
                        break;
                    case IFKey.Constant.F4:
                        result += "f4";
                        break;
                    case IFKey.Constant.F5:
                        result += "f5";
                        break;
                    case IFKey.Constant.F6:
                        result += "f6";
                        break;
                    case IFKey.Constant.F7:
                        result += "f7";
                        break;
                    case IFKey.Constant.F8:
                        result += "f8";
                        break;
                    case IFKey.Constant.F9:
                        result += "f9";
                        break;
                    case IFKey.Constant.F10:
                        result += "f10";
                        break;
                    case IFKey.Constant.F11:
                        result += "f11";
                        break;
                    case IFKey.Constant.F12:
                        result += "f12";
                        break;
                    default:
                        throw new Error("Unknown key code");
                }
            } else {
                result += key.toLowerCase();
            }
        }
        return result;
    };

    _.GApplication = GApplication;
})(this);


================================================
FILE: src/application/bootstrap.js
================================================
var gravit = {
    /**
     * Array<GModule>
     */
    modules: [],

    /**
     * Array<GStorage>
     */
    storages: [],

    /**
     * Array<GExporter>
     */
    exporters: [],

    /**
     * Array<GAction>
     */
    actions: [],

    /**
     * Array<GPalette>
     */
    palettes: [],

    /**
     * Array<GSidebar>
     */
    sidebars: [],

    /**
     * Array<GPanel>
     */
    panels: [],

    /**
     * Array<{{instance: IFTool, title: String|IFLocale.Key, group: String, keys: Array<String>}, icon: String}>
     */
    tools: [],

    /**
     * Array<GColorMatcher>
     */
    colorMatchers: [],

    /**
     * Array<GProperties>
     */
    properties: [],

    /**
     * Array<GStyleEntry>
     */
    styleEntries: [],

    /**
     * Array<GTransformer>
     */
    transformers: []
};

var gLoaderCode = '<div style="position: absolute; left: 0px; top: 0px; right: 0px; bottom: 0px; background: rgb(213, 223, 0);">\n    <style type="text/css">\n        .spinner {\n            width: 120px;\n            height: 120px;\n            position: absolute;\n            top: 50%;\n            left: 50%;\n            margin: -80px 0 0 -60px;\n            text-align: center;\n            -webkit-animation: rotate 2.0s infinite linear;\n            animation: rotate 2.0s infinite linear;\n        }\n\n        .spinner-dot1, .spinner-dot2 {\n            width: 60%;\n            height: 60%;\n            display: inline-block;\n            position: absolute;\n            top: 0;\n            background-color: rgb(229, 71, 97);\n            border-radius: 100%;\n            -webkit-animation: bounce 2.0s infinite ease-in-out;\n            animation: bounce 2.0s infinite ease-in-out;\n        }\n\n        .spinner-dot1 {\n            top: auto;\n            bottom: 0px;\n            -webkit-animation-delay: -1.0s;\n            animation-delay: -1.0s;\n        }\n\n        @-webkit-keyframes rotate { 100% { -webkit-transform: rotate(360deg) }}\n        @keyframes rotate { 100% { transform: rotate(360deg); -webkit-transform: rotate(360deg) }}\n\n        @-webkit-keyframes bounce {\n            0%, 100% { -webkit-transform: scale(0.0) }\n            50% { -webkit-transform: scale(1.0) }\n        }\n\n        @keyframes bounce {\n            0%, 100% {\n                transform: scale(0.0);\n                -webkit-transform: scale(0.0);\n            } 50% {\n                  transform: scale(1.0);\n                  -webkit-transform: scale(1.0);\n              }\n        }\n    </style>\n    <div class="spinner">\n        <div class="spinner-dot1"></div>\n        <div class="spinner-dot2"></div>\n    </div>\n</div>';

/**
 * @type {GShell}
 */
var gShell = null;

/**
 * @type {GApplication}
 */
var gApp = null;

var __loader = null;

// Those functions need to be called from within the shell
function gShellReady() {
    gApp = new GApplication();
}

function gShellFinished() {
    gApp.init();
    gShell.prepare();
    gApp.relayout();

    // Run shell start with a slight delay
    setTimeout(function () {
        gShell.start();
        __loader.remove();
    }, 2500);
}

// Bootstrapping when the DOM is ready
$(document).ready(function () {
    if (!gShell) {
        throw new Error("Shell needs to be initialized, first.");
    }

    // Add Gravit Loader
    __loader = $(gLoaderCode)
        .appendTo($('body'));

    //
    // -- FONTS --
    //

    //
    // TODO : Remove / handle default fonts somewhere else!?
    //

    // Open Sans
    ifFont.addType('Open Sans', IFFont.Style.Normal, IFFont.Weight.Light, 'font/OpenSans-Light.ttf', IFFont.Category.Serif);
    ifFont.addType('Open Sans', IFFont.Style.Italic, IFFont.Weight.Light, 'font/OpenSans-LightItalic.ttf');
    ifFont.addType('Open Sans', IFFont.Style.Normal, IFFont.Weight.Regular, 'font/OpenSans-Regular.ttf');
    ifFont.addType('Open Sans', IFFont.Style.Italic, IFFont.Weight.Regular, 'font/OpenSans-Italic.ttf');
    ifFont.addType('Open Sans', IFFont.Style.Normal, IFFont.Weight.SemiBold, 'font/OpenSans-Semibold.ttf');
    ifFont.addType('Open Sans', IFFont.Style.Italic, IFFont.Weight.SemiBold, 'font/OpenSans-SemiboldItalic.ttf');
    ifFont.addType('Open Sans', IFFont.Style.Normal, IFFont.Weight.Bold, 'font/OpenSans-Bold.ttf');
    ifFont.addType('Open Sans', IFFont.Style.Italic, IFFont.Weight.Bold, 'font/OpenSans-BoldItalic.ttf');
    ifFont.addType('Open Sans', IFFont.Style.Normal, IFFont.Weight.ExtraBold, 'font/OpenSans-ExtraBold.ttf');
    ifFont.addType('Open Sans', IFFont.Style.Italic, IFFont.Weight.ExtraBold, 'font/OpenSans-ExtraBoldItalic.ttf');

    // Source Sans Pro
    ifFont.addType('Source Sans Pro', IFFont.Style.Normal, IFFont.Weight.ExtraLight, 'font/SourceSansPro-ExtraLight.ttf', IFFont.Category.Serif);
    ifFont.addType('Source Sans Pro', IFFont.Style.Italic, IFFont.Weight.ExtraLight, 'font/SourceSansPro-ExtraLightIt.ttf');
    ifFont.addType('Source Sans Pro', IFFont.Style.Normal, IFFont.Weight.Light, 'font/SourceSansPro-Light.ttf');
    ifFont.addType('Source Sans Pro', IFFont.Style.Italic, IFFont.Weight.Light, 'font/SourceSansPro-LightIt.ttf');
    ifFont.addType('Source Sans Pro', IFFont.Style.Normal, IFFont.Weight.Regular, 'font/SourceSansPro-Regular.ttf');
    ifFont.addType('Source Sans Pro', IFFont.Style.Italic, IFFont.Weight.Regular, 'font/SourceSansPro-It.ttf');
    ifFont.addType('Source Sans Pro', IFFont.Style.Normal, IFFont.Weight.SemiBold, 'font/SourceSansPro-Semibold.ttf');
    ifFont.addType('Source Sans Pro', IFFont.Style.Italic, IFFont.Weight.SemiBold, 'font/SourceSansPro-SemiboldIt.ttf');
    ifFont.addType('Source Sans Pro', IFFont.Style.Normal, IFFont.Weight.Bold, 'font/SourceSansPro-Bold.ttf');
    ifFont.addType('Source Sans Pro', IFFont.Style.Italic, IFFont.Weight.Bold, 'font/SourceSansPro-BoldIt.ttf');
    ifFont.addType('Source Sans Pro', IFFont.Style.Normal, IFFont.Weight.Heavy, 'font/SourceSansPro-Black.ttf');
    ifFont.addType('Source Sans Pro', IFFont.Style.Italic, IFFont.Weight.Heavy, 'font/SourceSansPro-BlackIt.ttf');


    // Source Code Pro
    ifFont.addType('Source Code Pro', IFFont.Style.Normal, IFFont.Weight.ExtraLight, 'font/SourceCodePro-ExtraLight.ttf', IFFont.Category.Monospace);
    ifFont.addType('Source Code Pro', IFFont.Style.Normal, IFFont.Weight.Light, 'font/SourceCodePro-Light.ttf');
    ifFont.addType('Source Code Pro', IFFont.Style.Normal, IFFont.Weight.Regular, 'font/SourceCodePro-Regular.ttf');
    ifFont.addType('Source Code Pro', IFFont.Style.Normal, IFFont.Weight.Medium, 'font/SourceCodePro-Medium.ttf');
    ifFont.addType('Source Code Pro', IFFont.Style.Normal, IFFont.Weight.SemiBold, 'font/SourceCodePro-Semibold.ttf');
    ifFont.addType('Source Code Pro', IFFont.Style.Normal, IFFont.Weight.Bold, 'font/SourceCodePro-Bold.ttf');
    ifFont.addType('Source Code Pro', IFFont.Style.Normal, IFFont.Weight.Heavy, 'font/SourceCodePro-Black.ttf');

    // FontAwesome
    ifFont.addType('FontAwesome', IFFont.Style.Normal, IFFont.Weight.Regular, gShell.isDevelopment() ? '../bower_components/font-awesome/fonts/FontAwesome-webfont.ttf' : 'font/FontAwesome-webfont.ttf', IFFont.Category.Iconic);
});

$(window).load(function () {
    rangy.init();
});

================================================
FILE: src/application/component/autoedit.js
================================================
(function ($) {

    var methods = {
        init: function (options) {
            options = $.extend({
                // Selector for the content, if not provided
                // takes this element as content
                selector: null
            }, options);

            return this.each(function () {
                var self = this;
                $(this)
                    .data('gautoedit', {
                        options: options,
                        input: null
                    })
                    .on('dblclick', function (evt) {
                        methods.open.call(self);
                    });
            });
        },

        // Open the editor
        open: function () {
            var self = this;
            var $this = $(this);
            var data = $this.data('gautoedit');

            if (data.input) {
                methods.close.call(this);
            }

            var container = $this.find(data.options.selector);

            if (container.length === 0) {
                if (data.options.selector) {
                    return;
                }

                container = $this;
            }

            var offset = container.offset();

            data.value = container.text();

            data.input = $('<input>')
                .css({
                    'position': 'absolute',
                    'left': offset.left + 'px',
                    'top': offset.top + 'px',
                    'width': container.outerWidth() + 'px',
                    'height': container.outerHeight() + 'px'
                })
                .val(data.value)
                .on('blur', function (evt) {
                    methods.submit.call(self);
                })
                .on('keyup', function (evt) {
                    if (evt.keyCode === 13) {
                        methods.submit.call(self);
                    } else if (evt.keyCode === 27) {
                        methods.close.call(self);
                    }
                })
                .appendTo($('body'))
                .focus()
                .select();
        },

        // Submit editor contents and close it
        submit: function () {
            var $this = $(this);
            var data = $this.data('gautoedit');

            // save input value
            var inputVal = data.input ? data.input.val() : null;

            // close first
            methods.close.call(this);

            if (inputVal) {
                if (data.value !== inputVal) {
                    $this.trigger('submitvalue', inputVal);
                }
            }
        },

        // Close the editor
        close: function () {
            var self = this;
            var $this = $(this);
            var data = $this.data('gautoedit');

            if (data.input) {
                data.input.remove();
                data.input = null;
                data.value = null;
            }
        },
    };

    /**
     * Adds the capability to edit some text
     */
    $.fn.gAutoEdit = function (method) {
        if (methods[method]) {
            return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
        } else if (typeof method === 'object' || !method) {
            return methods.init.apply(this, arguments);
        } else {
            $.error('Method ' + method + ' does not exist on jQuery.myPlugin');
        }
    }

}(jQuery));

================================================
FILE: src/application/component/blendmode.js
================================================
(function ($) {

    var blendModes = [
        {
            type: IFPaintCanvas.BlendMode.Normal,
            // TODO : I18N
            name: 'Normal'
        },
        {
            type: IFPaintCanvas.BlendMode.Multiply,
            // TODO : I18N
            name: 'Multiply'
        },
        {
            type: IFPaintCanvas.BlendMode.Screen,
            // TODO : I18N
            name: 'Screen'
        },
        {
            type: IFPaintCanvas.BlendMode.Overlay,
            // TODO : I18N
            name: 'Overlay'
        },
        {
            type: IFPaintCanvas.BlendMode.Darken,
            // TODO : I18N
            name: 'Darken'
        },
        {
            type: IFPaintCanvas.BlendMode.Lighten,
            // TODO : I18N
            name: 'Lighten'
        },
        {
            type: IFPaintCanvas.BlendMode.ColorDodge,
            // TODO : I18N
            name: 'Color Dodge'
        },
        {
            type: IFPaintCanvas.BlendMode.ColorBurn,
            // TODO : I18N
            name: 'Color Burn'
        },
        {
            type: IFPaintCanvas.BlendMode.HardLight,
            // TODO : I18N
            name: 'Hard Light'
        },
        {
            type: IFPaintCanvas.BlendMode.SoftLight,
            // TODO : I18N
            name: 'Soft Light'
        },
        {
            type: IFPaintCanvas.BlendMode.Difference,
            // TODO : I18N
            name: 'Difference'
        },
        {
            type: IFPaintCanvas.BlendMode.Exclusion,
            // TODO : I18N
            name: 'Exclusion'
        },
        {
            type: IFPaintCanvas.BlendMode.Hue,
            // TODO : I18N
            name: 'Hue'
        },
        {
            type: IFPaintCanvas.BlendMode.Saturation,
            // TODO : I18N
            name: 'Saturation'
        },
        {
            type: IFPaintCanvas.BlendMode.Color,
            // TODO : I18N
            name: 'Color'
        },
        {
            type: IFPaintCanvas.BlendMode.Luminosity,
            // TODO : I18N
            name: 'Luminosity'
        }
    ];

    var methods = {
        init: function (options) {
            options = $.extend({
            }, options);

            return this.each(function () {
                var $this = $(this);
                if ($this.is("select")) {
                    // If the last item is an optgroup, use that as a target
                    var target = $this;
                    var lastElement = $this.find(':last');
                    if (lastElement.is('optgroup')) {
                        target = lastElement;
                    }

                    // Append corner types
                    for (var i = 0; i < blendModes.length; ++i) {
                        target.append($('<option></option>')
                            .attr('value', blendModes[i].type)
                            .text(blendModes[i].name));
                    }
                }
            });
        }
    };

    /**
     * Adds a translated list of options to a selection that
     * represents the IFPaintCanvas.BlendMode choices
     * TODO : Replace select with visual selector with icons
     */
    $.fn.gBlendMode = function (method) {
        if (methods[method]) {
            return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
        } else if (typeof method === 'object' || !method) {
            return methods.init.apply(this, arguments);
        } else {
            $.error('Method ' + method + ' does not exist on jQuery.myPlugin');
        }
    }

}(jQuery));

================================================
FILE: src/application/component/colorbutton.js
================================================
(function ($) {

    var COLORPANEL = null;

    function getColorPanel() {
        if (!COLORPANEL) {
            COLORPANEL = $('<div></div>')
                .css('padding', '5px')
                .gColorPanel()
                .gOverlay();
        }
        return COLORPANEL;
    }

    var methods = {
        init: function (options) {
            options = $.extend({
                // Whether to behave as button or not, which, in the latter case,
                // means that the target element will only react on clicking but
                // not behave like a button with icon and background color
                transient: false,
                // Whether to automatically open the color chooser on click
                // or wait for a manual call to the open function
                autoOpen: true,
                // Whether to allow clearing the color or not
                allowClear: false,
                // Whether to immediately close after color has changed or not
                immediateClose: false,
                // Scene to be used for swatches
                scene: null
                // see options of gPatternTarget
            }, options);

            // always overwrite types to allow colors, only
            options.types = [IFPattern.Type.Color];

            return this.each(function () {
                var self = this;
                var $this = $(this);

                $this
                    .addClass('g-input')
                    .css('min-width', '20px')
                    .data('g-colorbutton', {
                        options: options,
                        scene: options.scene,
                        panelCloseListener: function (evt) {
                            var data = $this.data('g-colorbutton');
                            var colorPanel = getColorPanel();
                            colorPanel.gColorPanel('scene', null);
                            colorPanel.off('colorchange', data.panelChangeListener);
                            colorPanel.off('close', data.panelCloseListener);
                        },
                        panelChangeListener: function (evt, color) {
                            if (options.immediateClose) {
                                methods.close.call(self);
                            }

                            methods.value.call(self, color);
                            $this.trigger('colorchange', color);
                        }
                    })
                    .gPatternTarget(options)
                    .on('patternchange', function (evt, color) {
                        methods.value.call(self, color);
                    });

                if (!options.transient) {
                    $this
                        .addClass('g-cursor-pipette');
                }

                if (options.autoOpen) {
                    $this
                        .on('click', function () {
                            methods.open.call(self);
                        })
                }
            });
        },

        open: function () {
            var $this = $(this);
            var data = $this.data('g-colorbutton');
            var colorPanel = getColorPanel();
            colorPanel.gOverlay('close', this);
            colorPanel.gColorPanel('scene', data.scene);
            colorPanel.gColorPanel('value', methods.value.call(this));
            colorPanel.gColorPanel('allowClear', data.options.allowClear);
            colorPanel.on('colorchange', data.panelChangeListener);
            colorPanel.on('close', data.panelCloseListener);
            colorPanel.gOverlay('open', this);
            return this;
        },

        close: function () {
            var colorPanel = getColorPanel();
            colorPanel.gOverlay('close', this);
            return this;
        },

        scene: function (value) {
            var $this = $(this);
            var data = $this.data('g-colorbutton');

            if (!arguments.length) {
                return data.scene;
            } else {
                data.scene = value;
                return this;
            }
        },

        value: function (value) {
            var $this = $(this);
            var data = $this.data('g-colorbutton');

            if (!arguments.length) {
                return $this.gPatternTarget('value');
            } else {
                $this.gPatternTarget('value', value);

                if (!data.options.transient) {
                    $this.css('background', IFPattern.asCSSBackground(value));
                }

                return this;
            }
        }
    };

    /**
     * Block to transform buttons to color buttons
     */
    $.fn.gColorButton = function (method) {
        if (methods[method]) {
            return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
        } else if (typeof method === 'object' || !method) {
            return methods.init.apply(this, arguments);
        } else {
            $.error('Method ' + method + ' does not exist on jQuery.myPlugin');
        }
    }

}(jQuery));

================================================
FILE: src/application/component/colorpanel.js
================================================
(function ($) {
    /** @enum */
    var ViewType = {
        Palette: 'palette',
        Swatches: 'swatches',
        Trends: 'trends',
        Image: 'image'
    };

    /**
     * @private
     */
    ColorModes = [
        {
            type: IFColor.Type.RGB,
            name: 'RGB',
            components: [
                {
                    label: 'R',
                    min: 0,
                    max: 255,
                    unit: ' ',
                    stops: function (components) {
                        var rgba = components;
                        return [
                            new IFColor(IFColor.Type.RGB, [0, rgba[1], rgba[2], 100]),
                            new IFColor(IFColor.Type.RGB, [255, rgba[1], rgba[2], 100]),
                        ];
                    }
                },
                {
                    label: 'G',
                    min: 0,
                    max: 255,
                    unit: ' ',
                    stops: function (components) {
                        var rgba = components;
                        return [
                            new IFColor(IFColor.Type.RGB, [rgba[0], 0, rgba[2], 100]),
                            new IFColor(IFColor.Type.RGB, [rgba[0], 255, rgba[2], 100]),
                        ];
                    }
                },
                {
                    label: 'B',
                    min: 0,
                    max: 255,
                    unit: ' ',
                    stops: function (components) {
                        var rgba = components;
                        return [
                            new IFColor(IFColor.Type.RGB, [rgba[0], rgba[1], 0, 100]),
                            new IFColor(IFColor.Type.RGB, [rgba[0], rgba[1], 255, 100]),
                        ];
                    }
                },
                {
                    label: 'A',
                    min: 0,
                    max: 100,
                    unit: '%'
                }
            ],
            makeColor: function (components) {
                return new IFColor(IFColor.Type.RGB, components);
            }
        },
        {
            type: IFColor.Type.HSL,
            name: 'HSL',
            components: [
                {
                    label: 'H',
                    min: 0,
                    max: 360,
                    unit: '° ',
                    stops: function (components) {
                        var hsla = components;
                        var result = [];
                        var steps = 60;
                        for (var i = 0; i <= 360; i += steps) {
                            result.push(new IFColor(IFColor.Type.HSL, [i, hsla[1], hsla[2], 100]));
                        }
                        return result;
                    }
                },
                {
                    label: 'S',
                    min: 0,
                    max: 100,
                    unit: '%',
                    stops: function (components) {
                        var hsla = components;
                        return [
                            new IFColor(IFColor.Type.HSL, [hsla[0], 0, hsla[2], 100]),
                            new IFColor(IFColor.Type.HSL, [hsla[0], 100, hsla[2], 100]),
                        ];
                    }
                },
                {
                    label: 'L',
                    min: 0,
                    max: 100,
                    unit: '%',
                    stops: function (components) {
                        var hsla = components;
                        return [
                            new IFColor(IFColor.Type.HSL, [hsla[0], hsla[1], 0, 100]),
                            new IFColor(IFColor.Type.HSL, [hsla[0], hsla[1], 100, 100]),
                        ];
                    }
                },
                {
                    label: 'A',
                    min: 0,
                    max: 100,
                    unit: '%'
                }
            ],
            makeColor: function (components) {
                return new IFColor(IFColor.Type.HSL, components);
            }
        },
        {
            type: IFColor.Type.Tone,
            name: 'Tone',
            components: [
                {
                    label: 'T',
                    min: 0,
                    max: 100,
                    unit: '%',
                    stops: function (components) {
                        return [IFColor.parseCSSColor('white'), IFColor.parseCSSColor('black')];
                    }
                },
                {
                    label: 'A',
                    min: 0,
                    max: 100,
                    unit: '%'
                }
            ],
            makeColor: function (components) {
                return new IFColor(IFColor.Type.Tone, components);
            }
        },

        {
            type: IFColor.Type.CMYK,
            name: 'CMYK',
            components: [
                {
                    label: 'C',
                    min: 0,
                    max: 100,
                    unit: '%',
                    stops: function (components) {
                        var cmyk = components;
                        return [
                            new IFColor(IFColor.Type.CMYK, [0, cmyk[1], cmyk[2], cmyk[3]]),
                            new IFColor(IFColor.Type.CMYK, [100, cmyk[1], cmyk[2], cmyk[3]]),
                        ];
                    }
                },
                {
                    label: 'M',
                    min: 0,
                    max: 100,
                    unit: '%',
                    stops: function (components) {
                        var cmyk = components;
                        return [
                            new IFColor(IFColor.Type.CMYK, [cmyk[0], 0, cmyk[2], cmyk[3]]),
                            new IFColor(IFColor.Type.CMYK, [cmyk[0], 100, cmyk[2], cmyk[3]]),
                        ];
                    }
                },
                {
                    label: 'Y',
                    min: 0,
                    max: 100,
                    unit: '%',
                    stops: function (components) {
                        var cmyk = components;
                        return [
                            new IFColor(IFColor.Type.CMYK, [cmyk[0], cmyk[1], 0, cmyk[3]]),
                            new IFColor(IFColor.Type.CMYK, [cmyk[0], cmyk[1], 100, cmyk[3]]),
                        ];
                    }
                },
                {
                    label: 'K',
                    min: 0,
                    max: 100,
                    unit: '%',
                    stops: function (components) {
                        var cmyk = components;
                        return [
                            new IFColor(IFColor.Type.CMYK, [cmyk[0], cmyk[1], cmyk[2], 0]),
                            new IFColor(IFColor.Type.CMYK, [cmyk[0], cmyk[1], cmyk[2], 100]),
                        ];
                    }
                }
            ],
            makeColor: function (components) {
                return new IFColor(IFColor.Type.CMYK, components);
            }
        }
    ];

    var MAX_SWATCHES_PER_ROW = 21;

    // 21 per row
    var PALETTE = [
        '000000', '001F3F', 'DDDDDD', '000000', '003300', '006600', '009900', '00CC00', '00FF00', '330000', '333300', '336600', '339900', '33CC00', '33FF00', '660000', '663300', '666600', '669900', '66CC00', '66FF00',
        '333333', '0074D9', 'CCCCCC', '000033', '003333', '006633', '009933', '00CC33', '00FF33', '330033', '333333', '336633', '339933', '33CC33', '33FF33', '660033', '663333', '666633', '669933', '66CC33', '66FF33',
        '666666', '7FDBFF', 'BBBBBB', '000066', '003366', '006666', '009966', '00CC66', '00FF66', '330066', '333366', '336666', '339966', '33CC66', '33FF66', '660066', '663366', '666666', '669966', '66CC66', '66FF66',
        '999999', '39CCCC', 'AAAAAA', '000099', '003399', '006699', '009999', '00CC99', '00FF99', '330099', '333399', '336699', '339999', '33CC99', '33FF99', '660099', '663399', '666699', '669999', '66CC99', '66FF99',
        'CCCCCC', '3D9970', '999999', '0000CC', '0033CC', '0066CC', '0099CC', '00CCCC', '00FFCC', '3300CC', '3333CC', '3366CC', '3399CC', '33CCCC', '33FFCC', '6600CC', '6633CC', '6666CC', '6699CC', '66CCCC', '66FFCC',
        'FFFFFF', '2ECC40', '888888', '0000FF', '0033FF', '0066FF', '0099FF', '00CCFF', '00FFFF', '3300FF', '3333FF', '3366FF', '3399FF', '33CCFF', '33FFFF', '6600FF', '6633FF', '6666FF', '6699FF', '66CCFF', '66FFFF',
        'FF0000', '01FF70', '777777', '990000', '993300', '996600', '999900', '99CC00', '99FF00', 'CC0000', 'CC3300', 'CC6600', 'CC9900', 'CCCC00', 'CCFF00', 'FF0000', 'FF3300', 'FF6600', 'FF9900', 'FFCC00', 'FFFF00',
        '00FF00', 'FFDC00', '666666', '990033', '993333', '996633', '999933', '99CC33', '99FF33', 'CC0033', 'CC3333', 'CC6633', 'CC9933', 'CCCC33', 'CCFF33', 'FF0033', 'FF3333', 'FF6633', 'FF9933', 'FFCC33', 'FFFF33',
        '0000FF', 'FF851B', '555555', '990066', '993366', '996666', '999966', '99CC66', '99FF66', 'CC0066', 'CC3366', 'CC6666', 'CC9966', 'CCCC66', 'CCFF66', 'FF0066', 'FF3366', 'FF6666', 'FF9966', 'FFCC66', 'FFFF66',
        'FFFF00', 'FF4136', '444444', '990099', '993399', '996699', '999999', '99CC99', '99FF99', 'CC0099', 'CC3399', 'CC6699', 'CC9999', 'CCCC99', 'CCFF99', 'FF0099', 'FF3399', 'FF6699', 'FF9999', 'FFCC99', 'FFFF99',
        '00FFFF', '85144B', '333333', '9900CC', '9933CC', '9966CC', '9999CC', '99CCCC', '99FFCC', 'CC00CC', 'CC33CC', 'CC66CC', 'CC99CC', 'CCCCCC', 'CCFFCC', 'FF00CC', 'FF33CC', 'FF66CC', 'FF99CC', 'FFCCCC', 'FFFFCC',
        'FF00FF', 'B10DC9', '222222', '9900FF', '9933FF', '9966FF', '9999FF', '99CCFF', '99FFFF', 'CC00FF', 'CC33FF', 'CC66FF', 'CC99FF', 'CCCCFF', 'CCFFFF', 'FF00FF', 'FF33FF', 'FF66FF', 'FF99FF', 'FFCCFF', 'FFFFFF'];

    function createPaletteView($this) {
        var view = $('<table></table>');

        var parent = $('<tr></tr>').appendTo(view);

        var col = 0;
        for (var i = 0; i < PALETTE.length; ++i) {
            var color = '#' + PALETTE[i];

            $('<td></td>')
                .addClass('swatch')
                .css('background', color)
                .attr('data-color', color)
                .on('click', function () {
                    var color = IFColor.parseCSSColor($(this).attr('data-color'));
                    assignValue($this, color, false);
                    $this.trigger('colorchange', color);
                })
                .appendTo(parent);

            if (++col === MAX_SWATCHES_PER_ROW) {
                col = 0;
                parent = $('<tr></tr>').appendTo(view);
            }
        }

        return view;
    };

    function createSwatchesView($this) {
        var data = $this.data('gcolorpanel');

        return $('<div></div>')
            .gSwatchPanel({
                nullSwatch: $('<span></span>')
                    .addClass('fa fa-plus-circle'),
                // TODO : I18N
                nullName: 'Add current color as new swatch',
                types: [IFPattern.Type.Color],
                allowSelect: false
            })
            .on('swatchchange', function (evt, swatch) {
                if (!swatch) {
                    if (!data.scene || !data.color) {
                        return; // leave here, no color or scene
                    }

                    // Make sure there's no such color, yet
                    var swatches = data.scene.getSwatchCollection();
                    for (var node = swatches.getFirstChild(); node !== null; node = node.getNext()) {
                        if (node instanceof IFSwatch && node.getPatternType() === IFPattern.Type.Color) {
                            if (IFColor.equals(data.color, node.getProperty('pat'))) {
                                return; // leave here, colors are equal
                            }
                        }
                    }

                    // Ask for a name
                    var name = prompt('Enter a name for the new swatch:', data.color.asString());
                    if (name === null) {
                        return; // leave here, user has canceled
                    }
                    if (name.trim() === '') {
                        name = data.color.asString();
                    }

                    // Add current color as swatch
                    var editor = IFEditor.getEditor(data.scene);

                    if (editor) {
                        editor.beginTransaction();
                    }

                    try {
                        var swatch = new IFSwatch();
                        swatch.setProperties(['name', 'pat'], [name, data.color]);
                        swatches.appendChild(swatch);
                    } finally {
                        if (editor) {
                            editor.commitTransaction('Add Swatch');
                        }
                    }
                } else {
                    var color = swatch.getProperty('pat');
                    assignValue($this, color, false);
                    $this.trigger('colorchange', color);
                }
            });
    }

    function createTrendsView($this) {
        var _addTrend = function (index, view) {
            var label = null;
            switch (index) {
                // Tint
                case 1:
                    // TODO : I18N
                    label = 'Tint';
                    break;
                // Shade
                case 2:
                    label = 'Shade';
                    break;
                // Tone
                case 3:
                    label = 'Tone';
                    break;
            }


            $('<div></div>')
                .addClass('color-trend-label')
                .text(label)
                .appendTo(view);

            var container = $('<div></div>')
                .addClass('color-trend color-trend-' + index.toString())
                .append($('<div></div>')
                    .addClass('color-palette')
                    .append($('<div></div>')
                        .addClass('color-preview')))
                .append($('<div></div>')
                    .addClass('color-trend-value')
                    .append($('<input>')
                        .attr('type', 'text')
                        .on('input', function (evt) {
                            var val = $(this).val();
                            var color = updateTrendValue($this, index, val);
                        })))
                .appendTo(view);

            var preview = container.find('.color-preview');
            for (var i = 1; i <= 10; ++i) {
                $('<div>&nbsp;</div>')
                    .addClass('g-input color-trend-box-' + (i === 10 ? 'current' : i.toString()))
                    .css('width', '10%')
                    .on('click', function (evt) {
                        var color = IFColor.parseColor($(this).attr('data-color'));
                        if (color) {
                            assignValue($this, color, false);
                            $this.trigger('colorchange', color);
                        }
                    })
                    .appendTo(preview);
            }
        }.bind(this);

        var view = $('<div></div>');

        _addTrend(1, view);
        _addTrend(2, view);
        _addTrend(3, view);

        return view;
    }

    function colorForTrendAndValue($this, trend, value) {
        var sourceColor = $this.data('gcolorpanel').color;
        if (!sourceColor) {
            return null;
        }

        // Calculate a new color
        switch (trend) {
            // Tint
            case 1:
                return sourceColor.withTint(value);

            // Shade
            case 2:
                return sourceColor.withShade(value);

            // Tone
            case 3:
                return sourceColor.withTone(value);

            default:
                throw new Error('Unknown trend: ' + trend);
        }
    };

    function updateTrendValue($this, trend, value) {
        if (typeof value === 'string') {
            value = parseInt(value);
            if (isNaN(value)) {
                value = 50;
            }
        }
        if (value < 0) {
            value = 0;
        } else if (value > 100) {
            value = 100;
        }


        var trendsView = $this.find('[data-view="trends"]');
        var container = trendsView.find('.color-trend-' + trend.toString());

        var newColor = colorForTrendAndValue($this, trend, value);

        container.find('.color-trend-box-current')
            .attr('data-color', newColor ? newColor.asString() : '')
            .css('background', IFPattern.asCSSBackground(newColor));

        container.find('.color-trend-value > input')
            .val(value);

        return newColor;
    }

    function updateTrends($this) {
        var trendsView = $this.find('[data-view="trends"]');

        for (var i = 1; i <= 3; ++i) {
            var container = trendsView.find('.color-trend-' + i.toString());
            for (var k = 1; k <= 9; ++k) {
                var newColor = colorForTrendAndValue($this, i, k * 10);

                // Assign to color box
                container
                    .find('.color-trend-box-' + k.toString())
                    .attr('data-color', newColor ? newColor.asString() : '')
                    .css('background', IFPattern.asCSSBackground(newColor));

                // If this is 50% then assign to current trend box
                // and update it's text input
                if (k === 5) {
                    updateTrendValue($this, i, 50);
                }
            }
        }
    }

    function cvColorThiefColor(color) {
        return new IFColor(IFColor.Type.RGB, [color[0], color[1], color[2], 100]);
    }

    function createImageView($this) {
        return $('<div></div>')
            .append($('<div></div>')
                .addClass('image-panel')
                .text('Drag an image here')
                .on('dragenter', function () {
                    $(this).css('background', 'maroon');
                    return false;
                })
                .on('dragleave', function () {
                    $(this).css('background', '');
                    return false;
                })
                .on('dragover', function () {
                    return false;
                })
                .on('drop', function (event) {
                    var imagePanel = $(this);
                    imagePanel.css('background', '');

                    var palettePanel = imagePanel.closest('[data-view]').find('.image-palette');
                    palettePanel.empty();

                    var _addPaletteColor = function (color) {
                        $('<div></div>')
                            .css('background', IFPattern.asCSSBackground(color))
                            .on('click', function () {
                                assignValue($this, color, false);
                                $this.trigger('colorchange', color);
                            }.bind(this))
                            .appendTo(palettePanel);
                    }.bind(this);

                    var files = event.originalEvent.dataTransfer.files;
                    var fileCount = files.length;
                    var imageType = /image.*/;

                    for (var i = 0; i < fileCount; i++) {
                        var file = files[i];

                        if (file.type.match(imageType)) {
                            var reader = new FileReader();
                            reader.onload = function (event) {
                                var image = new Image();
                                image.src = event.target.result;
                                image.onload = function () {
                                    var colorThief = new ColorThief();
                                    var mainColor = cvColorThiefColor(colorThief.getColor(image));
                                    _addPaletteColor(mainColor);

                                    var palette = colorThief.getPalette(image, 8);
                                    for (var i = 0; i < palette.length; ++i) {
                                        var convertedColor = cvColorThiefColor(palette[i]);

                                        // Take care to avoid duplications with dominant color
                                        if (!IFColor.equals(convertedColor, mainColor)) {
                                            _addPaletteColor(convertedColor);
                                        }
                                    }
                                }.bind(this);

                                imagePanel
                                    .css('background-image', 'url(' + event.target.result + ')')
                                    .text('');
                            }.bind(this);
                            reader.readAsDataURL(file);
                            break;
                        }
                    }

                    return false;
                }))
            .append($('<div></div>')
                .addClass('image-palette'));
    }

    function createView($this, viewType) {
        var view = null;

        if (viewType === ViewType.Palette) {
            view = createPaletteView($this);
        } else if (viewType === ViewType.Swatches) {
            view = createSwatchesView($this);
        } else if (viewType === ViewType.Trends) {
            view = createTrendsView($this);
        } else if (viewType === ViewType.Image) {
            view = createImageView($this);
        } else {
            throw new Error('Unknown color view: ' + viewType);
        }

        if (view) {
            view
                .attr('data-view', viewType)
                .css('display', 'none')
                .appendTo($this.find('.color-view'));
        }
    }

    function updateMatches($this) {
        var data = $this.data('gcolorpanel');
        var palettePanel = $this.find('.matcher-palette');
        palettePanel.empty();

        if (data.matcher && data.color) {
            var _addMatchColor = function (color, width) {
                $('<div></div>')
                    .css('width', width.toString() + '%')
                    .css('background', IFPattern.asCSSBackground(color))
                    .on('click', function () {
                        assignValue($this, color, false);
                        $this.trigger('colorchange', color);
                    }.bind(this))
                    .appendTo(palettePanel);
            }.bind(this);

            var matches = data.matcher.match(data.color);
            if (matches && matches.length > 0) {
                var len = Math.min(matches.length, 8);
                var width = 100 / len;
                for (var i = 0; i < len; ++i) {
                    // Convert match color to same type as curent color if any
                    var match = data.color ? matches[i].toType(data.color.getType()) : matches[i];
                    _addMatchColor(match, width);
                }
            }
        }
    };

    function activateMatcher($this, matcher) {
        var data = $this.data('gcolorpanel');
        data.matcher = matcher;

        updateMatches($this);
    };

    function activateColorMode($this, colorMode) {
        var data = $this.data('gcolorpanel');

        if (!data.colorMode || colorMode !== data.colorMode.type.key) {
            for (var i = 0; i < ColorModes.length; ++i) {
                var modeInfo = ColorModes[i];
                if (modeInfo.type.key === colorMode) {
                    data.colorMode = modeInfo;

                    // Activate sliders
                    for (var i = 0; i < 4; ++i) {
                        var componentPanel = $this.find('.color-component-' + i.toString());

                        if (i >= modeInfo.components.length) {
                            componentPanel.css('visibility', 'hidden');
                        } else {
                            componentPanel.css('visibility', '');

                            var component = modeInfo.components[i];
                            var range = componentPanel.find('input[type="range"]');
                            var label = componentPanel.find('.color-label');
                            var unit = componentPanel.find('.color-unit');

                            label.text(component.label);
                            unit.text(component.unit);
                            unit.css('display', component.unit != '' ? '' : 'none');

                            range.attr('min', component.min);
                            range.attr('max', component.max);
                        }
                    }

                    updateToComponents($this);
                    updateFromComponents($this);

                    break;
                }
            }
        }
        $this.find('.color-mode-select').val(data.colorMode ? data.colorMode.type.key : IFColor.Type.RGB.key);
    }

    function updateFromComponents($this) {
        var data = $this.data('gcolorpanel');

        // Collect component values / correct them for current mode
        var components = [];
        for (var i = 0; i < data.colorMode.components.length; ++i) {
            var component = data.colorMode.components[i];
            var componentEl = $this.find('.color-component-' + i.toString());
            var textInput = componentEl.find('input[type="text"]');
            var rangeInput = componentEl.find('input[type="range"]');
            var value = parseInt(textInput.val());

            if (isNaN(value) || value < component.min) {
                value = component.min;
            } else if (value > component.max) {
                value = component.max;
            }

            // Push value
            components.push(value);

            // Update inputs with correct value
            textInput.val(value);
            rangeInput.val(value);
        }

        var color = data.colorMode.makeColor(components);
        assignValue($this, color);
        return color;
    }

    function updateToComponents($this) {
        var data = $this.data('gcolorpanel');

        var color = data.color ? data.color : IFColor.TRANSPARENT_WHITE;

        // Get the components in the right format
        var components = null;
        if (data.colorMode.type === IFColor.Type.RGB) {
            components = color.asRGB();
        } else if (data.colorMode.type === IFColor.Type.HSL) {
            components = color.asHSL();
        } else if (data.colorMode.type === IFColor.Type.Tone) {
            components = color.asTone();
        } else if (data.colorMode.type === IFColor.Type.CMYK) {
            components = color.asCMYK();
        } else {
            throw new Error('Unknown mode.');
        }

        if (components) {
            for (var i = 0; i < data.colorMode.components.length; ++i) {
                var component = data.colorMode.components[i];
                var componentEl = $this.find('.color-component-' + i.toString());
                var textInput = componentEl.find('input[type="text"]');
                var rangeInput = componentEl.find('input[type="range"]');
                var val = Math.min(component.max, Math.max(component.min, components[i])).toFixed(0);

                var stopsFunc = data.colorMode.components[i].stops;
                var stops = stopsFunc ? stopsFunc(components) : null;

                if (stops) {
                    if (stops.length === 1) {
                        rangeInput.css('background', stops[0].asCSSString());
                    } else {
                        var cssStops = '';
                        for (var s = 0; s < stops.length; ++s) {
                            if (cssStops !== '') {
                                cssStops += ',';
                            }
                            cssStops += stops[s].asCSSString();
                        }
                        rangeInput.css('background', 'linear-gradient(90deg, ' + cssStops + ')');
                    }
                } else {
                    rangeInput.css('background', '');
                }

                textInput.val(val);
                rangeInput.val(val);
            }
        }
    }

    function assignValue($this, value, overwritePrevious) {
        var data = $this.data('gcolorpanel');

        value = typeof value === 'string' ? IFColor.parseColor(value) : value;

        data.color = value;

        if (overwritePrevious) {
            data.previousColor = value;
        }

        $this.find('input[type="color"]').val(value ? value.asHTMLHexString() : '');
        $this.find('.previous-color').css('background', IFPattern.asCSSBackground(data.previousColor));
        $this.find('.current-color').css('background', IFPattern.asCSSBackground(data.color));
        $this.find('.color-input').val(data.color ? data.color.asHTMLHexString() : '');

        // Show color difference
        var colorDiff = '&ndash;';
        if (data.previousColor && data.color) {
            var diff = data.color.difference(data.previousColor);
            colorDiff = '&Delta;&nbsp;' + (diff < 0 ? ifUtil.formatNumber(diff, 2) : diff.toFixed(0));
        }
        $this.find('.color-difference').html(colorDiff);


        activateColorMode($this, value ? value.getType().key : IFColor.Type.RGB.key);
        updateMatches($this);
        updateToComponents($this);
        updateTrends($this);
    }

    var methods = {
        init: function (options) {
            options = $.extend({
                // The default view
                defaultView: ViewType.Palette
            }, options);

            return this.each(function () {
                var self = this;

                var data = {
                    options: options,
                    allowClear: false,
                    scene: null
                };

                var $this = $(this)
                    .data('gcolorpanel', data);

                $this
                    .addClass('g-color-panel')
                    .append($('<input>')
                        .attr('type', 'color')
                        .css({
                            'position': 'absolute',
                            'visibility': 'hidden'
                        })
                        .on('change', function () {
                            var color = IFColor.parseCSSColor($(this).val());
                            assignValue($this, color, false);
                            $this.trigger('colorchange', color);
                        }))
                    .append($('<div></div>')
                        .addClass('toolbar')
                        .append($('<div></div>')
                            .addClass('section-start')
                            .append($('<button></button>')
                                .attr('data-action', 'clear')
                                // TODO : I18N
                                .attr('title', 'Clear Color')
                                .css('display', 'none')
                                .append($('<span></span>')
                                    .addClass('fa fa-ban'))
                                .on('click', function () {
                                    assignValue($this, null, false);
                                    $this.trigger('colorchange', null);
                                }))
                            .append($('<button></button>')
                                .attr('data-action', 'system-color')
                                // TODO : I18N
                                .attr('title', 'System')
                                .append($('<span></span>')
                                    .addClass('fa fa-cog'))
                                .on('click', function () {
                                    $this.find('input[type="color"]').trigger('click');
                                })))
                        .append($('<div></div>')
                            .addClass('section-center')
                            .append($('<button></button>')
                                .attr('data-activate-view', ViewType.Palette)
                                // TODO : I18N
                                .attr('title', 'Palette')
                                .append($('<span></span>')
                                    .addClass('fa fa-th')))
                            .append($('<button></button>')
                                .attr('data-activate-view', ViewType.Swatches)
                                // TODO : I18N
                                .attr('title', 'Swatches')
                                .css('display', 'none') // of by default
                                .append($('<span></span>')
                                    .addClass('fa fa-bars')))
                            .append($('<button></button>')
                                .attr('data-activate-view', ViewType.Trends)
                                // TODO : I18N
                                .attr('title', 'Trends')
                                .append($('<span></span>')
                                    .addClass('fa fa-sliders')))
                            .append($('<button></button>')
                                .attr('data-activate-view', ViewType.Image)
                                // TODO : I18N
                                .attr('title', 'From Image')
                                .append($('<span></span>')
                                    .addClass('fa fa-image'))))
                        .append($('<div></div>')
                            .addClass('section-end')
                            .append($('<select></select>')
                                .addClass('color-mode-select')
                                .on('change', function () {
                                    activateColorMode($this, $(this).val());
                                }))))
                    .append($('<div></div>')
                        .addClass('color-view'))
                    .append($('<div></div>')
                        .addClass('color-components'))
                    .append($('<hr/>'))
                    .append($('<div></div>')
                        .addClass('color')
                        .append($('<div></div>')
                            .append($('<div>&nbsp;</div>')
                                .addClass('previous-color g-input')
                                .on('click', function () {
                                    if (data.previousColor) {
                                        assignValue($this, data.previousColor, false);
                                        $this.trigger('colorchange', data.previousColor);
                                    }
                                }))
                            .append($('<div>&nbsp;</div>')
                                .addClass('current-color g-input'))
                            .append($('<div></div>')
                                .addClass('color-difference g-input')
                                // TODO : I18N
                                .attr('title', 'Color Difference (CIEDE2000)'))
                            .append($('<input>')
                                .addClass('color-input')
                                .on('change', function () {
                                    var color = IFColor.parseCSSColor($(this).val());
                                    if (color) {
                                        assignValue($this, color, false);
                                        $this.trigger('colorchange', color);
                                    }
                                })))
                        .append($('<div></div>')
                            .append($('<select></select>')
                                .addClass('matcher-select')
                                .on('change', function () {
                                    activateMatcher($this, $(this).find(':selected').data('matcher'));
                                }))))
                    .append($('<div></div>')
                        .addClass('matcher-palette'));

                $this.find('[data-activate-view]').each(function (index, element) {
                    var $element = $(element);
                    $element
                        .on('click', function (evt) {
                            methods.view.call(self, $element.attr('data-activate-view'));
                        });
                });

                // Initiate components
                var components = $this.find('.color-components');
                var _addComponent = function (index) {
                    $('<div></div>')
                        .addClass('color-component color-component-' + index.toString())
                        .append($('<div></div>')
                            .addClass('color-label')
                            .on('click', function () {
                                var input = $this.find('.color-component-' + index.toString() + ' input[type="text"]');
                                if (input.val() == data.colorMode.components[index].max) {
                                    input.val(data.colorMode.components[index].min);
                                } else {
                                    input.val(data.colorMode.components[index].max);
                                }

                                var color = updateFromComponents($this);
                                $this.trigger('colorchange', color);
                            }.bind(this)))
                        .append($('<div></div>')
                            .addClass('color-range')
                            .append($('<input>')
                                .attr('type', 'range')
                                .attr('tabIndex', '-1')
                                .on('input', function (evt) {
                                    $this.find('.color-component-' + index.toString() + ' input[type="text"]').val($(evt.target).val());
                                    var color = updateFromComponents($this);
                                    $this.trigger('colorchange', color);
                                }.bind(this))))
                        .append($('<div></div>')
                            .addClass('color-value')
                            .append($('<input>')
                                .attr('type', 'text')
                                .on('input', function () {
                                    var color = updateFromComponents($this);
                                    $this.trigger('colorchange', color);
                                })))
                        .append($('<div></div>')
                            .addClass('color-unit'))
                        .appendTo(components);
                }.bind(this);

                for (var i = 0; i < 4; ++i) {
                    _addComponent(i);
                }

                // Init color modes
                var colorModeSelect = $this.find('.color-mode-select');
                for (var i = 0; i < ColorModes.length; ++i) {
                    var modeInfo = ColorModes[i];
                    $('<option></option>')
                        .attr('value', modeInfo.type.key)
                        .text(modeInfo.name)
                        .appendTo(colorModeSelect);

                    if (!data.colorMode) {
                        activateColorMode($this, modeInfo.type.key);
                    }
                }

                // Initiate matchers
                var matcherSelect = $this.find('.matcher-select');
                var matcherGroup = matcherSelect;
                var lastCategory = null;
                for (var i = 0; i < gravit.colorMatchers.length; ++i) {
                    var matcher = gravit.colorMatchers[i];
                    var category = ifLocale.get(matcher.getCategory());

                    // Add to selector
                    if (!lastCategory || category !== lastCategory) {
                        matcherGroup = $('<optgroup></optgroup>')
                            .attr('label', category)
                            .appendTo(matcherSelect);
                        lastCategory = category;
                    }

                    $('<option></option>')
                        .data('matcher', matcher)
                        .text(ifLocale.get(matcher.getTitle()))
                        .appendTo(matcherGroup);

                    if (!data.matcher) {
                        activateMatcher($this, matcher);
                    }
                }

                // Init views
                for (var view in ViewType) {
                    if (ViewType.hasOwnProperty(view)) {
                        createView($this, ViewType[view]);
                    }
                }

                // Set default view
                methods.view.call(self, options.defaultView);
            });
        },

        view: function (value) {
            var self = this;
            var $this = $(this);
            var data = $this.data('gcolorpanel');

            if (!arguments.length) {
                return data.view;
            } else {
                if (value !== data.view) {
                    data.view = value;

                    $this.find('[data-activate-view]').each(function (index, element) {
                        var $element = $(element);
                        $element.toggleClass('g-active', $element.attr('data-activate-view') === value);
                    });

                    $this.find('.color-view > *').each(function (index, element) {
                        var $element = $(element);
                        $element.css('display', $element.attr('data-view') === value ? '' : 'none');
                    });
                }
                return this;
            }
        },

        allowClear: function (value) {
            var $this = $(this);
            if (!arguments.length) {
                return $this.data('gcolorpanel').allowClear;
            } else {
                // TODO : Detach & Attach listeners & Change active view if swatches & value=null
                $this.data('gcolorpanel').allowClear = value;
                $this.find('[data-action="clear"]')
                    .css('display', value ? '' : 'none');
                return this;
            }
        },

        scene: function (value) {
            var $this = $(this);
            var data = $this.data('gcolorpanel');

            if (!arguments.length) {
                return data.scene;
            } else {
                var oldScene = data.scene;
                data.scene = value;

                // Update swatches
                var swatchView = $this.find('[data-view="' + ViewType.Swatches + '"]');
                if (oldScene) {
                    swatchView.gSwatchPanel('detach');
                }

                if (data.scene) {
                    swatchView.gSwatchPanel('attach', data.scene.getSwatchCollection());
                }

                $this.find('[data-activate-view="' + ViewType.Swatches + '"]')
                    .css('display', data.scene ? '' : 'none');

                return this;
            }
        },

        value: function (value) {
            var $this = $(this);
            if (!arguments.length) {
                return $this.data('gcolorpanel').color;
            } else {
                var data = $this.data('gcolorpanel');
                assignValue($this, value, true);

                if (data.scene && (!data.color || data.color.getType() === IFColor.Type.Black || data.color.getType() === IFColor.Type.White || data.color.getType() === IFColor.Type.Registration)) {
                    var clspace = data.scene.getProperty('clspace');
                    if (clspace) {
                        for (var i = 0; i < ColorModes.length; ++i) {
                            if (ColorModes[i].type.space === clspace) {
                                activateColorMode($this, ColorModes[i].type.key);
                                break;
                            }
                        }
                    }
                }

                return this;
            }
        }
    };

    /**
     * Block to transform divs to color panels
     */
    $.fn.gColorPanel = function (method) {
        if (methods[method]) {
            return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
        } else if (typeof method === 'object' || !method) {
            return methods.init.apply(this, arguments);
        } else {
            $.error('Method ' + method + ' does not exist on jQuery.myPlugin');
        }
    }

}
    (jQuery)
    )
;


================================================
FILE: src/application/component/cornertype.js
================================================
(function ($) {

    var cornerTypes = [
        {
            type: IFPathBase.CornerType.Rounded,
            // TODO : I18N
            name: 'Rounded'
        },
        {
            type: IFPathBase.CornerType.InverseRounded,
            // TODO : I18N
            name: 'Inverse Rounded'
        },
        {
            type: IFPathBase.CornerType.Bevel,
            // TODO : I18N
            name: 'Beveled'
        },
        {
            type: IFPathBase.CornerType.Inset,
            // TODO : I18N
            name: 'Inset'
        },
        {
            type: IFPathBase.CornerType.Fancy,
            // TODO : I18N
            name: 'Fancy'
        }
    ];

    var methods = {
        init: function (options) {
            options = $.extend({
            }, options);

            return this.each(function () {
                var $this = $(this);
                if ($this.is("select")) {
                    // If the last item is an optgroup, use that as a target
                    var target = $this;
                    var lastElement = $this.find(':last');
                    if (lastElement.is('optgroup')) {
                        target = lastElement;
                    }

                    // Append corner types
                    for (var i = 0; i < cornerTypes.length; ++i) {
                        target.append($('<option></option>')
                            .attr('value', cornerTypes[i].type)
                            .text(cornerTypes[i].name));
                    }
                }
            });
        }
    };

    /**
     * Adds a translated list of options to a selection that
     * represents the IFBasePath.CornerType choices
     * TODO : Replace select with visual selector with icons
     */
    $.fn.gCornerType = function (method) {
        if (methods[method]) {
            return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
        } else if (typeof method === 'object' || !method) {
            return methods.init.apply(this, arguments);
        } else {
            $.error('Method ' + method + ' does not exist on jQuery.myPlugin');
        }
    }

}(jQuery));

================================================
FILE: src/application/component/gradienteditor.js
================================================
(function ($) {

    function updateStop($this, $stop) {
        $stop.css('left', $stop.data('stop-position') + '%');
        $stop.find('.stop-color').css('background', $stop.data('stop-color').asCSSString());
        $stop.gColorButton('value', $stop.data('stop-color'));

        updateGradient($this);
    }

    function updateGradient($this) {
        var stops = $this.gGradientEditor('value');
        var cssStops = [];

        for (var i = 0; i < stops.length; ++i) {
            var stop = stops[i];
            cssStops.push('' + stop.color.asCSSString() + ' ' + stop.position + '%');
        }

        $this.find('.gradient').css('background', 'linear-gradient(90deg, ' + cssStops.join(", ") + ')');

        $this.gPatternTarget('value', new IFGradient(stops));
    }

    var methods = {
        init: function (options) {
            options = $.extend({
                // An attached scene used for swatch handling
                // when coosing a color
                scene: null
            }, options);

            return this.each(function () {
                var self = this;

                var data = {
                    options: options
                };

                var $this = $(this)
                    .addClass('g-gradient-editor')
                    .data('ggradienteditor', data)
                    .gPatternTarget({
                        types: [IFPattern.Type.Color, IFPattern.Type.Gradient]
                    })
                    .on('patterndrop', function (evt, pattern, mouseEvent) {
                        if (pattern && pattern instanceof IFColor) {
                            var $stops = $(this).find('.stops');
                            var stopsWidth = $stops.width();
                            var relativePos = mouseEvent.pageX - $stops.offset().left;
                            var percentPos = relativePos <= 0 ? 0 :
                                relativePos >= stopsWidth ? 100 : (relativePos / stopsWidth * 100);
                            methods.insertStop.call(self, percentPos, pattern);
                            $this.trigger('change');
                        } else if (pattern instanceof IFGradient) {
                            methods.value.call(self, pattern.getStops());
                            $this.trigger('change');
                        }
                    });

                var container = $('<div></div>')
                    .addClass('container')
                    .appendTo($this);

                container
                    .append($('<div></div>')
                        .addClass('editor')
                        .append($('<div></div>')
                            .addClass('gradient g-input')
                            .css('background', 'transparent'))
                        .append($('<div></div>')
                            .addClass('stops')
                            .on('mousedown', function (evt) {
                                // Prevents any accident drag'n'drop actions
                                evt.preventDefault();
                                evt.stopPropagation();
                            })
                            .on('dblclick', function (evt) {
                                var $stops = $(evt.target);
                                if ($stops.hasClass('stops')) {
                                    // Calculate insert position
                                    // TODO : Calculate gradient color at given position and set it
                                    var stopsWidth = $stops.width();
                                    var relativePos = evt.pageX - $stops.offset().left;
                                    var percentPos = relativePos <= 0 ? 0 :
                                        relativePos >= stopsWidth ? 100 : (relativePos / stopsWidth * 100);

                                    var finalPosition = methods.insertStop.call(self, percentPos, IFColor.parseCSSColor('black'));
                                    $this.trigger('change');

                                    $stops.find('> .stop').each(function (index, element) {
                                        var $stop = $(element);
                                        if ($stop.data('stop-position') === finalPosition) {
                                            $stop.gColorButton('open');
                                            return false;
                                        }
                                    });
                                }
                            })));
            });
        },

        value: function (value) {
            var self = this;
            var $this = $(this);
            var data = $this.data('ggradienteditor');
            var $stops = $this.find('.stops > .stop');

            if (!arguments.length) {
                var stops = [];
                $stops.each(function (index, element) {
                    var $stop = $(element);
                    if ($stop.data('stop-delete') === false) {
                        stops.push({
                            position: $stop.data('stop-position'),
                            color: $stop.data('stop-color')
                        });
                    }
                });

                stops.sort(function (a, b) {
                    return a.position > b.position;
                });

                return stops;
            } else {
                // Shortcut: If stops lengths are equal,
                // do a simple stop-update instead of clearing
                if (value && value.length === $stops.length) {
                    $stops.each(function (index, element) {
                        var $stop = $(element);
                        $stop
                            .data('stop-position', value[index].position)
                            .data('stop-color', value[index].color)
                            .data('stop-delete', false);

                        updateStop($this, $stop);
                    });
                } else {
                    // Clear stops and add all again
                    $this.find('.stops').empty();

                    // Insert all stops now if any
                    if (value) {
                        for (var i = 0; i < value.length; ++i) {
                            methods.insertStop.call(self, value[i].position, value[i].color);
                        }
                    }
                }

                return this;
            }
        },

        insertStop: function (position, color) {
            var self = this;
            var $this = $(this);
            var data = $this.data('ggradienteditor');
            var $stops = $this.find('.stops');

            // Normalize position
            position = Math.round(position);
            position = position < 0 ? 0 : position > 100 ? 100 : position;

            var hasChanged = false;

            // Insert stop widget
            var $stop = $('<div></div>')
                .addClass('stop')
                .data('stop-position', position)
                .data('stop-color', color)
                .data('stop-delete', false)
                .gColorButton({
                    drag: false,
                    transient: true,
                    autoOpen: false,
                    scene: data.options.scene
                })
                .append($('<div></div>')
                    .addClass('stop-color'))
                .on('colorchange', function (evt, color) {
                    if (color) {
                        $stop.data('stop-color', color);
                        updateStop($this, $stop);
                        $this.trigger('change');
                    }
                })
                .on('click', function (evt) {
                    if (!hasChanged) {
                        $(this).gColorButton('open');
                    }
                })
                .on('mousedown', function (evt) {
                    hasChanged = false;

                    // Implement dragging stuff
                    var stopsOffset = $stops.offset();
                    var moveMinX = stopsOffset.left;
                    var moveMaxX = moveMinX + $stops.width();
                    var moveMaxY = stopsOffset.top + $stops.outerHeight();
                    var stopWidth = $stop.outerWidth();
                    var startPosX = $stop.offset().left + stopWidth - evt.pageX;
                    var startPosY = $stop.offset().top - evt.pageY;

                    var $document = $(document);

                    var docMouseMove = function (evt) {
                        var left = evt.pageX + startPosX - (stopWidth / 2);
                        var top = evt.pageY + startPosY;

                        // Ensure to not move outside our range horizontally
                        if (left <= moveMinX) {
                            left = moveMinX;
                        } else if (left >= moveMaxX) {
                            left = moveMaxX;
                        }

                        if (top > moveMaxY) {
                            // Moved outside area so get rid of our stop
                            $stop
                                .css('display', 'none')
                                .data('stop-delete', true);
                        } else {
                            $stop
                                .css('display', '')
                                .data('stop-delete', false);
                        }

                        // Calculate percentage for stop
                        var relativePos = left - moveMinX;
                        var relativeMoveArea = (moveMaxX - moveMinX);
                        var percentPos = relativePos <= 0 ? 0 :
                            relativePos >= relativeMoveArea ? 100 : (relativePos / relativeMoveArea * 100);

                        $stop.data('stop-position', percentPos);
                        updateStop($this, $stop);
                        hasChanged = true;
                    };

                    var docMouseUp = function (evt) {
                        // Clear the document listeners
                        $document
                            .off("mousemove", docMouseMove)
                            .off("mouseup", docMouseUp);

                        // Delete the stop if marked and we
                        // still have at least two steps left
                        var triggerChange = true;
                        if ($stop.data('stop-delete') === true) {
                            if ($stops.find('> .stop').length > 2) {
                                $stop.remove();
                                hasChanged = true;
                            } else {
                                $stop.data('stop-delete', false);
                                hasChanged = true;
                                triggerChange = false;
                            }
                        }

                        if (hasChanged && triggerChange) {
                            $this.trigger('change');
                        }
                    };

                    $document
                        .on("mousemove", docMouseMove)
                        .on("mouseup", docMouseUp);
                })
                .appendTo($stops);

            updateStop($this, $stop);

            return position;
        }
    };

    /**
     * Initiates a gradient editor on a given div
     */
    $.fn.gGradientEditor = function (method) {
        if (methods[method]) {
            return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
        } else if (typeof method === 'object' || !method) {
            return methods.init.apply(this, arguments);
        } else {
            $.error('Method ' + method + ' does not exist on jQuery.myPlugin');
        }
    }
})(jQuery);

================================================
FILE: src/application/component/menu.js
================================================
(function (_) {
    /**
     * A menu implementation
     * @param {GMenuItem|GMenuBar} parent parent if not a standalone menu
     * @class GMenu
     * @extends GEventTarget
     * @constructor
     * @version 1.0
     */
    function GMenu(parent) {
        this._parent = parent;
        this._htmlElement = $("<ul></ul>").addClass('g-menu');
        this._htmlElement.on("mouseover", this._mouseOver.bind(this));
        this._htmlElement.on("mouseout", this._mouseOut.bind(this));
    };

    IFObject.inherit(GMenu, GEventTarget);

    /**
     * A position a menu can be opened at
     * @enum
     */
    GMenu.Position = {
        Left_Top: 0,
        Center: 1,
        Right_Bottom: 2
    };

    /**
     * Global, active menu
     * @type {GMenu}
     * @private
     */
    GMenu._activeMenu = null;

    /**
     * Array of tracked mouse locations
     * @type {Array<{{x:Number, y: Number}}>}
     * @private
     */
    GMenu._activeMenuMouseLocations = null;

    /**
     * Get the globally, active menu
     * @returns {GMenu}
     */
    GMenu.getActiveMenu = function () {
        return GMenu._activeMenu;
    };

    /**
     * Assign the globally, active menu, closing any active one, first
     * @param {GMenu} menu if null, only closes
     */
    GMenu.setActiveMenu = function (menu, noCloseCall) {
        // Close any active menu, first
        if (this._activeMenu) {
            if (!noCloseCall) {
                this._activeMenu.close();
            }

            this._activeMenu = null;

            // Remove global menu listeners
            document.removeEventListener("mousemove", GMenu._activeMenuMouseMoveListener);
            document.removeEventListener("mousedown", GMenu._activeMenuMouseUpDownListener);
            document.removeEventListener("mouseup", GMenu._activeMenuMouseUpDownListener);
            document.removeEventListener("keyup", GMenu._activeMenuKeyDownListener);
        }

        this._activeMenu = menu;

        // Assign a new, active menu if any
        if (this._activeMenu) {
            // Register menu listeners
            document.addEventListener("mousemove", GMenu._activeMenuMouseMoveListener);
            document.addEventListener("mousedown", GMenu._activeMenuMouseUpDownListener);
            // Mouse down close listener needs slight timeout to not hit the 'click' event and
            // immediately close any active menu after mouse up
            setTimeout(function () {
                document.addEventListener("mouseup", GMenu._activeMenuMouseUpDownListener);
            }, 250);
            document.addEventListener("keyup", GMenu._activeMenuKeyDownListener);
        }
    };

    /**
     * Method listening on document mouse move for tracking
     * the last mouse locations
     * @param evt
     * @private
     */
    GMenu._activeMenuMouseMoveListener = function (evt) {
        if (!GMenu._activeMenuMouseLocations) {
            GMenu._activeMenuMouseLocations = [];
        }

        GMenu._activeMenuMouseLocations.push({
            x: evt.pageX,
            y: evt.pageY
        });

        if (GMenu._activeMenuMouseLocations.length > 3) {
            GMenu._activeMenuMouseLocations.shift();
        }
    };

    /**
     * Method listening on document mouse down/up and simply
     * closes the active menu if any
     * @param evt
     * @private
     */
    GMenu._activeMenuMouseUpDownListener = function (evt) {
        GMenu.setActiveMenu(null);
    };

    /**
     * Method listening on document key event and closes
     * the active menu if any when the ESC is hit
     * @param evt
     * @private
     */
    GMenu._activeMenuKeyDownListener = function (evt) {
        if (evt.keyCode == 27) {
            GMenu.setActiveMenu(null);
        }
    };

    /**
     * Creates a menu out of the registered actions
     * @param {Array<GAction>} actions array of actions to create a menu of
     * @param {GMenu} targetMenu the menu to create the action structure into
     */
    GMenu.createActionMenu = function (actions, targetMenu) {
        // TODO : Order given actions by category & group

        var itemToGroupArray = [];

        var _getGroupForItem = function (item) {
            for (var i = 0; i < itemToGroupArray.length; ++i) {
                if (itemToGroupArray[i].item === item) {
                    return itemToGroupArray[i].group;
                }
            }
        };

        var _addItemGroupAndDivider = function (menu, item, group) {
            if (menu.getItemCount() > 0) {
                var lastGroup = _getGroupForItem(menu.getItem(menu.getItemCount() - 1));
                if (lastGroup !== group) {
                    menu.addItem(new GMenuItem(GMenuItem.Type.Divider));
                }
            }
            itemToGroupArray.push({
                item: item,
                group: group
            });
        };

        for (var i = 0; i < actions.length; ++i) {
            var action = actions[i];

            if (!action.isAvailable()) {
                continue;
            }

            var category = ifLocale.get(action.getCategory());
            var group = action.getGroup();
            var categories = category ? category.split('/') : null;
            var groups = group ? [""].concat(group.split('/')) : null;

            if (groups && categories && categories.length !== groups.length - 1) {
                throw new Error("Number of categories different thant number of groups.");
            }

            // Build up our structure by iterating our categories
            var currentMenu = targetMenu;
            if (categories) {
                for (var k = 0; k < categories.length; ++k) {
                    var category = categories[k];
                    var group = groups ? groups[k] : null;
                    var item = currentMenu.findItem(category);
                    if (!item) {
                        item = new GMenuItem(GMenuItem.Type.Menu);
                        item.setCaption(category);
                        _addItemGroupAndDivider(currentMenu, item, group);

                        currentMenu.addItem(item);
                    }
                    currentMenu = item.getMenu();
                }
            }

            // Add our action item now
            var actionItem = new GMenuItem();
            actionItem.setAction(action);
            _addItemGroupAndDivider(currentMenu, actionItem, groups ? groups[groups.length - 1] : null);

            currentMenu.addItem(actionItem);
        }
    };


    // -----------------------------------------------------------------------------------------------------------------
    // GMenu.OpenEvent Event
    // -----------------------------------------------------------------------------------------------------------------

    /**
     * An event whenever a (standalone) menu is opened
     * @class GMenu.OpenEvent
     * @extends GEvent
     * @constructor
     * @version 1.0
     */
    GMenu.OpenEvent = function () {
    };
    IFObject.inherit(GMenu.OpenEvent, GEvent);

    /** @override */
    GMenu.OpenEvent.prototype.toString = function () {
        return "[Object GMenu.OpenEvent]";
    };

    GMenu.OPEN_EVENT = new GMenu.OpenEvent();

    // -----------------------------------------------------------------------------------------------------------------
    // GMenu.CloseEvent Event
    // -----------------------------------------------------------------------------------------------------------------

    /**
     * An event whenever a (standalone) menu is closed
     * @class GMenu.CloseEvent
     * @extends GEvent
     * @constructor
     * @version 1.0
     */
    GMenu.CloseEvent = function () {
    };
    IFObject.inherit(GMenu.CloseEvent, GEvent);

    /** @override */
    GMenu.CloseEvent.prototype.toString = function () {
        return "[Object GMenu.CloseEvent]";
    };

    GMenu.CLOSE_EVENT = new GMenu.CloseEvent();

    // -----------------------------------------------------------------------------------------------------------------
    // GMenu Class
    // -----------------------------------------------------------------------------------------------------------------
    /**
     * @type {GMenuItem|GMenuBar}
     * @private
     */
    GMenu.prototype._parent = null;

    /**
     * @type {HTMLDivElement}
     * @private
     */
    GMenu.prototype._htmlElement = null;

    /**
     * @type {Array<GMenuItem>}
     * @private
     */
    GMenu.prototype._items = null;

    /**
     * @type {boolean}
     * @private
     */
    GMenu.prototype._hovered = false;

    /**
     * @returns {GMenuItem|GMenuBar}
     */
    GMenu.prototype.getParent = function () {
        return this._parent;
    };

    /**
     * Checks and returns whether the mouse is currently
     * over this menu including sub menus if desired
     * @param {boolean} recursive if true, considers
     * also any sub-menu hovering as effective, otherwise
     * only takes into account this menu
     */
    GMenu.prototype.isHovered = function (recursive) {
        if (this._hovered) {
            return true;
        }
        else if (!recursive) {
            return false;
        } else {
            for (var i = 0; i < this.getItemCount(); ++i) {
Download .txt
gitextract_2b0_wtzl/

├── .bowerrc
├── .gitignore
├── Gruntfile.js
├── LICENSE
├── README.md
├── assets/
│   └── cursor/
│       ├── cross.cur
│       ├── hand-closed.cur
│       ├── hand-open.cur
│       ├── lasso.cur
│       ├── pen-close.cur
│       ├── pen-drag.cur
│       ├── pen-end.cur
│       ├── pen-minus.cur
│       ├── pen-modify.cur
│       ├── pen-plus.cur
│       ├── pen-start.cur
│       ├── pen.cur
│       ├── pipette.cur
│       ├── select-arrow-only.cur
│       ├── select-cross.cur
│       ├── select-dot-inverse.cur
│       ├── select-dot.cur
│       ├── select-inverse.cur
│       ├── select-resize-horiz.cur
│       ├── select-resize-vert.cur
│       ├── select-rot-bc.cur
│       ├── select-rot-bl.cur
│       ├── select-rot-br.cur
│       ├── select-rot-lc.cur
│       ├── select-rot-rc.cur
│       ├── select-rot-tc.cur
│       ├── select-rot-tl.cur
│       ├── select-rot-tr.cur
│       ├── select-skew-horiz.cur
│       ├── select-skew-vert.cur
│       ├── select-upleft-downright.cur
│       ├── select-upright-downleft.cur
│       ├── select.cur
│       ├── zoom-minus.cur
│       ├── zoom-none.cur
│       └── zoom-plus.cur
├── bower.json
├── package.json
├── shell/
│   ├── browser/
│   │   ├── index.html
│   │   └── shell.js
│   ├── chrome/
│   │   ├── background.js
│   │   ├── filestorage.js
│   │   ├── index.html
│   │   ├── manifest.json
│   │   └── shell.js
│   └── system/
│       ├── Info.plist
│       ├── appicon.icns
│       ├── doc.icns
│       ├── filestorage.js
│       ├── index.html
│       ├── package/
│       │   └── osx/
│       │       ├── background.tiff
│       │       └── dmg.json
│       ├── package.json
│       ├── shell.js
│       └── winstate.js
├── src/
│   ├── application/
│   │   ├── application.js
│   │   ├── bootstrap.js
│   │   ├── component/
│   │   │   ├── autoedit.js
│   │   │   ├── blendmode.js
│   │   │   ├── colorbutton.js
│   │   │   ├── colorpanel.js
│   │   │   ├── cornertype.js
│   │   │   ├── gradienteditor.js
│   │   │   ├── menu.js
│   │   │   ├── menubar.js
│   │   │   ├── menubutton.js
│   │   │   ├── menuitem.js
│   │   │   ├── overlay.js
│   │   │   ├── panel.js
│   │   │   ├── patterntarget.js
│   │   │   ├── pivot.js
│   │   │   ├── stylepanel.js
│   │   │   ├── swatchpanel.js
│   │   │   └── unit.js
│   │   ├── document.js
│   │   ├── extension/
│   │   │   ├── action.js
│   │   │   ├── colormatcher.js
│   │   │   ├── exporter.js
│   │   │   ├── module.js
│   │   │   ├── palette.js
│   │   │   ├── panel.js
│   │   │   ├── properties.js
│   │   │   ├── sidebar.js
│   │   │   ├── storage.js
│   │   │   ├── styleentry.js
│   │   │   ├── transformer.js
│   │   │   └── view.js
│   │   ├── i18n/
│   │   │   ├── i18n_de.js
│   │   │   └── i18n_en.js
│   │   ├── shell.js
│   │   ├── util/
│   │   │   ├── ciede2000.js
│   │   │   ├── image.js
│   │   │   └── selectors.js
│   │   └── workspace/
│   │       ├── header.js
│   │       ├── palettes.js
│   │       ├── panels.js
│   │       ├── sidebars.js
│   │       ├── toolbar.js
│   │       ├── window.js
│   │       └── windows.js
│   ├── development/
│   │   ├── bootstrap.js
│   │   ├── development.js
│   │   ├── test/
│   │   │   ├── clone_scene.js
│   │   │   ├── create_multiple_pages.js
│   │   │   ├── create_rect_grid_page.js
│   │   │   ├── deserialize_scene.js
│   │   │   └── serialize_scene.js
│   │   └── testaction.js
│   ├── gravit/
│   │   ├── action/
│   │   │   ├── addlayeraction.js
│   │   │   ├── addpageaction.js
│   │   │   ├── alignaction.js
│   │   │   ├── arrangeaction.js
│   │   │   ├── cloneaction.js
│   │   │   ├── closeaction.js
│   │   │   ├── closeallaction.js
│   │   │   ├── copyaction.js
│   │   │   ├── copyattributesaction.js
│   │   │   ├── cutaction.js
│   │   │   ├── deleteaction.js
│   │   │   ├── deletelayeraction.js
│   │   │   ├── deletepageaction.js
│   │   │   ├── distributeaction.js
│   │   │   ├── duplicateaction.js
│   │   │   ├── fitallaction.js
│   │   │   ├── fitcurrentlayeraction.js
│   │   │   ├── fitcurrentpageaction.js
│   │   │   ├── fitselectionaction.js
│   │   │   ├── groupaction.js
│   │   │   ├── invertselectionaction.js
│   │   │   ├── layertypeaction.js
│   │   │   ├── magnificationaction.js
│   │   │   ├── newaction.js
│   │   │   ├── newwindowaction.js
│   │   │   ├── openaction.js
│   │   │   ├── originalviewaction.js
│   │   │   ├── paintmodeaction.js
│   │   │   ├── pasteaction.js
│   │   │   ├── pasteattributesaction.js
│   │   │   ├── pasteinplaceaction.js
│   │   │   ├── pasteinsideaction.js
│   │   │   ├── pixelpreviewaction.js
│   │   │   ├── placeimageaction.js
│   │   │   ├── redoaction.js
│   │   │   ├── saveaction.js
│   │   │   ├── saveallaction.js
│   │   │   ├── saveasaction.js
│   │   │   ├── selectallaction.js
│   │   │   ├── showallpagesaction.js
│   │   │   ├── showgridaction.js
│   │   │   ├── showrulersaction.js
│   │   │   ├── slicefromselection.js
│   │   │   ├── snapunitaction.js
│   │   │   ├── transformaction.js
│   │   │   ├── undoaction.js
│   │   │   ├── ungroupaction.js
│   │   │   ├── zoominaction.js
│   │   │   └── zoomoutaction.js
│   │   ├── colormatcher/
│   │   │   ├── analogousmatcher.js
│   │   │   └── complementarymatcher.js
│   │   ├── exporter/
│   │   │   └── imagexporter.js
│   │   ├── gravit.js
│   │   ├── i18n/
│   │   │   ├── i18n_de.js
│   │   │   └── i18n_en.js
│   │   ├── palette/
│   │   │   ├── exportpalette.js
│   │   │   └── stylepalette.js
│   │   ├── panel/
│   │   │   ├── propertiespanel.js
│   │   │   └── transformpanel.js
│   │   ├── properties/
│   │   │   ├── documentproperties.js
│   │   │   ├── ellipseproperties.js
│   │   │   ├── imageproperties.js
│   │   │   ├── infoproperties.js
│   │   │   ├── pageproperties.js
│   │   │   ├── pathproperties.js
│   │   │   ├── polygonproperties.js
│   │   │   ├── rectangleproperties.js
│   │   │   ├── sliceproperties.js
│   │   │   └── textproperties.js
│   │   ├── sidebar/
│   │   │   ├── pageslayerssidebar.js
│   │   │   └── stylesswatchessidebar.js
│   │   ├── styleentry/
│   │   │   ├── areapaintentry.js
│   │   │   ├── blurfilterentry.js
│   │   │   ├── fillpaintentry.js
│   │   │   ├── offsetveffectentry.js
│   │   │   ├── patternpaintentry.js
│   │   │   ├── shadoweffectentry.js
│   │   │   └── strokepaintentry.js
│   │   └── transformer/
│   │       ├── adjusttransformer.js
│   │       └── aligntransformer.js
│   ├── index.html
│   ├── infinity/
│   │   ├── core/
│   │   │   ├── cursor.js
│   │   │   ├── key.js
│   │   │   ├── locale.js
│   │   │   ├── math.js
│   │   │   ├── object.js
│   │   │   ├── system.js
│   │   │   └── util.js
│   │   ├── event/
│   │   │   ├── event.js
│   │   │   ├── eventtarget.js
│   │   │   ├── inputevent.js
│   │   │   ├── keyevent.js
│   │   │   └── mouseevent.js
│   │   ├── geometry/
│   │   │   ├── length.js
│   │   │   ├── point.js
│   │   │   ├── rect.js
│   │   │   └── transform.js
│   │   ├── i18n/
│   │   │   ├── i18n_de.js
│   │   │   └── i18n_en.js
│   │   ├── paint/
│   │   │   ├── annotation.js
│   │   │   ├── bitmap.js
│   │   │   ├── color.js
│   │   │   ├── colorprofile.js
│   │   │   ├── colorspace.js
│   │   │   ├── dirtylist.js
│   │   │   ├── font.js
│   │   │   ├── gradient.js
│   │   │   ├── paintcanvas.js
│   │   │   ├── paintconfiguration.js
│   │   │   ├── paintcontext.js
│   │   │   └── pattern.js
│   │   ├── platform.js
│   │   ├── scene/
│   │   │   ├── block.js
│   │   │   ├── element.js
│   │   │   ├── item.js
│   │   │   ├── node.js
│   │   │   ├── scene.js
│   │   │   ├── scenepaintconfiguration.js
│   │   │   ├── selector.js
│   │   │   ├── shape/
│   │   │   │   ├── ellipse.js
│   │   │   │   ├── image.js
│   │   │   │   ├── path.js
│   │   │   │   ├── pathbase.js
│   │   │   │   ├── polygon.js
│   │   │   │   ├── rectangle.js
│   │   │   │   ├── shape.js
│   │   │   │   ├── shapeset.js
│   │   │   │   └── text.js
│   │   │   ├── structure/
│   │   │   │   ├── layer.js
│   │   │   │   ├── page.js
│   │   │   │   ├── slice.js
│   │   │   │   └── swatch.js
│   │   │   └── style/
│   │   │       ├── appliedstyle.js
│   │   │       ├── effect/
│   │   │       │   └── shadoweffect.js
│   │   │       ├── effectentry.js
│   │   │       ├── filter/
│   │   │       │   └── blurfilter.js
│   │   │       ├── filterentry.js
│   │   │       ├── inlinestyle.js
│   │   │       ├── linkedstyle.js
│   │   │       ├── paint/
│   │   │       │   ├── areapaint.js
│   │   │       │   ├── fillpaint.js
│   │   │       │   ├── patternpaint.js
│   │   │       │   └── strokepaint.js
│   │   │       ├── paintentry.js
│   │   │       ├── sharedstyle.js
│   │   │       ├── style.js
│   │   │       ├── styleentry.js
│   │   │       ├── styleset.js
│   │   │       ├── veffect/
│   │   │       │   └── offsetveffect.js
│   │   │       └── veffectentry.js
│   │   ├── vertex/
│   │   │   ├── vertex.js
│   │   │   ├── vertexcontainer.js
│   │   │   ├── vertexinfo.js
│   │   │   ├── vertexoffsetter.js
│   │   │   ├── vertexpixelaligner.js
│   │   │   ├── vertexsource.js
│   │   │   ├── vertextarget.js
│   │   │   └── vertextransformer.js
│   │   └── view/
│   │       ├── scenestage.js
│   │       ├── stage.js
│   │       ├── view.js
│   │       └── widget.js
│   ├── infinity-editor/
│   │   ├── editor.js
│   │   ├── editorpaintconfiguration.js
│   │   ├── guide/
│   │   │   ├── gridguide.js
│   │   │   ├── guide.js
│   │   │   ├── guides.js
│   │   │   ├── pageguide.js
│   │   │   ├── shapeboxguide.js
│   │   │   └── unitguide.js
│   │   ├── i18n/
│   │   │   ├── i18n_de.js
│   │   │   └── i18n_en.js
│   │   ├── scene/
│   │   │   ├── blockeditor.js
│   │   │   ├── elementeditor.js
│   │   │   ├── sceneeditor.js
│   │   │   ├── shape/
│   │   │   │   ├── ellipseeditor.js
│   │   │   │   ├── imageeditor.js
│   │   │   │   ├── pathbaseeditor.js
│   │   │   │   ├── patheditor.js
│   │   │   │   ├── polygoneditor.js
│   │   │   │   ├── rectangleeditor.js
│   │   │   │   ├── shapeeditor.js
│   │   │   │   ├── shapeseteditor.js
│   │   │   │   └── texteditor.js
│   │   │   ├── structure/
│   │   │   │   ├── layereditor.js
│   │   │   │   ├── pageeditor.js
│   │   │   │   └── sliceeditor.js
│   │   │   └── transformbox.js
│   │   ├── tool/
│   │   │   ├── bezigontool.js
│   │   │   ├── ellipsetool.js
│   │   │   ├── handtool.js
│   │   │   ├── lassotool.js
│   │   │   ├── layertool.js
│   │   │   ├── linetool.js
│   │   │   ├── marqueetool.js
│   │   │   ├── pagetool.js
│   │   │   ├── pathtool.js
│   │   │   ├── pentool.js
│   │   │   ├── pointertool.js
│   │   │   ├── polygontool.js
│   │   │   ├── rectangletool.js
│   │   │   ├── selecttool.js
│   │   │   ├── shapetool.js
│   │   │   ├── slicetool.js
│   │   │   ├── subselecttool.js
│   │   │   ├── texttool.js
│   │   │   ├── tool.js
│   │   │   ├── toolmanager.js
│   │   │   ├── transformtool.js
│   │   │   └── zoomtool.js
│   │   └── view/
│   │       ├── editorbackstage.js
│   │       ├── editorfrontstage.js
│   │       ├── editorscenestage.js
│   │       ├── editortoolstage.js
│   │       └── editorview.js
│   └── package.json
├── style/
│   ├── _base.scss
│   ├── _contenteditable.scss
│   ├── _cursors.scss
│   ├── _fonts.scss
│   ├── _input.scss
│   ├── _variables.scss
│   ├── component/
│   │   ├── _colorpanel.scss
│   │   ├── _colorswatch.scss
│   │   ├── _form.scss
│   │   ├── _gradienteditor.scss
│   │   ├── _menu.scss
│   │   ├── _overlay.scss
│   │   ├── _panel.scss
│   │   ├── _pivot.scss
│   │   ├── _stylepanel.scss
│   │   ├── _swatchpanel.scss
│   │   └── _tree.scss
│   ├── gravit.scss
│   ├── module/
│   │   ├── palette/
│   │   │   ├── _export.scss
│   │   │   └── _style.scss
│   │   ├── panel/
│   │   │   ├── _properties.scss
│   │   │   └── _transform.scss
│   │   └── sidebar/
│   │       └── _pageslayerssidebar.scss
│   └── workspace/
│       ├── _header.scss
│       ├── _palettes.scss
│       ├── _panels.scss
│       ├── _sidebars.scss
│       ├── _toolbar.scss
│       ├── _windows.scss
│       └── _workspace.scss
└── test/
    ├── index.html
    ├── lib/
    │   ├── chai.js
    │   ├── expect.js
    │   └── mocha/
    │       ├── mocha.css
    │       └── mocha.js
    └── spec/
        ├── infinity/
        │   └── pathmodel.js
        └── test.js
Download .txt
SYMBOL INDEX (444 symbols across 248 files)

FILE: shell/browser/shell.js
  function GBrowserShell (line 8) | function GBrowserShell() {

FILE: shell/chrome/filestorage.js
  function GFileStorage (line 6) | function GFileStorage() {

FILE: shell/chrome/shell.js
  function GChromeShell (line 8) | function GChromeShell() {

FILE: shell/system/filestorage.js
  function GFileStorage (line 8) | function GFileStorage() {

FILE: shell/system/shell.js
  function GSystemShell (line 16) | function GSystemShell() {

FILE: shell/system/winstate.js
  function initWindowState (line 37) | function initWindowState() {
  function dumpWindowState (line 54) | function dumpWindowState() {
  function restoreWindowState (line 81) | function restoreWindowState() {
  function saveWindowState (line 93) | function saveWindowState() {

FILE: src/application/application.js
  function GApplication (line 9) | function GApplication() {
  function dispatchEventFromTouch (line 1117) | function dispatchEventFromTouch(eventType, touch) {

FILE: src/application/bootstrap.js
  function gShellReady (line 78) | function gShellReady() {
  function gShellFinished (line 82) | function gShellFinished() {

FILE: src/application/component/colorbutton.js
  function getColorPanel (line 5) | function getColorPanel() {

FILE: src/application/component/colorpanel.js
  function createPaletteView (line 229) | function createPaletteView($this) {
  function createSwatchesView (line 258) | function createSwatchesView($this) {
  function createTrendsView (line 319) | function createTrendsView($this) {
  function colorForTrendAndValue (line 385) | function colorForTrendAndValue($this, trend, value) {
  function updateTrendValue (line 410) | function updateTrendValue($this, trend, value) {
  function updateTrends (line 439) | function updateTrends($this) {
  function cvColorThiefColor (line 462) | function cvColorThiefColor(color) {
  function createImageView (line 466) | function createImageView($this) {
  function createView (line 542) | function createView($this, viewType) {
  function updateMatches (line 565) | function updateMatches($this) {
  function activateMatcher (line 595) | function activateMatcher($this, matcher) {
  function activateColorMode (line 602) | function activateColorMode($this, colorMode) {
  function updateFromComponents (line 644) | function updateFromComponents($this) {
  function updateToComponents (line 675) | function updateToComponents($this) {
  function assignValue (line 728) | function assignValue($this, value, overwritePrevious) {

FILE: src/application/component/gradienteditor.js
  function updateStop (line 3) | function updateStop($this, $stop) {
  function updateGradient (line 11) | function updateGradient($this) {

FILE: src/application/component/menu.js
  function GMenu (line 10) | function GMenu(parent) {

FILE: src/application/component/menubar.js
  function GMenuBar (line 9) | function GMenuBar(menu) {

FILE: src/application/component/menuitem.js
  function GMenuItem (line 12) | function GMenuItem(type) {

FILE: src/application/component/stylepanel.js
  function updateSelectedStyle (line 3) | function updateSelectedStyle($this, style) {
  function updatePlaceholder (line 11) | function updatePlaceholder($this) {
  function afterInsertEvent (line 32) | function afterInsertEvent(evt) {
  function beforeRemoveEvent (line 40) | function beforeRemoveEvent(evt) {
  function afterPropertiesChangeEvent (line 48) | function afterPropertiesChangeEvent(evt) {
  function styleChangeEvent (line 56) | function styleChangeEvent(evt) {
  function canDrop (line 67) | function canDrop($this, target) {

FILE: src/application/component/swatchpanel.js
  function updateSelectedSwatch (line 3) | function updateSelectedSwatch($this, swatch) {
  function updatePlaceholder (line 13) | function updatePlaceholder($this) {
  function afterInsertEvent (line 34) | function afterInsertEvent(evt) {
  function beforeRemoveEvent (line 42) | function beforeRemoveEvent(evt) {
  function afterPropertiesChangeEvent (line 50) | function afterPropertiesChangeEvent(evt) {
  function canDrop (line 61) | function canDrop($this, target) {

FILE: src/application/document.js
  function GDocument (line 8) | function GDocument(scene, url, title) {

FILE: src/application/extension/action.js
  function GAction (line 9) | function GAction() {

FILE: src/application/extension/colormatcher.js
  function GColorMatcher (line 8) | function GColorMatcher() {

FILE: src/application/extension/exporter.js
  function GExporter (line 7) | function GExporter() {

FILE: src/application/extension/module.js
  function GModule (line 8) | function GModule() {

FILE: src/application/extension/palette.js
  function GPalette (line 9) | function GPalette() {

FILE: src/application/extension/panel.js
  function GPanel (line 9) | function GPanel() {

FILE: src/application/extension/properties.js
  function GProperties (line 9) | function GProperties() {

FILE: src/application/extension/sidebar.js
  function GSidebar (line 9) | function GSidebar() {

FILE: src/application/extension/storage.js
  function GStorage (line 6) | function GStorage() {

FILE: src/application/extension/styleentry.js
  function GStyleEntry (line 8) | function GStyleEntry() {

FILE: src/application/extension/transformer.js
  function GTransformer (line 9) | function GTransformer() {

FILE: src/application/extension/view.js
  function GView (line 9) | function GView() {

FILE: src/application/shell.js
  function GShell (line 8) | function GShell() {

FILE: src/application/util/ciede2000.js
  function ciede2000 (line 53) | function ciede2000(c1, c2) {
  function degrees (line 154) | function degrees(n) {
  function radians (line 158) | function radians(n) {

FILE: src/application/workspace/header.js
  function GHeader (line 8) | function GHeader(htmlElement) {

FILE: src/application/workspace/palettes.js
  function GPalettes (line 7) | function GPalettes(htmlElement) {

FILE: src/application/workspace/panels.js
  function GPanels (line 7) | function GPanels(htmlElement) {

FILE: src/application/workspace/sidebars.js
  function GSidebars (line 7) | function GSidebars(htmlElement) {

FILE: src/application/workspace/toolbar.js
  function GToolbar (line 7) | function GToolbar(htmlElement) {

FILE: src/application/workspace/window.js
  function GWindow (line 9) | function GWindow(document) {

FILE: src/application/workspace/windows.js
  function GWindows (line 8) | function GWindows(htmlElement) {

FILE: src/development/development.js
  function GDevelopmentModule (line 8) | function GDevelopmentModule() {

FILE: src/development/testaction.js
  function TestAction (line 2) | function TestAction(test) {

FILE: src/gravit/action/addlayeraction.js
  function GAddLayerAction (line 9) | function GAddLayerAction() {

FILE: src/gravit/action/addpageaction.js
  function GAddPageAction (line 9) | function GAddPageAction() {

FILE: src/gravit/action/alignaction.js
  function GAlignAction (line 9) | function GAlignAction(type) {

FILE: src/gravit/action/arrangeaction.js
  function GArrangeAction (line 9) | function GArrangeAction(type) {

FILE: src/gravit/action/cloneaction.js
  function GCloneAction (line 9) | function GCloneAction() {

FILE: src/gravit/action/closeaction.js
  function GCloseAction (line 9) | function GCloseAction() {

FILE: src/gravit/action/closeallaction.js
  function GCloseAllAction (line 9) | function GCloseAllAction() {

FILE: src/gravit/action/copyaction.js
  function GCopyAction (line 9) | function GCopyAction() {

FILE: src/gravit/action/copyattributesaction.js
  function GCopyAttributesAction (line 9) | function GCopyAttributesAction() {

FILE: src/gravit/action/cutaction.js
  function GCutAction (line 9) | function GCutAction() {

FILE: src/gravit/action/deleteaction.js
  function GDeleteAction (line 9) | function GDeleteAction() {

FILE: src/gravit/action/deletelayeraction.js
  function GDeleteLayerAction (line 9) | function GDeleteLayerAction() {

FILE: src/gravit/action/deletepageaction.js
  function GDeletePageAction (line 9) | function GDeletePageAction() {

FILE: src/gravit/action/distributeaction.js
  function GDistributeAction (line 9) | function GDistributeAction(type) {

FILE: src/gravit/action/duplicateaction.js
  function GDuplicateAction (line 9) | function GDuplicateAction() {

FILE: src/gravit/action/fitallaction.js
  function GFitAllAction (line 9) | function GFitAllAction() {

FILE: src/gravit/action/fitcurrentlayeraction.js
  function GFitCurrentLayerAction (line 9) | function GFitCurrentLayerAction() {

FILE: src/gravit/action/fitcurrentpageaction.js
  function GFitCurrentPageAction (line 9) | function GFitCurrentPageAction() {

FILE: src/gravit/action/fitselectionaction.js
  function GFitSelectionAction (line 9) | function GFitSelectionAction() {

FILE: src/gravit/action/groupaction.js
  function GGroupAction (line 9) | function GGroupAction() {

FILE: src/gravit/action/invertselectionaction.js
  function GInvertSelectionAction (line 9) | function GInvertSelectionAction() {

FILE: src/gravit/action/layertypeaction.js
  function GLayerTypeAction (line 9) | function GLayerTypeAction(layerType) {

FILE: src/gravit/action/magnificationaction.js
  function GMagnificationAction (line 9) | function GMagnificationAction(magnification, shortcut) {

FILE: src/gravit/action/newaction.js
  function GNewAction (line 9) | function GNewAction() {

FILE: src/gravit/action/newwindowaction.js
  function GNewWindowAction (line 9) | function GNewWindowAction() {

FILE: src/gravit/action/openaction.js
  function GOpenAction (line 9) | function GOpenAction() {

FILE: src/gravit/action/originalviewaction.js
  function GOriginalViewAction (line 9) | function GOriginalViewAction() {

FILE: src/gravit/action/paintmodeaction.js
  function GPaintModeAction (line 9) | function GPaintModeAction(paintMode) {

FILE: src/gravit/action/pasteaction.js
  function GPasteAction (line 9) | function GPasteAction() {

FILE: src/gravit/action/pasteattributesaction.js
  function GPasteAttributesAction (line 9) | function GPasteAttributesAction() {

FILE: src/gravit/action/pasteinplaceaction.js
  function GPasteInPlaceAction (line 9) | function GPasteInPlaceAction() {

FILE: src/gravit/action/pasteinsideaction.js
  function GPasteInsideAction (line 9) | function GPasteInsideAction() {

FILE: src/gravit/action/pixelpreviewaction.js
  function GPixelPreviewAction (line 9) | function GPixelPreviewAction() {

FILE: src/gravit/action/placeimageaction.js
  function GPlaceImageAction (line 9) | function GPlaceImageAction() {

FILE: src/gravit/action/redoaction.js
  function GRedoAction (line 9) | function GRedoAction() {

FILE: src/gravit/action/saveaction.js
  function GSaveAction (line 9) | function GSaveAction() {

FILE: src/gravit/action/saveallaction.js
  function GSaveAllAction (line 9) | function GSaveAllAction() {

FILE: src/gravit/action/saveasaction.js
  function GSaveAsAction (line 9) | function GSaveAsAction() {

FILE: src/gravit/action/selectallaction.js
  function GSelectAllAction (line 9) | function GSelectAllAction() {

FILE: src/gravit/action/showallpagesaction.js
  function GShowAllPagesAction (line 9) | function GShowAllPagesAction() {

FILE: src/gravit/action/showgridaction.js
  function GShowGridAction (line 9) | function GShowGridAction() {

FILE: src/gravit/action/showrulersaction.js
  function GShowRulersAction (line 9) | function GShowRulersAction() {

FILE: src/gravit/action/slicefromselection.js
  function GSliceFromSelectionAction (line 9) | function GSliceFromSelectionAction() {

FILE: src/gravit/action/snapunitaction.js
  function GSnapUnitAction (line 9) | function GSnapUnitAction(type) {

FILE: src/gravit/action/transformaction.js
  function GTransformAction (line 9) | function GTransformAction(type) {

FILE: src/gravit/action/undoaction.js
  function GUndoAction (line 9) | function GUndoAction() {

FILE: src/gravit/action/ungroupaction.js
  function GUngroupAction (line 9) | function GUngroupAction() {

FILE: src/gravit/action/zoominaction.js
  function GZoomInAction (line 9) | function GZoomInAction() {

FILE: src/gravit/action/zoomoutaction.js
  function GZoomOutAction (line 9) | function GZoomOutAction() {

FILE: src/gravit/colormatcher/analogousmatcher.js
  function GAnalogousMatcher (line 9) | function GAnalogousMatcher() {

FILE: src/gravit/colormatcher/complementarymatcher.js
  function GComplementaryMatcher (line 9) | function GComplementaryMatcher() {

FILE: src/gravit/exporter/imagexporter.js
  function GImageExporter (line 8) | function GImageExporter() {

FILE: src/gravit/gravit.js
  function GravitModule (line 8) | function GravitModule() {

FILE: src/gravit/palette/exportpalette.js
  function GExportPalette (line 9) | function GExportPalette() {

FILE: src/gravit/palette/stylepalette.js
  function GStylePalette (line 9) | function GStylePalette() {

FILE: src/gravit/panel/propertiespanel.js
  function GPropertiesPanel (line 9) | function GPropertiesPanel() {

FILE: src/gravit/panel/transformpanel.js
  function GTransformPanel (line 9) | function GTransformPanel() {

FILE: src/gravit/properties/documentproperties.js
  function GDocumentProperties (line 9) | function GDocumentProperties() {

FILE: src/gravit/properties/ellipseproperties.js
  function GEllipseProperties (line 9) | function GEllipseProperties() {

FILE: src/gravit/properties/imageproperties.js
  function GImageProperties (line 9) | function GImageProperties() {

FILE: src/gravit/properties/infoproperties.js
  function GInfoProperties (line 9) | function GInfoProperties() {

FILE: src/gravit/properties/pageproperties.js
  function GPageProperties (line 9) | function GPageProperties() {

FILE: src/gravit/properties/pathproperties.js
  function GPathProperties (line 9) | function GPathProperties() {

FILE: src/gravit/properties/polygonproperties.js
  function GPolygonProperties (line 9) | function GPolygonProperties() {

FILE: src/gravit/properties/rectangleproperties.js
  function GRectangleProperties (line 9) | function GRectangleProperties() {

FILE: src/gravit/properties/sliceproperties.js
  function GSliceProperties (line 9) | function GSliceProperties() {

FILE: src/gravit/properties/textproperties.js
  function GTextProperties (line 9) | function GTextProperties() {

FILE: src/gravit/sidebar/pageslayerssidebar.js
  function canDropPage (line 5) | function canDropPage(target) {
  function GPagesLayersSidebar (line 23) | function GPagesLayersSidebar() {

FILE: src/gravit/sidebar/stylesswatchessidebar.js
  function GStylesSwatchesSidebar (line 9) | function GStylesSwatchesSidebar() {

FILE: src/gravit/styleentry/areapaintentry.js
  function GAreaPaintEntry (line 9) | function GAreaPaintEntry() {

FILE: src/gravit/styleentry/blurfilterentry.js
  function GBlurFilterEntry (line 9) | function GBlurFilterEntry() {

FILE: src/gravit/styleentry/fillpaintentry.js
  function GFillPaintEntry (line 9) | function GFillPaintEntry() {

FILE: src/gravit/styleentry/offsetveffectentry.js
  function GOffsetVEffectEntry (line 9) | function GOffsetVEffectEntry() {

FILE: src/gravit/styleentry/patternpaintentry.js
  function GPatternPaintEntry (line 9) | function GPatternPaintEntry() {

FILE: src/gravit/styleentry/shadoweffectentry.js
  function GShadowEffectEntry (line 9) | function GShadowEffectEntry() {

FILE: src/gravit/styleentry/strokepaintentry.js
  function GStrokePaintEntry (line 9) | function GStrokePaintEntry() {

FILE: src/gravit/transformer/adjusttransformer.js
  function GAdjustTransformer (line 9) | function GAdjustTransformer() {

FILE: src/gravit/transformer/aligntransformer.js
  function GAlignTransformer (line 9) | function GAlignTransformer() {

FILE: src/infinity-editor/editor.js
  function IFEditor (line 10) | function IFEditor(scene) {

FILE: src/infinity-editor/editorpaintconfiguration.js
  function IFEditorPaintConfiguration (line 8) | function IFEditorPaintConfiguration() {

FILE: src/infinity-editor/guide/gridguide.js
  function IFGridGuide (line 11) | function IFGridGuide(guides) {

FILE: src/infinity-editor/guide/guide.js
  function IFGuide (line 8) | function IFGuide(guides) {

FILE: src/infinity-editor/guide/guides.js
  function IFGuides (line 9) | function IFGuides(scene) {

FILE: src/infinity-editor/guide/pageguide.js
  function IFPageGuide (line 11) | function IFPageGuide(guides) {

FILE: src/infinity-editor/guide/shapeboxguide.js
  function IFShapeBoxGuide (line 11) | function IFShapeBoxGuide(guides) {

FILE: src/infinity-editor/guide/unitguide.js
  function IFUnitGuide (line 10) | function IFUnitGuide(guides) {

FILE: src/infinity-editor/scene/blockeditor.js
  function IFBlockEditor (line 9) | function IFBlockEditor(block) {

FILE: src/infinity-editor/scene/elementeditor.js
  function IFElementEditor (line 9) | function IFElementEditor(element) {

FILE: src/infinity-editor/scene/sceneeditor.js
  function IFSceneEditor (line 9) | function IFSceneEditor(scene) {

FILE: src/infinity-editor/scene/shape/ellipseeditor.js
  function IFEllipseEditor (line 9) | function IFEllipseEditor(ellipse) {

FILE: src/infinity-editor/scene/shape/imageeditor.js
  function IFImageEditor (line 9) | function IFImageEditor(image) {

FILE: src/infinity-editor/scene/shape/pathbaseeditor.js
  function IFPathBaseEditor (line 9) | function IFPathBaseEditor(path) {

FILE: src/infinity-editor/scene/shape/patheditor.js
  function IFPathEditor (line 9) | function IFPathEditor(path) {

FILE: src/infinity-editor/scene/shape/polygoneditor.js
  function IFPolygonEditor (line 9) | function IFPolygonEditor(polygon) {

FILE: src/infinity-editor/scene/shape/rectangleeditor.js
  function IFRectangleEditor (line 9) | function IFRectangleEditor(rectangle) {

FILE: src/infinity-editor/scene/shape/shapeeditor.js
  function IFShapeEditor (line 8) | function IFShapeEditor(shape) {

FILE: src/infinity-editor/scene/shape/shapeseteditor.js
  function IFShapeSetEditor (line 9) | function IFShapeSetEditor(set) {

FILE: src/infinity-editor/scene/shape/texteditor.js
  function IFTextEditor (line 9) | function IFTextEditor(rectangle) {

FILE: src/infinity-editor/scene/structure/layereditor.js
  function IFLayerEditor (line 9) | function IFLayerEditor(layer) {

FILE: src/infinity-editor/scene/structure/pageeditor.js
  function IFPageEditor (line 9) | function IFPageEditor(group) {

FILE: src/infinity-editor/scene/structure/sliceeditor.js
  function IFSliceEditor (line 9) | function IFSliceEditor(slice) {

FILE: src/infinity-editor/scene/transformbox.js
  function IFTransformBox (line 14) | function IFTransformBox(bbox, cx, cy) {

FILE: src/infinity-editor/tool/bezigontool.js
  function IFBezigonTool (line 9) | function IFBezigonTool() {

FILE: src/infinity-editor/tool/ellipsetool.js
  function IFEllipseTool (line 8) | function IFEllipseTool() {

FILE: src/infinity-editor/tool/handtool.js
  function IFHandTool (line 9) | function IFHandTool() {

FILE: src/infinity-editor/tool/lassotool.js
  function IFLassoTool (line 8) | function IFLassoTool() {

FILE: src/infinity-editor/tool/layertool.js
  function IFLayerTool (line 8) | function IFLayerTool() {

FILE: src/infinity-editor/tool/linetool.js
  function IFLineTool (line 8) | function IFLineTool() {

FILE: src/infinity-editor/tool/marqueetool.js
  function IFMarqueeTool (line 9) | function IFMarqueeTool(areaSelector) {

FILE: src/infinity-editor/tool/pagetool.js
  function IFPageTool (line 9) | function IFPageTool() {

FILE: src/infinity-editor/tool/pathtool.js
  function IFPathTool (line 9) | function IFPathTool() {

FILE: src/infinity-editor/tool/pentool.js
  function IFPenTool (line 9) | function IFPenTool() {

FILE: src/infinity-editor/tool/pointertool.js
  function IFPointerTool (line 9) | function IFPointerTool() {

FILE: src/infinity-editor/tool/polygontool.js
  function IFPolygonTool (line 9) | function IFPolygonTool() {

FILE: src/infinity-editor/tool/rectangletool.js
  function IFRectangleTool (line 8) | function IFRectangleTool() {

FILE: src/infinity-editor/tool/selecttool.js
  function IFSelectTool (line 9) | function IFSelectTool() {

FILE: src/infinity-editor/tool/shapetool.js
  function IFShapeTool (line 13) | function IFShapeTool(keepRatio, fromCenter) {

FILE: src/infinity-editor/tool/slicetool.js
  function IFSliceTool (line 8) | function IFSliceTool() {

FILE: src/infinity-editor/tool/subselecttool.js
  function IFSubSelectTool (line 9) | function IFSubSelectTool() {

FILE: src/infinity-editor/tool/texttool.js
  function IFTextTool (line 8) | function IFTextTool() {

FILE: src/infinity-editor/tool/tool.js
  function IFTool (line 9) | function IFTool() {

FILE: src/infinity-editor/tool/toolmanager.js
  function IFToolManager (line 10) | function IFToolManager() {

FILE: src/infinity-editor/tool/transformtool.js
  function IFTransformTool (line 9) | function IFTransformTool() {

FILE: src/infinity-editor/tool/zoomtool.js
  function IFZoomTool (line 9) | function IFZoomTool() {

FILE: src/infinity-editor/view/editorbackstage.js
  function IFEditorBackStage (line 11) | function IFEditorBackStage(view) {

FILE: src/infinity-editor/view/editorfrontstage.js
  function IFEditorFrontStage (line 9) | function IFEditorFrontStage(view) {

FILE: src/infinity-editor/view/editorscenestage.js
  function IFEditorSceneStage (line 9) | function IFEditorSceneStage(view) {

FILE: src/infinity-editor/view/editortoolstage.js
  function IFEditorToolStage (line 9) | function IFEditorToolStage(view) {

FILE: src/infinity-editor/view/editorview.js
  function IFEditorView (line 10) | function IFEditorView(editor) {

FILE: src/infinity/core/key.js
  function IFKey (line 8) | function IFKey() {

FILE: src/infinity/core/locale.js
  function IFLocale (line 9) | function IFLocale() {

FILE: src/infinity/core/math.js
  function IFMath (line 8) | function IFMath() {

FILE: src/infinity/core/object.js
  function IFObject (line 8) | function IFObject() {

FILE: src/infinity/core/system.js
  function IFSystem (line 8) | function IFSystem() {

FILE: src/infinity/core/util.js
  function IFUtil (line 8) | function IFUtil() {

FILE: src/infinity/event/event.js
  function GEvent (line 9) | function GEvent() {

FILE: src/infinity/event/eventtarget.js
  function GEventTarget (line 9) | function GEventTarget() {

FILE: src/infinity/event/inputevent.js
  function GUIInputEvent (line 9) | function GUIInputEvent() {

FILE: src/infinity/event/keyevent.js
  function GUIKeyEvent (line 9) | function GUIKeyEvent() {

FILE: src/infinity/event/mouseevent.js
  function GUIMouseEvent (line 9) | function GUIMouseEvent() {

FILE: src/infinity/geometry/length.js
  function IFLength (line 10) | function IFLength(value, unit) {
  function peekNextChar (line 252) | function peekNextChar() {
  function getNextChar (line 257) | function getNextChar() {
  function isWhiteSpace (line 267) | function isWhiteSpace(ch) {
  function isLetter (line 271) | function isLetter(ch) {
  function isDecimalDigit (line 275) | function isDecimalDigit(ch) {
  function createToken (line 279) | function createToken(unit, value) {
  function skipSpaces (line 288) | function skipSpaces() {
  function scanOperator (line 300) | function scanOperator() {
  function isIdentifierStart (line 308) | function isIdentifierStart(ch) {
  function isIdentifierPart (line 312) | function isIdentifierPart(ch) {
  function scanIdentifier (line 316) | function scanIdentifier() {
  function scanNumber (line 336) | function scanNumber() {
  function reset (line 407) | function reset(str) {
  function next (line 413) | function next() {
  function peek (line 442) | function peek() {
  function matchOp (line 470) | function matchOp(token, op) {
  function parseArgumentList (line 478) | function parseArgumentList() {
  function parseFunctionCall (line 500) | function parseFunctionCall(name) {
  function parsePrimary (line 530) | function parsePrimary() {
  function parseUnary (line 574) | function parseUnary() {
  function parseMultiplicative (line 595) | function parseMultiplicative() {
  function parseAdditive (line 617) | function parseAdditive() {
  function parseAssignment (line 638) | function parseAssignment() {
  function parseExpression (line 661) | function parseExpression() {
  function parse (line 665) | function parse(expression) {
  function exec (line 723) | function exec(node) {
  function evaluate (line 800) | function evaluate(expr) {

FILE: src/infinity/geometry/point.js
  function IFPoint (line 11) | function IFPoint(x, y) {

FILE: src/infinity/geometry/rect.js
  function IFRect (line 13) | function IFRect(x, y, width, height) {

FILE: src/infinity/geometry/transform.js
  function IFTransform (line 10) | function IFTransform(sx, shy, shx, sy, tx, ty) {

FILE: src/infinity/paint/annotation.js
  function IFAnnotation (line 8) | function IFAnnotation() {

FILE: src/infinity/paint/bitmap.js
  function IFBitmap (line 9) | function IFBitmap(sourceOrWidth, height) {
  function BlurStack (line 699) | function BlurStack() {

FILE: src/infinity/paint/color.js
  function IFColor (line 10) | function IFColor(type, value) {
  function clamp_css_byte (line 275) | function clamp_css_byte(i) {  // Clamp to integer 0 .. 255.
  function clamp_css_float (line 280) | function clamp_css_float(f) {  // Clamp to float 0.0 .. 1.0.
  function parse_css_int (line 284) | function parse_css_int(str) {  // int or percentage.
  function parse_css_float (line 290) | function parse_css_float(str) {  // float or percentage.
  function css_hue_to_rgb (line 296) | function css_hue_to_rgb(m1, m2, h) {

FILE: src/infinity/paint/colorprofile.js
  function IFColorProfile (line 7) | function IFColorProfile() {

FILE: src/infinity/paint/dirtylist.js
  function IFDirtyList (line 16) | function IFDirtyList() {

FILE: src/infinity/paint/font.js
  function IFFont (line 7) | function IFFont() {

FILE: src/infinity/paint/gradient.js
  function IFGradient (line 8) | function IFGradient(stops, type) {

FILE: src/infinity/paint/paintcanvas.js
  function IFPaintCanvas (line 8) | function IFPaintCanvas() {
  function createChessboardCanvas (line 165) | function createChessboardCanvas(size, backColor, foreColor) {

FILE: src/infinity/paint/paintconfiguration.js
  function IFPaintConfiguration (line 8) | function IFPaintConfiguration() {

FILE: src/infinity/paint/paintcontext.js
  function IFPaintContext (line 7) | function IFPaintContext() {

FILE: src/infinity/paint/pattern.js
  function IFPattern (line 12) | function IFPattern() {

FILE: src/infinity/platform.js
  function GUIPlatform (line 10) | function GUIPlatform() {

FILE: src/infinity/scene/block.js
  function IFBlock (line 10) | function IFBlock() {

FILE: src/infinity/scene/element.js
  function IFElement (line 9) | function IFElement() {

FILE: src/infinity/scene/item.js
  function IFItem (line 8) | function IFItem() {

FILE: src/infinity/scene/node.js
  function IFNode (line 9) | function IFNode() {

FILE: src/infinity/scene/scene.js
  function IFScene (line 12) | function IFScene() {

FILE: src/infinity/scene/scenepaintconfiguration.js
  function IFScenePaintConfiguration (line 8) | function IFScenePaintConfiguration() {

FILE: src/infinity/scene/selector.js
  function chain (line 329) | function chain(prepend, append, aux, unshift) {

FILE: src/infinity/scene/shape/ellipse.js
  function IFEllipse (line 9) | function IFEllipse() {

FILE: src/infinity/scene/shape/image.js
  function IFImage (line 9) | function IFImage() {

FILE: src/infinity/scene/shape/path.js
  function IFPath (line 9) | function IFPath(closed, evenOdd, anchorPoints) {

FILE: src/infinity/scene/shape/pathbase.js
  function IFPathBase (line 9) | function IFPathBase() {

FILE: src/infinity/scene/shape/polygon.js
  function IFPolygon (line 9) | function IFPolygon() {

FILE: src/infinity/scene/shape/rectangle.js
  function IFRectangle (line 9) | function IFRectangle() {

FILE: src/infinity/scene/shape/shape.js
  function IFShape (line 14) | function IFShape() {

FILE: src/infinity/scene/shape/shapeset.js
  function IFShapeSet (line 10) | function IFShapeSet() {

FILE: src/infinity/scene/shape/text.js
  function IFText (line 8) | function IFText() {

FILE: src/infinity/scene/structure/layer.js
  function IFLayer (line 10) | function IFLayer() {

FILE: src/infinity/scene/structure/page.js
  function IFPage (line 10) | function IFPage() {

FILE: src/infinity/scene/structure/slice.js
  function IFSlice (line 9) | function IFSlice() {

FILE: src/infinity/scene/structure/swatch.js
  function IFSwatch (line 10) | function IFSwatch() {

FILE: src/infinity/scene/style/appliedstyle.js
  function IFAppliedStyle (line 9) | function IFAppliedStyle() {

FILE: src/infinity/scene/style/effect/shadoweffect.js
  function IFShadowEffect (line 9) | function IFShadowEffect() {

FILE: src/infinity/scene/style/effectentry.js
  function IFEffectEntry (line 9) | function IFEffectEntry() {

FILE: src/infinity/scene/style/filter/blurfilter.js
  function IFBlurFilter (line 9) | function IFBlurFilter() {

FILE: src/infinity/scene/style/filterentry.js
  function IFFilterEntry (line 9) | function IFFilterEntry() {

FILE: src/infinity/scene/style/inlinestyle.js
  function IFInlineStyle (line 10) | function IFInlineStyle() {

FILE: src/infinity/scene/style/linkedstyle.js
  function IFLinkedStyle (line 9) | function IFLinkedStyle() {

FILE: src/infinity/scene/style/paint/areapaint.js
  function IFAreaPaint (line 9) | function IFAreaPaint() {

FILE: src/infinity/scene/style/paint/fillpaint.js
  function IFFillPaint (line 9) | function IFFillPaint() {

FILE: src/infinity/scene/style/paint/patternpaint.js
  function IFPatternPaint (line 9) | function IFPatternPaint() {

FILE: src/infinity/scene/style/paint/strokepaint.js
  function IFStrokePaint (line 9) | function IFStrokePaint() {

FILE: src/infinity/scene/style/paintentry.js
  function IFPaintEntry (line 9) | function IFPaintEntry() {

FILE: src/infinity/scene/style/sharedstyle.js
  function IFSharedStyle (line 11) | function IFSharedStyle() {

FILE: src/infinity/scene/style/style.js
  function IFStyle (line 12) | function IFStyle() {

FILE: src/infinity/scene/style/styleentry.js
  function IFStyleEntry (line 11) | function IFStyleEntry() {

FILE: src/infinity/scene/style/styleset.js
  function IFStyleSet (line 11) | function IFStyleSet() {

FILE: src/infinity/scene/style/veffect/offsetveffect.js
  function IFOffsetVEffect (line 9) | function IFOffsetVEffect() {

FILE: src/infinity/scene/style/veffectentry.js
  function IFVEffectEntry (line 9) | function IFVEffectEntry() {

FILE: src/infinity/vertex/vertex.js
  function IFVertex (line 9) | function IFVertex() {

FILE: src/infinity/vertex/vertexcontainer.js
  function IFVertexContainer (line 10) | function IFVertexContainer() {

FILE: src/infinity/vertex/vertexinfo.js
  function IFVertexInfo (line 9) | function IFVertexInfo() {
  function curvFunc (line 872) | function curvFunc(x, y) {
  function cHullCheck (line 884) | function cHullCheck(x, y) {
  function curvFunc (line 1094) | function curvFunc(x, y) {
  function cHullCheck (line 1147) | function cHullCheck(x, y) {
  function measurePoint (line 1352) | function measurePoint(x, y) {
  function measureCurve (line 1368) | function measureCurve(px1, py1, px2, py2, cx, cy) {
  function measureCurve2 (line 1408) | function measureCurve2(px1, py1, px2, py2, cx1, cy1, cx2, cy2) {

FILE: src/infinity/vertex/vertexoffsetter.js
  function IFVertexOffsetter (line 12) | function IFVertexOffsetter(source, offset, inset, outset, tol) {

FILE: src/infinity/vertex/vertexpixelaligner.js
  function IFVertexPixelAligner (line 11) | function IFVertexPixelAligner(source) {

FILE: src/infinity/vertex/vertexsource.js
  function IFVertexSource (line 10) | function IFVertexSource() {

FILE: src/infinity/vertex/vertextarget.js
  function IFVertexTarget (line 10) | function IFVertexTarget() {

FILE: src/infinity/vertex/vertextransformer.js
  function IFVertexTransformer (line 12) | function IFVertexTransformer(source, transform) {

FILE: src/infinity/view/scenestage.js
  function IFSceneStage (line 9) | function IFSceneStage(view) {

FILE: src/infinity/view/stage.js
  function IFStage (line 8) | function IFStage(view) {

FILE: src/infinity/view/view.js
  function IFView (line 9) | function IFView(scene) {

FILE: src/infinity/view/widget.js
  function GUIWidget (line 12) | function GUIWidget(container) {
  function addDocumentListener (line 678) | function addDocumentListener(eventClass) {

FILE: test/lib/chai.js
  function require (line 13) | function require(p) {
  function Assertion (line 177) | function Assertion (obj, msg, stack) {
  function an (line 373) | function an(type, msg) {
  function includeChainingBehavior (line 409) | function includeChainingBehavior () {
  function include (line 413) | function include (val, msg) {
  function checkArguments (line 601) | function checkArguments () {
  function assertEqual (line 636) | function assertEqual (val, msg) {
  function assertAbove (line 706) | function assertAbove (n, msg) {
  function assertLeast (line 754) | function assertLeast (n, msg) {
  function assertBelow (line 802) | function assertBelow (n, msg) {
  function assertMost (line 850) | function assertMost (n, msg) {
  function assertInstanceOf (line 936) | function assertInstanceOf (constructor, msg) {
  function assertOwnProperty (line 1058) | function assertOwnProperty (name, msg) {
  function assertLengthChain (line 1097) | function assertLengthChain () {
  function assertLength (line 1101) | function assertLength (n, msg) {
  function assertKeys (line 1184) | function assertKeys (keys) {
  function assertThrows (line 1271) | function assertThrows (constructor, errMsg, msg) {
  function AssertionError (line 1496) | function AssertionError (options) {
  function loadShould (line 2546) | function loadShould () {
  function _deepEqual (line 2791) | function _deepEqual(actual, expected, memos) {
  function isUndefinedOrNull (line 2827) | function isUndefinedOrNull(value) {
  function isArguments (line 2831) | function isArguments(object) {
  function objEquiv (line 2835) | function objEquiv(a, b, memos) {
  function parsePath (line 3098) | function parsePath (path) {
  function _getPathValue (line 3123) | function _getPathValue (parsed, obj) {
  function inspect (line 3268) | function inspect(obj, showHidden, depth, colors) {
  function formatValue (line 3307) | function formatValue(ctx, value, recurseTimes) {
  function formatPrimitive (line 3415) | function formatPrimitive(ctx, value) {
  function formatError (line 3439) | function formatError(value) {
  function formatArray (line 3444) | function formatArray(ctx, value, recurseTimes, visibleKeys, keys) {
  function formatProperty (line 3464) | function formatProperty(ctx, value, recurseTimes, visibleKeys, key, arra...
  function reduceToSingleString (line 3524) | function reduceToSingleString(output, base, braces) {
  function isArray (line 3544) | function isArray(ar) {
  function isRegExp (line 3549) | function isRegExp(re) {
  function isDate (line 3553) | function isDate(d) {
  function isError (line 3557) | function isError(e) {
  function objectToString (line 3561) | function objectToString(o) {

FILE: test/lib/mocha/mocha.js
  function require (line 6) | function require(p){
  function clonePath (line 78) | function clonePath(path) {
  function removeEmpty (line 81) | function removeEmpty(array) {
  function escapeHTML (line 90) | function escapeHTML(s) {
  function contextLines (line 251) | function contextLines(lines) {
  function eofNL (line 254) | function eofNL(curRange, i, current) {
  function isArray (line 363) | function isArray(obj) {
  function EventEmitter (line 373) | function EventEmitter(){}
  function on (line 408) | function on () {
  function Progress (line 552) | function Progress() {
  function Context (line 694) | function Context(){}
  function Hook (line 775) | function Hook(title, fn) {
  function F (line 784) | function F(){}
  function visit (line 979) | function visit(obj) {
  function image (line 1284) | function image(name) {
  function Mocha (line 1306) | function Mocha(options) {
  function parse (line 1594) | function parse(str) {
  function format (line 1633) | function format(ms) {
  function Base (line 1869) | function Base(runner) {
  function pluralize (line 1937) | function pluralize(n) {
  function pad (line 1987) | function pad(str, len) {
  function errorDiff (line 2000) | function errorDiff(err, type, escape) {
  function colorLines (line 2023) | function colorLines(name, str) {
  function Doc (line 2053) | function Doc(runner) {
  function Dot (line 2113) | function Dot(runner) {
  function F (line 2153) | function F(){}
  function HTMLCov (line 2182) | function HTMLCov(runner) {
  function coverageClass (line 2206) | function coverageClass(n) {
  function HTML (line 2259) | function HTML(runner, root) {
  function error (line 2400) | function error(msg) {
  function fragment (line 2408) | function fragment(html) {
  function hideSuitesWithout (line 2428) | function hideSuitesWithout(classname) {
  function unhide (line 2440) | function unhide() {
  function text (line 2451) | function text(el, str) {
  function on (line 2463) | function on(el, event, fn) {
  function JSONCov (line 2518) | function JSONCov(runner, output) {
  function map (line 2561) | function map(cov) {
  function coverage (line 2600) | function coverage(filename, data) {
  function clean (line 2643) | function clean(test) {
  function List (line 2675) | function List(runner) {
  function clean (line 2708) | function clean(test) {
  function JSONReporter (line 2740) | function JSONReporter(runner) {
  function clean (line 2781) | function clean(test) {
  function Landing (line 2831) | function Landing(runner) {
  function F (line 2887) | function F(){}
  function List (line 2917) | function List(runner) {
  function F (line 2958) | function F(){}
  function Markdown (line 2987) | function Markdown(runner) {
  function Min (line 3081) | function Min(runner) {
  function F (line 3098) | function F(){}
  function NyanCat (line 3127) | function NyanCat(runner) {
  function draw (line 3193) | function draw(color, n) {
  function write (line 3356) | function write(string) {
  function F (line 3364) | function F(){}
  function Progress (line 3402) | function Progress(runner, options) {
  function F (line 3458) | function F(){}
  function Spec (line 3489) | function Spec(runner) {
  function F (line 3553) | function F(){}
  function TAP (line 3584) | function TAP(runner) {
  function title (line 3632) | function title(test) {
  function Teamcity (line 3659) | function Teamcity(runner) {
  function escape (line 3692) | function escape(str) {
  function XUnit (line 3740) | function XUnit(runner) {
  function F (line 3774) | function F(){}
  function test (line 3784) | function test(test) {
  function tag (line 3806) | function tag(name, attrs, close, content) {
  function cdata (line 3824) | function cdata(str) {
  function Runnable (line 3870) | function Runnable(title, fn) {
  function F (line 3884) | function F(){}
  function multiple (line 4008) | function multiple(err) {
  function done (line 4015) | function done(err) {
  function Runner (line 4109) | function Runner(suite) {
  function F (line 4125) | function F(){}
  function next (line 4285) | function next(i) {
  function next (line 4325) | function next(suite) {
  function next (line 4424) | function next(err) {
  function next (line 4491) | function next() {
  function done (line 4497) | function done() {
  function filterLeaks (line 4584) | function filterLeaks(ok, globals) {
  function Suite (line 4647) | function Suite(title, ctx) {
  function F (line 4667) | function F(){}
  function Test (line 4924) | function Test(title, fn) {
  function F (line 4934) | function F(){}
  function ignored (line 5088) | function ignored(path){
  function highlight (line 5200) | function highlight(js) {
Condensed preview — 366 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (2,799K chars).
[
  {
    "path": ".bowerrc",
    "chars": 40,
    "preview": "{\n    \"directory\" : \"bower_components\"\n}"
  },
  {
    "path": ".gitignore",
    "chars": 79,
    "preview": ".idea\n.tmp\n.sass-cache\nnode_modules\nnode-webkit\nbower_components\nbuild\ndist\ntmp"
  },
  {
    "path": "Gruntfile.js",
    "chars": 16230,
    "preview": "var exec = require('child_process').exec;\n\nmodule.exports = function (grunt) {\n    require('load-grunt-tasks')(grunt);\n\n"
  },
  {
    "path": "LICENSE",
    "chars": 1035,
    "preview": "Gravit - The versatile, cross-platform design tool\nCopyright (C) 2012-2014 Quasado GmbH, Quasado e.K.\n\nThe name Gravit a"
  },
  {
    "path": "README.md",
    "chars": 2239,
    "preview": "## Introduction\n\nGravit is a design tool for Mac, Windows, Linux, ChromeOS and the Browser made\nin the spirit for Freeha"
  },
  {
    "path": "bower.json",
    "chars": 584,
    "preview": "{\n    \"name\": \"gravit\",\n    \"version\": \"0.0.1\",\n    \"dependencies\": {\n        \"jquery\": \"~2.1.1\",\n        \"mousetrap\": \""
  },
  {
    "path": "package.json",
    "chars": 1112,
    "preview": "{\n    \"name\": \"Gravit\",\n    \"description\" : \"Gravit, the versatile Design Tool\",\n    \"author\" : \"Quasado\",\n    \"version\""
  },
  {
    "path": "shell/browser/index.html",
    "chars": 1146,
    "preview": "<!doctype html>\n<html>\n<head>\n    <meta charset=\"utf-8\">\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n    <"
  },
  {
    "path": "shell/browser/shell.js",
    "chars": 3407,
    "preview": "(function (_) {\n    /**\n     * The browser shell\n     * @class GBrowserShell\n     * @extends GShell\n     * @constructor\n"
  },
  {
    "path": "shell/chrome/background.js",
    "chars": 228,
    "preview": "// TODO : Load/Store window state\nchrome.app.runtime.onLaunched.addListener(function() {\n    chrome.app.window.create('i"
  },
  {
    "path": "shell/chrome/filestorage.js",
    "chars": 5425,
    "preview": "(function (_) {\n    /**\n     * The file storage class for chrome\n     * @constructor\n     */\n    function GFileStorage()"
  },
  {
    "path": "shell/chrome/index.html",
    "chars": 1117,
    "preview": "<!doctype html>\n<html>\n<head>\n    <meta charset=\"utf-8\">\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n    <"
  },
  {
    "path": "shell/chrome/manifest.json",
    "chars": 528,
    "preview": "{\n    \"name\": \"%name%\",\n    \"description\": \"%description%\",\n    \"version\": \"%version%\",\n    \"manifest_version\": 2,\n    \""
  },
  {
    "path": "shell/chrome/shell.js",
    "chars": 3440,
    "preview": "(function (_) {\n    /**\n     * The chrome shell\n     * @class GChromeShell\n     * @extends GShell\n     * @constructor\n  "
  },
  {
    "path": "shell/system/Info.plist",
    "chars": 1584,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "shell/system/filestorage.js",
    "chars": 7217,
    "preview": "(function (_) {\n    var fs = require('fs');\n\n    /**\n     * The file storage class for the system\n     * @constructor\n  "
  },
  {
    "path": "shell/system/index.html",
    "chars": 572,
    "preview": "<!doctype html>\n<html>\n<head>\n    <meta charset=\"utf-8\">\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n    <"
  },
  {
    "path": "shell/system/package/osx/dmg.json",
    "chars": 328,
    "preview": "{\n    \"title\": \"Gravit\",\n    \"icon\": \"../../appicon.icns\",\n    \"background\": \"background.tiff\",\n    \"icon-size\": 120,\n  "
  },
  {
    "path": "shell/system/package.json",
    "chars": 313,
    "preview": "{\n    \"name\": \"%name%\",\n    \"description\": \"%description%\",\n    \"version\": \"%version%\",\n    \"main\": \"index.html\",\n    \"w"
  },
  {
    "path": "shell/system/shell.js",
    "chars": 13554,
    "preview": "(function (_) {\n    var gui = require('nw.gui');\n\n    gui.App.on('open', function (cmdline) {\n        if (cmdline && cmd"
  },
  {
    "path": "shell/system/winstate.js",
    "chars": 4492,
    "preview": "/**\n * Cross-platform window state preservation.\n * Yes this code is quite complicated, but this is the best I came up w"
  },
  {
    "path": "src/application/application.js",
    "chars": 43319,
    "preview": "(function (_) {\n    /**\n     * The global application class\n     * @class GApplication\n     * @extends GEventTarget\n    "
  },
  {
    "path": "src/application/bootstrap.js",
    "chars": 7224,
    "preview": "var gravit = {\n    /**\n     * Array<GModule>\n     */\n    modules: [],\n\n    /**\n     * Array<GStorage>\n     */\n    storag"
  },
  {
    "path": "src/application/component/autoedit.js",
    "chars": 3434,
    "preview": "(function ($) {\n\n    var methods = {\n        init: function (options) {\n            options = $.extend({\n               "
  },
  {
    "path": "src/application/component/blendmode.js",
    "chars": 3601,
    "preview": "(function ($) {\n\n    var blendModes = [\n        {\n            type: IFPaintCanvas.BlendMode.Normal,\n            // TODO "
  },
  {
    "path": "src/application/component/colorbutton.js",
    "chars": 5127,
    "preview": "(function ($) {\n\n    var COLORPANEL = null;\n\n    function getColorPanel() {\n        if (!COLORPANEL) {\n            COLOR"
  },
  {
    "path": "src/application/component/colorpanel.js",
    "chars": 45265,
    "preview": "(function ($) {\n    /** @enum */\n    var ViewType = {\n        Palette: 'palette',\n        Swatches: 'swatches',\n        "
  },
  {
    "path": "src/application/component/cornertype.js",
    "chars": 2174,
    "preview": "(function ($) {\n\n    var cornerTypes = [\n        {\n            type: IFPathBase.CornerType.Rounded,\n            // TODO "
  },
  {
    "path": "src/application/component/gradienteditor.js",
    "chars": 11942,
    "preview": "(function ($) {\n\n    function updateStop($this, $stop) {\n        $stop.css('left', $stop.data('stop-position') + '%');\n "
  },
  {
    "path": "src/application/component/menu.js",
    "chars": 21057,
    "preview": "(function (_) {\n    /**\n     * A menu implementation\n     * @param {GMenuItem|GMenuBar} parent parent if not a standalon"
  },
  {
    "path": "src/application/component/menubar.js",
    "chars": 1326,
    "preview": "(function (_) {\n    /**\n     * A menu representing a menu bar\n     * @param {GMenu} [menu] optional menu to use as base\n"
  },
  {
    "path": "src/application/component/menubutton.js",
    "chars": 3370,
    "preview": "(function ($) {\n\n    var DEF_ACTION_CARET_CSS = {\n        'margin': '0px 1px',\n        'transform': 'rotate(-45deg)',\n  "
  },
  {
    "path": "src/application/component/menuitem.js",
    "chars": 17665,
    "preview": "(function (_) {\n    /**\n     * A menu item\n     * @param {Number} type the type of the menu item.\n     * Defaults to GMe"
  },
  {
    "path": "src/application/component/overlay.js",
    "chars": 3968,
    "preview": "(function ($) {\n    var openOverlayStack = [];\n\n    document.addEventListener('keydown', function (evt) {\n        if (op"
  },
  {
    "path": "src/application/component/panel.js",
    "chars": 2966,
    "preview": "(function ($) {\n    var methods = {\n        init: function (options) {\n            options = $.extend({\n                "
  },
  {
    "path": "src/application/component/patterntarget.js",
    "chars": 5106,
    "preview": "(function ($) {\n\n    var previewBoxSize = 20;\n\n    var dragImage = $('<div></div>')\n        .css({\n            'position"
  },
  {
    "path": "src/application/component/pivot.js",
    "chars": 2352,
    "preview": "(function ($) {\n\n    var methods = {\n        init: function (options) {\n            return this.each(function () {\n     "
  },
  {
    "path": "src/application/component/stylepanel.js",
    "chars": 18128,
    "preview": "(function ($) {\n\n    function updateSelectedStyle($this, style) {\n        $this.find('.style-block').each(function (inde"
  },
  {
    "path": "src/application/component/swatchpanel.js",
    "chars": 20718,
    "preview": "(function ($) {\n\n    function updateSelectedSwatch($this, swatch) {\n        if ($this.data('gswatchpanel').options.allow"
  },
  {
    "path": "src/application/component/unit.js",
    "chars": 1775,
    "preview": "(function ($) {\n\n    var units = [\n        {\n            unit: IFLength.Unit.PX,\n            // TODO : I18N\n            "
  },
  {
    "path": "src/application/document.js",
    "chars": 6299,
    "preview": "(function (_) {\n    /**\n     * An instance of an opened document\n     * @class GDocument\n     * @extends GEventTarget\n  "
  },
  {
    "path": "src/application/extension/action.js",
    "chars": 2451,
    "preview": "(function (_) {\n\n    /**\n     * Base class for an action\n     * @class GAction\n     * @extends IFObject\n     * @construc"
  },
  {
    "path": "src/application/extension/colormatcher.js",
    "chars": 1300,
    "preview": "(function (_) {\n\n    /**\n     * Base class for a color matcher\n     * @class GColorMatcher\n     * @constructor\n     */\n "
  },
  {
    "path": "src/application/extension/exporter.js",
    "chars": 3062,
    "preview": "(function (_) {\n    /**\n     * The base for an exporter\n     * @class GExporter\n     * @constructor\n     */\n    function"
  },
  {
    "path": "src/application/extension/module.js",
    "chars": 338,
    "preview": "(function (_) {\n    /**\n     * The base for a module\n     * @class GModule\n     * @constructor\n     * @version 1.0\n     "
  },
  {
    "path": "src/application/extension/palette.js",
    "chars": 1314,
    "preview": "(function (_) {\n\n    /**\n     * Base class for an palette\n     * @class GPalette\n     * @extends GView\n     * @construct"
  },
  {
    "path": "src/application/extension/panel.js",
    "chars": 1088,
    "preview": "(function (_) {\n\n    /**\n     * Base class for an panel\n     * @class GPanel\n     * @extends GView\n     * @constructor\n "
  },
  {
    "path": "src/application/extension/properties.js",
    "chars": 1152,
    "preview": "(function (_) {\n\n    /**\n     * Base class for property panels\n     * @class GProperties\n     * @extends GEventTarget\n  "
  },
  {
    "path": "src/application/extension/sidebar.js",
    "chars": 1119,
    "preview": "(function (_) {\n\n    /**\n     * Base class for an sidebar\n     * @class GSidebar\n     * @extends GView\n     * @construct"
  },
  {
    "path": "src/application/extension/storage.js",
    "chars": 5298,
    "preview": "(function (_) {\n    /**\n     * The storage base class\n     * @constructor\n     */\n    function GStorage() {\n    };\n\n    "
  },
  {
    "path": "src/application/extension/styleentry.js",
    "chars": 1682,
    "preview": "(function (_) {\n\n    /**\n     * Base class for style entry handlers\n     * @class GStyleEntry\n     * @constructor\n     *"
  },
  {
    "path": "src/application/extension/transformer.js",
    "chars": 1176,
    "preview": "(function (_) {\n\n    /**\n     * Base class for transformer panels\n     * @class GTransformer\n     * @extends GEventTarge"
  },
  {
    "path": "src/application/extension/view.js",
    "chars": 6230,
    "preview": "(function (_) {\n\n    /**\n     * Base class for a view\n     * @class GView\n     * @extends GEventTarget\n     * @construct"
  },
  {
    "path": "src/application/i18n/i18n_de.js",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "src/application/i18n/i18n_en.js",
    "chars": 1151,
    "preview": "// Extensions\nifLocale.setValues(GColorMatcher, IFLocale.Language.English, [\"category.harmony\", \"category.palette\"], [\"H"
  },
  {
    "path": "src/application/shell.js",
    "chars": 4011,
    "preview": "(function (_) {\n    /**\n     * The global shell class\n     * @class GShell\n     * @extends GEventTarget\n     * @construc"
  },
  {
    "path": "src/application/util/ciede2000.js",
    "chars": 6169,
    "preview": "(function (_) {\n    /**\n     * @author Markus Näsman\n     * @copyright 2012 (c) Markus Näsman <markus at botten dot org "
  },
  {
    "path": "src/application/util/image.js",
    "chars": 2048,
    "preview": "(function (_) {\n\n    /**\n     * Converts a given image or url into a canvas\n     * @param {String|Image|HTMLImageelement"
  },
  {
    "path": "src/application/util/selectors.js",
    "chars": 405,
    "preview": "(function ($) {\n\n    /**\n     * Selector that selects all editable elements including all input\n     * elements and also"
  },
  {
    "path": "src/application/workspace/header.js",
    "chars": 615,
    "preview": "(function (_) {\n    /**\n     * The global idebar class\n     * @class GHeader\n     * @constructor\n     * @version 1.0\n   "
  },
  {
    "path": "src/application/workspace/palettes.js",
    "chars": 12759,
    "preview": "(function (_) {\n    /**\n     * The global palettes class\n     * @class GPalettes\n     * @constructor\n     */\n    functio"
  },
  {
    "path": "src/application/workspace/panels.js",
    "chars": 3285,
    "preview": "(function (_) {\n    /**\n     * The global panels class\n     * @class GPanels\n     * @constructor\n     */\n    function GP"
  },
  {
    "path": "src/application/workspace/sidebars.js",
    "chars": 2427,
    "preview": "(function (_) {\n    /**\n     * The global sidebars class\n     * @class GSidebars\n     * @constructor\n     */\n    functio"
  },
  {
    "path": "src/application/workspace/toolbar.js",
    "chars": 12070,
    "preview": "(function (_) {\n    /**\n     * The global toolbar class\n     * @class GToolbar\n     * @constructor\n     */\n    function "
  },
  {
    "path": "src/application/workspace/window.js",
    "chars": 5614,
    "preview": "(function (_) {\n    /**\n     * An instance of an opened window\n     * @class GWindow\n     * @extends GEventTarget\n     *"
  },
  {
    "path": "src/application/workspace/windows.js",
    "chars": 9198,
    "preview": "(function (_) {\n    /**\n     * The global window container class\n     * @class GWindows\n     * @extends GEventTarget\n   "
  },
  {
    "path": "src/development/bootstrap.js",
    "chars": 38,
    "preview": "var gDevelopment = {\n    tests : []\n};"
  },
  {
    "path": "src/development/development.js",
    "chars": 783,
    "preview": "(function (_) {\n    /**\n     * Gravit Development Module\n     * @class GDevelopmentModule\n     * @constructor\n     * @ex"
  },
  {
    "path": "src/development/test/clone_scene.js",
    "chars": 448,
    "preview": "(function () {\n    gDevelopment.tests.push({\n        title: 'Clone Scene',\n        test: function () {\n            var s"
  },
  {
    "path": "src/development/test/create_multiple_pages.js",
    "chars": 3254,
    "preview": "(function () {\n    gDevelopment.tests.push({\n        title: 'Create Multiple Pages',\n        test: function () {\n       "
  },
  {
    "path": "src/development/test/create_rect_grid_page.js",
    "chars": 1442,
    "preview": "(function () {\n    gDevelopment.tests.push({\n        title: 'Create Rectangular Grid on Page',\n        test: function te"
  },
  {
    "path": "src/development/test/deserialize_scene.js",
    "chars": 490,
    "preview": "(function () {\n    gDevelopment.tests.push({\n        title: 'Deserialize Scene',\n        test: function () {\n           "
  },
  {
    "path": "src/development/test/serialize_scene.js",
    "chars": 222,
    "preview": "(function () {\n    gDevelopment.tests.push({\n        title: 'Serialize Scene',\n        test: function () {\n            p"
  },
  {
    "path": "src/development/testaction.js",
    "chars": 852,
    "preview": "(function (_) {\n    function TestAction(test) {\n        this._test = test;\n    };\n    IFObject.inherit(TestAction, GActi"
  },
  {
    "path": "src/gravit/action/addlayeraction.js",
    "chars": 1749,
    "preview": "(function (_) {\n\n    /**\n     * Action for inserting a layer\n     * @class GAddLayerAction\n     * @extends GAction\n     "
  },
  {
    "path": "src/gravit/action/addpageaction.js",
    "chars": 1335,
    "preview": "(function (_) {\n\n    /**\n     * Action for adding a new page\n     * @class GAddPageAction\n     * @extends GAction\n     *"
  },
  {
    "path": "src/gravit/action/alignaction.js",
    "chars": 9004,
    "preview": "(function (_) {\n\n    /**\n     * Action for aligning\n     * @class GAlignAction\n     * @extends GAction\n     * @construct"
  },
  {
    "path": "src/gravit/action/arrangeaction.js",
    "chars": 4892,
    "preview": "(function (_) {\n\n    /**\n     * Action for ordering\n     * @class GArrangeAction\n     * @extends GAction\n     * @constru"
  },
  {
    "path": "src/gravit/action/cloneaction.js",
    "chars": 1744,
    "preview": "(function (_) {\n\n    /**\n     * Action for cloning the selection from the current document\n     * @class GCloneAction\n  "
  },
  {
    "path": "src/gravit/action/closeaction.js",
    "chars": 1449,
    "preview": "(function (_) {\n\n    /**\n     * Action closing a document\n     * @class GCloseAction\n     * @extends GAction\n     * @con"
  },
  {
    "path": "src/gravit/action/closeallaction.js",
    "chars": 1397,
    "preview": "(function (_) {\n\n    /**\n     * Action closing all documents\n     * @class GCloseAllAction\n     * @extends GAction\n     "
  },
  {
    "path": "src/gravit/action/copyaction.js",
    "chars": 2229,
    "preview": "(function (_) {\n\n    /**\n     * Action for copying the current selection to the clipboard\n     * @class GCopyAction\n    "
  },
  {
    "path": "src/gravit/action/copyattributesaction.js",
    "chars": 2623,
    "preview": "(function (_) {\n\n    /**\n     * Action for copying the current selection's attributes to the clipboard\n     * @class GCo"
  },
  {
    "path": "src/gravit/action/cutaction.js",
    "chars": 2152,
    "preview": "(function (_) {\n\n    /**\n     * Action for cutting the current selection to the clipboard\n     * @class GCutAction\n     "
  },
  {
    "path": "src/gravit/action/deleteaction.js",
    "chars": 1872,
    "preview": "(function (_) {\n\n    /**\n     * Action for deleting the selection from the current document\n     * @class GDeleteAction\n"
  },
  {
    "path": "src/gravit/action/deletelayeraction.js",
    "chars": 3239,
    "preview": "(function (_) {\n\n    /**\n     * Action for deleting a layer\n     * @class GDeleteLayerAction\n     * @extends GAction\n   "
  },
  {
    "path": "src/gravit/action/deletepageaction.js",
    "chars": 3062,
    "preview": "(function (_) {\n\n    /**\n     * Action for deleting the active page\n     * @class GDeletePageAction\n     * @extends GAct"
  },
  {
    "path": "src/gravit/action/distributeaction.js",
    "chars": 6395,
    "preview": "(function (_) {\n\n    /**\n     * Action for distributing\n     * @class GDistributeAction\n     * @extends GAction\n     * @"
  },
  {
    "path": "src/gravit/action/duplicateaction.js",
    "chars": 1812,
    "preview": "(function (_) {\n\n    /**\n     * Action for cloning the selection from the current document\n     * @class GDuplicateActio"
  },
  {
    "path": "src/gravit/action/fitallaction.js",
    "chars": 1947,
    "preview": "(function (_) {\n\n    /**\n     * Action for fitting everything into the current view\n     * @class GFitAllAction\n     * @"
  },
  {
    "path": "src/gravit/action/fitcurrentlayeraction.js",
    "chars": 1844,
    "preview": "(function (_) {\n\n    /**\n     * Action for fitting the current layer into the current view\n     * @class GFitCurrentLaye"
  },
  {
    "path": "src/gravit/action/fitcurrentpageaction.js",
    "chars": 1992,
    "preview": "(function (_) {\n\n    /**\n     * Action for fitting the current page into the current view\n     * @class GFitCurrentPageA"
  },
  {
    "path": "src/gravit/action/fitselectionaction.js",
    "chars": 2227,
    "preview": "(function (_) {\n\n    /**\n     * Action for fitting the selection into the current view\n     * @class GFitSelectionAction"
  },
  {
    "path": "src/gravit/action/groupaction.js",
    "chars": 2651,
    "preview": "(function (_) {\n\n    /**\n     * Action for grouping the selection together\n     * @class GGroupAction\n     * @extends GA"
  },
  {
    "path": "src/gravit/action/invertselectionaction.js",
    "chars": 2185,
    "preview": "(function (_) {\n\n    /**\n     * Action for inverting the current selection\n     * @class GInvertSelectionAction\n     * @"
  },
  {
    "path": "src/gravit/action/layertypeaction.js",
    "chars": 2264,
    "preview": "(function (_) {\n\n    /**\n     * Action for setting the type of the active layer\n     * @class GLayerTypeAction\n     * @e"
  },
  {
    "path": "src/gravit/action/magnificationaction.js",
    "chars": 2520,
    "preview": "(function (_) {\n\n    /**\n     * Action for setting a specific magnfication on current view\n     * @class GMagnificationA"
  },
  {
    "path": "src/gravit/action/newaction.js",
    "chars": 1225,
    "preview": "(function (_) {\n\n    /**\n     * Action creating a new document\n     * @class GNewAction\n     * @extends GAction\n     * @"
  },
  {
    "path": "src/gravit/action/newwindowaction.js",
    "chars": 1557,
    "preview": "(function (_) {\n\n    /**\n     * Action for cloning the current view\n     * @class GNewWindowAction\n     * @extends GActi"
  },
  {
    "path": "src/gravit/action/openaction.js",
    "chars": 1972,
    "preview": "(function (_) {\n\n    /**\n     * Action opening a document via the system storage\n     * @class GOpenAction\n     * @exten"
  },
  {
    "path": "src/gravit/action/originalviewaction.js",
    "chars": 1802,
    "preview": "(function (_) {\n\n    /**\n     * Action for reseting the current view to the original view\n     * @class GOriginalViewAct"
  },
  {
    "path": "src/gravit/action/paintmodeaction.js",
    "chars": 2233,
    "preview": "(function (_) {\n\n    /**\n     * Action for changing paint mode in the current view\n     * @class GPaintModeAction\n     *"
  },
  {
    "path": "src/gravit/action/pasteaction.js",
    "chars": 3280,
    "preview": "(function (_) {\n\n    /**\n     * Action for pasting clipboard contents into the center of active page\n     * @class GPast"
  },
  {
    "path": "src/gravit/action/pasteattributesaction.js",
    "chars": 2930,
    "preview": "(function (_) {\n\n    /**\n     * Action for pasting clipboard contents as attributes\n     * @class GPasteAttributesAction"
  },
  {
    "path": "src/gravit/action/pasteinplaceaction.js",
    "chars": 2346,
    "preview": "(function (_) {\n\n    /**\n     * Action for pasting clipboard contents onto the positions relative to the active page top"
  },
  {
    "path": "src/gravit/action/pasteinsideaction.js",
    "chars": 4343,
    "preview": "(function (_) {\n\n    /**\n     * Action for pasting clipboard contents into selection\n     * @class GPasteInsideAction\n  "
  },
  {
    "path": "src/gravit/action/pixelpreviewaction.js",
    "chars": 1975,
    "preview": "(function (_) {\n\n    /**\n     * Action for switching between pixel-mode and vector mode\n     * @class GPixelPreviewActio"
  },
  {
    "path": "src/gravit/action/placeimageaction.js",
    "chars": 2484,
    "preview": "(function (_) {\n\n    /**\n     * Action for placing an image\n     * @class GPlaceImageAction\n     * @extends GAction\n    "
  },
  {
    "path": "src/gravit/action/redoaction.js",
    "chars": 2059,
    "preview": "(function (_) {\n\n    /**\n     * Action for redo on the current document\n     * @class GRedoAction\n     * @extends GActio"
  },
  {
    "path": "src/gravit/action/saveaction.js",
    "chars": 1437,
    "preview": "(function (_) {\n\n    /**\n     * Action saving a document\n     * @class GSaveAction\n     * @extends GAction\n     * @const"
  },
  {
    "path": "src/gravit/action/saveallaction.js",
    "chars": 1642,
    "preview": "(function (_) {\n\n    /**\n     * Action saving all documents\n     * @class GSaveAllAction\n     * @extends GAction\n     * "
  },
  {
    "path": "src/gravit/action/saveasaction.js",
    "chars": 2108,
    "preview": "(function (_) {\n\n    /**\n     * Action saving a document filed under a name\n     * @class GSaveAsAction\n     * @extends "
  },
  {
    "path": "src/gravit/action/selectallaction.js",
    "chars": 2396,
    "preview": "(function (_) {\n\n    /**\n     * Action for selecting everything\n     * @class GSelectAllAction\n     * @extends GAction\n "
  },
  {
    "path": "src/gravit/action/showallpagesaction.js",
    "chars": 1906,
    "preview": "(function (_) {\n\n    /**\n     * Action for switching between single and multi page view\n     * @class GShowAllPagesActio"
  },
  {
    "path": "src/gravit/action/showgridaction.js",
    "chars": 2050,
    "preview": "(function (_) {\n\n    /**\n     * Action for showing / hiding the grid\n     * @class GShowGridAction\n     * @extends GActi"
  },
  {
    "path": "src/gravit/action/showrulersaction.js",
    "chars": 1778,
    "preview": "(function (_) {\n\n    /**\n     * Action for showing / hiding the rulers\n     * @class GShowRulersAction\n     * @extends G"
  },
  {
    "path": "src/gravit/action/slicefromselection.js",
    "chars": 2381,
    "preview": "(function (_) {\n\n    /**\n     * Action for creating a slice on selection\n     * @class GSliceFromSelectionAction\n     * "
  },
  {
    "path": "src/gravit/action/snapunitaction.js",
    "chars": 3596,
    "preview": "(function (_) {\n\n    /**\n     * Action for snapping to units\n     * @class GSnapUnitAction\n     * @extends GAction\n     "
  },
  {
    "path": "src/gravit/action/transformaction.js",
    "chars": 5395,
    "preview": "(function (_) {\n\n    /**\n     * Action for transforming\n     * @class GTransformAction\n     * @extends GAction\n     * @c"
  },
  {
    "path": "src/gravit/action/undoaction.js",
    "chars": 2038,
    "preview": "(function (_) {\n\n    /**\n     * Action for undo on the current document\n     * @class GUndoAction\n     * @extends GActio"
  },
  {
    "path": "src/gravit/action/ungroupaction.js",
    "chars": 3093,
    "preview": "(function (_) {\n\n    /**\n     * Action for ungrouping the selection\n     * @class GUngroupAction\n     * @extends GAction"
  },
  {
    "path": "src/gravit/action/zoominaction.js",
    "chars": 2246,
    "preview": "(function (_) {\n\n    /**\n     * Action for zooming into the current view\n     * @class GZoomInAction\n     * @extends GAc"
  },
  {
    "path": "src/gravit/action/zoomoutaction.js",
    "chars": 2266,
    "preview": "(function (_) {\n\n    /**\n     * Action for zooming out of the current view\n     * @class GZoomOutAction\n     * @extends "
  },
  {
    "path": "src/gravit/colormatcher/analogousmatcher.js",
    "chars": 1409,
    "preview": "(function (_) {\n\n    /**\n     * Analogous Color Matcher\n     * @class GAnalogousMatcher\n     * @extends GColorMatcher\n  "
  },
  {
    "path": "src/gravit/colormatcher/complementarymatcher.js",
    "chars": 1078,
    "preview": "(function (_) {\n\n    /**\n     * Complementary Color Matcher\n     * @class GComplementaryMatcher\n     * @extends GColorMa"
  },
  {
    "path": "src/gravit/exporter/imagexporter.js",
    "chars": 1556,
    "preview": "(function (_) {\n    /**\n     * The image exporter\n     * @class GImageExporter\n     * @extends GExporter\n     * @constru"
  },
  {
    "path": "src/gravit/gravit.js",
    "chars": 16498,
    "preview": "(function (_) {\n    /**\n     * Gravit Core Module\n     * @class GravitModule\n     * @constructor\n     * @extends GModule"
  },
  {
    "path": "src/gravit/i18n/i18n_de.js",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "src/gravit/i18n/i18n_en.js",
    "chars": 6082,
    "preview": "// Action\nifLocale.setValues(GAddLayerAction, IFLocale.Language.English, [\"title\"], [\"Add Layer\"]);\nifLocale.setValues(G"
  },
  {
    "path": "src/gravit/palette/exportpalette.js",
    "chars": 11512,
    "preview": "(function (_) {\n\n    /**\n     * Export Palette\n     * @class GExportPalette\n     * @extends GPalette\n     * @constructor"
  },
  {
    "path": "src/gravit/palette/stylepalette.js",
    "chars": 47105,
    "preview": "(function (_) {\n\n    /**\n     * Style Palette\n     * @class GStylePalette\n     * @extends GPalette\n     * @constructor\n "
  },
  {
    "path": "src/gravit/panel/propertiespanel.js",
    "chars": 5169,
    "preview": "(function (_) {\n\n    /**\n     * Properties Panel\n     * @class GPropertiesPanel\n     * @extends GPanel\n     * @construct"
  },
  {
    "path": "src/gravit/panel/transformpanel.js",
    "chars": 4501,
    "preview": "(function (_) {\n\n    /**\n     * Transform Panel\n     * @class GTransformPanel\n     * @extends GPanel\n     * @constructor"
  },
  {
    "path": "src/gravit/properties/documentproperties.js",
    "chars": 16738,
    "preview": "(function (_) {\n\n    /**\n     * Document properties panel\n     * @class GDocumentProperties\n     * @extends GProperties\n"
  },
  {
    "path": "src/gravit/properties/ellipseproperties.js",
    "chars": 6803,
    "preview": "(function (_) {\n\n    /**\n     * Ellipse properties panel\n     * @class GEllipseProperties\n     * @extends GProperties\n  "
  },
  {
    "path": "src/gravit/properties/imageproperties.js",
    "chars": 7841,
    "preview": "(function (_) {\n\n    /**\n     * Image properties panel\n     * @class GImageProperties\n     * @extends GProperties\n     *"
  },
  {
    "path": "src/gravit/properties/infoproperties.js",
    "chars": 14422,
    "preview": "(function (_) {\n\n    /**\n     * Info properties panel\n     * @class GInfoProperties\n     * @extends GProperties\n     * @"
  },
  {
    "path": "src/gravit/properties/pageproperties.js",
    "chars": 18656,
    "preview": "(function (_) {\n\n    /**\n     * Page properties panel\n     * @class GPageProperties\n     * @extends GProperties\n     * @"
  },
  {
    "path": "src/gravit/properties/pathproperties.js",
    "chars": 17854,
    "preview": "(function (_) {\n\n    /**\n     * Path properties panel\n     * @class GPathProperties\n     * @extends GProperties\n     * @"
  },
  {
    "path": "src/gravit/properties/polygonproperties.js",
    "chars": 12185,
    "preview": "(function (_) {\n\n    /**\n     * Polygon properties panel\n     * @class GPolygonProperties\n     * @extends GProperties\n  "
  },
  {
    "path": "src/gravit/properties/rectangleproperties.js",
    "chars": 10724,
    "preview": "(function (_) {\n\n    /**\n     * Rectangle properties panel\n     * @class GRectangleProperties\n     * @extends GPropertie"
  },
  {
    "path": "src/gravit/properties/sliceproperties.js",
    "chars": 5247,
    "preview": "(function (_) {\n\n    /**\n     * Slice properties panel\n     * @class GSliceProperties\n     * @extends GProperties\n     *"
  },
  {
    "path": "src/gravit/properties/textproperties.js",
    "chars": 24433,
    "preview": "(function (_) {\n\n    /**\n     * Text properties panel\n     * @class GTextProperties\n     * @extends GProperties\n     * @"
  },
  {
    "path": "src/gravit/sidebar/pageslayerssidebar.js",
    "chars": 38443,
    "preview": "(function (_) {\n\n    var dragPage = null;\n\n    function canDropPage(target) {\n        if (dragPage) {\n            var ta"
  },
  {
    "path": "src/gravit/sidebar/stylesswatchessidebar.js",
    "chars": 5595,
    "preview": "(function (_) {\n\n    /**\n     * The styles sidebar\n     * @class GStylesSwatchesSidebar\n     * @extends GSidebar\n     * "
  },
  {
    "path": "src/gravit/styleentry/areapaintentry.js",
    "chars": 8549,
    "preview": "(function (_) {\n\n    /**\n     * Area pattern paint style entry handler\n     * @class GAreaPaintEntry\n     * @extends GPa"
  },
  {
    "path": "src/gravit/styleentry/blurfilterentry.js",
    "chars": 2676,
    "preview": "(function (_) {\n\n    /**\n     * Blur filter style entry handler\n     * @class GBlurFilterEntry\n     * @extends GStyleEnt"
  },
  {
    "path": "src/gravit/styleentry/fillpaintentry.js",
    "chars": 1128,
    "preview": "(function (_) {\n\n    /**\n     * Fill paint style entry handler\n     * @class GFillPaintEntry\n     * @extends GAreaPaintE"
  },
  {
    "path": "src/gravit/styleentry/offsetveffectentry.js",
    "chars": 3824,
    "preview": "(function (_) {\n\n    /**\n     * Offset vector effect style entry handler\n     * @class GOffsetVEffectEntry\n     * @exten"
  },
  {
    "path": "src/gravit/styleentry/patternpaintentry.js",
    "chars": 7667,
    "preview": "(function (_) {\n\n    /**\n     * Pattern paint style entry handler\n     * @class GPatternPaintEntry\n     * @extends GStyl"
  },
  {
    "path": "src/gravit/styleentry/shadoweffectentry.js",
    "chars": 5542,
    "preview": "(function (_) {\n\n    /**\n     * Shadow effect style entry handler\n     * @class GShadowEffectEntry\n     * @extends GStyl"
  },
  {
    "path": "src/gravit/styleentry/strokepaintentry.js",
    "chars": 7542,
    "preview": "(function (_) {\n\n    /**\n     * Stroke paint style entry handler\n     * @class GStrokePaintEntry\n     * @extends GAreaPa"
  },
  {
    "path": "src/gravit/transformer/adjusttransformer.js",
    "chars": 15231,
    "preview": "(function (_) {\n\n    /**\n     * Transform transform panel\n     * @class GAdjustTransformer\n     * @extends GTransformer\n"
  },
  {
    "path": "src/gravit/transformer/aligntransformer.js",
    "chars": 14707,
    "preview": "(function (_) {\n\n    /**\n     * Align transform panel\n     * @class GAlignTransformer\n     * @extends GTransformer\n     "
  },
  {
    "path": "src/index.html",
    "chars": 18697,
    "preview": "<!doctype html>\n<html>\n<head>\n    <meta charset=\"utf-8\">\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n    <"
  },
  {
    "path": "src/infinity/core/cursor.js",
    "chars": 3685,
    "preview": "(function (_) {\n\n    /**\n     * @enum\n     */\n    var IFCursor = {\n        /**\n         * The default/auto cursor\n      "
  },
  {
    "path": "src/infinity/core/key.js",
    "chars": 9945,
    "preview": "(function (_) {\n\n    /**\n     * @class IFKey\n     * @constructor\n     * @version 1.0\n     */\n    function IFKey() {\n    "
  },
  {
    "path": "src/infinity/core/locale.js",
    "chars": 8456,
    "preview": "(function (_) {\n\n    /**\n     * @class IFLocale\n     * @extends IFObject\n     * @constructor\n     * @version 1.0\n     */"
  },
  {
    "path": "src/infinity/core/math.js",
    "chars": 79209,
    "preview": "(function (_) {\n\n    /**\n     * @class IFMath\n     * @constructor\n     * @version 1.0\n     */\n    function IFMath() {\n  "
  },
  {
    "path": "src/infinity/core/object.js",
    "chars": 6790,
    "preview": "(function (_) {\n    /**\n     * IFObject is the base object for everything and all\n     * @class IFObject\n     * @constru"
  },
  {
    "path": "src/infinity/core/system.js",
    "chars": 4554,
    "preview": "(function (_) {\n\n    /**\n     * @class IFSystem\n     * @constructor\n     * @version 1.0\n     */\n    function IFSystem() "
  },
  {
    "path": "src/infinity/core/util.js",
    "chars": 10691,
    "preview": "(function (_) {\n\n    /**\n     * @class IFUtil\n     * @constructor\n     * @version 1.0\n     */\n    function IFUtil() {\n  "
  },
  {
    "path": "src/infinity/event/event.js",
    "chars": 361,
    "preview": "(function (_) {\n    /**\n     * An object representing an event.\n     * @class GEvent\n     * @extends IFObject\n     * @co"
  },
  {
    "path": "src/infinity/event/eventtarget.js",
    "chars": 3900,
    "preview": "(function (_) {\n    /**\n     * A mixin representing the target of an event.\n     * @class GEventTarget\n     * @mixin\n   "
  },
  {
    "path": "src/infinity/event/inputevent.js",
    "chars": 561,
    "preview": "(function (_) {\n    /**\n     * An object representing an input event.\n     * @class GUIInputEvent\n     * @extends GEvent"
  },
  {
    "path": "src/infinity/event/keyevent.js",
    "chars": 2927,
    "preview": "(function (_) {\n    /**\n     * An object representing a key input event.\n     * @class GUIKeyEvent\n     * @extends GUIIn"
  },
  {
    "path": "src/infinity/event/mouseevent.js",
    "chars": 10319,
    "preview": "(function (_) {\n    /**\n     * An object representing a mouse input event.\n     * @class GUIMouseEvent\n     * @extends G"
  },
  {
    "path": "src/infinity/geometry/length.js",
    "chars": 23320,
    "preview": "(function (_) {\n    /**\n     * A class representing a unit with a value\n     * @param {Number} value\n     * @param {IFLe"
  },
  {
    "path": "src/infinity/geometry/point.js",
    "chars": 4705,
    "preview": "(function (_) {\n\n    /**\n     * A simple point construct. Note that this class is immutable.\n     * @class IFPoint\n     "
  },
  {
    "path": "src/infinity/geometry/rect.js",
    "chars": 20276,
    "preview": "(function (_) {\n\n    /**\n     * A simple rect construct. Note that this class is immutable.\n     * @class IFRect\n     * "
  },
  {
    "path": "src/infinity/geometry/transform.js",
    "chars": 10504,
    "preview": "(function (_) {\n\n    /**\n     * A 2d affine transform, if no parameters are given then\n     * this constructos an identi"
  },
  {
    "path": "src/infinity/i18n/i18n_de.js",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "src/infinity/i18n/i18n_en.js",
    "chars": 3755,
    "preview": "// Core\nifLocale.setValues(IFLocale, IFLocale.Language.English, [\"create\", \"add\", \"edit\", \"remove\", \"delete\", \"open\", \"s"
  },
  {
    "path": "src/infinity/paint/annotation.js",
    "chars": 5126,
    "preview": "(function (_) {\n\n    /**\n     * @class IFAnnotation\n     * @constructor\n     * @version 1.0\n     */\n    function IFAnnot"
  },
  {
    "path": "src/infinity/paint/bitmap.js",
    "chars": 26488,
    "preview": "(function (_) {\n    /**\n     * A class representing a bitmap\n     * @param {Number|Image|HTMLImageElement|IFPaintCanvas|"
  },
  {
    "path": "src/infinity/paint/color.js",
    "chars": 32859,
    "preview": "(function (_) {\n    /**\n     * A class representing a color\n     * @param {IFColor.Type} type\n     * @param {Array<Numbe"
  },
  {
    "path": "src/infinity/paint/colorprofile.js",
    "chars": 2322,
    "preview": "(function (_) {\n    /**\n     * A class representing a color profile\n     * @class IFColorProfile\n     * @constructor\n   "
  },
  {
    "path": "src/infinity/paint/colorspace.js",
    "chars": 222,
    "preview": "(function (_) {\n    /**\n     * @enum\n     */\n    var IFColorSpace = {\n        None: 'n',\n        RGB: 'rgb',\n        CMY"
  },
  {
    "path": "src/infinity/paint/dirtylist.js",
    "chars": 13381,
    "preview": "(function (_) {\n\n    /**\n     * A general-purpose data structure for holding a list of rectangular\n     * regions that n"
  },
  {
    "path": "src/infinity/paint/font.js",
    "chars": 10360,
    "preview": "(function (_) {\n\n    /**\n     * @class IFFont\n     * @constructor\n     */\n    function IFFont() {\n        this._types = "
  },
  {
    "path": "src/infinity/paint/gradient.js",
    "chars": 5194,
    "preview": "(function (_) {\n    /**\n     * A class representing a color gradient\n     * @class IFGradient\n     * @extends IFPattern\n"
  },
  {
    "path": "src/infinity/paint/paintcanvas.js",
    "chars": 35912,
    "preview": "(function (_) {\n    /**\n     * A canvas wrapper to paint onto\n     * @class IFPaintCanvas\n     * @extends IFObject\n     "
  },
  {
    "path": "src/infinity/paint/paintconfiguration.js",
    "chars": 408,
    "preview": "(function (_) {\n    /**\n     * A class representing the configuration for painting\n     * @class IFPaintConfiguration\n  "
  },
  {
    "path": "src/infinity/paint/paintcontext.js",
    "chars": 2320,
    "preview": "(function (_) {\n    /**\n     * A class representing a context for painting\n     * @class IFPaintContext\n     * @construc"
  },
  {
    "path": "src/infinity/paint/pattern.js",
    "chars": 3070,
    "preview": "(function (_) {\n    var SVG_CHESSBOARD_CSS_URL = 'url(\"data:image/svg+xml;base64,' +\n        btoa('<svg xmlns=\"http://ww"
  },
  {
    "path": "src/infinity/platform.js",
    "chars": 7901,
    "preview": "(function (_) {\n    /**\n     * Instance of a platform implementation\n     * @class GUIPlatform\n     * @extends IFObject\n"
  },
  {
    "path": "src/infinity/scene/block.js",
    "chars": 6037,
    "preview": "(function (_) {\n    /**\n     * A block element that supports properties and storage\n     * @class IFBlock\n     * @extend"
  },
  {
    "path": "src/infinity/scene/element.js",
    "chars": 48190,
    "preview": "(function (_) {\n    /**\n     * An element represent an elementary node within a scene, something like a layer,\n     * a "
  },
  {
    "path": "src/infinity/scene/item.js",
    "chars": 276,
    "preview": "(function (_) {\n    /**\n     * The base for items like shapes and groups\n     * @class IFItem\n     * @extends IFBlock\n  "
  },
  {
    "path": "src/infinity/scene/node.js",
    "chars": 60971,
    "preview": "(function (_) {\n    /**\n     * Base node representing a single item within a scene\n     * @class IFNode\n     * @extends "
  },
  {
    "path": "src/infinity/scene/scene.js",
    "chars": 23854,
    "preview": "(function (_) {\n    /**\n     * A scene covers all graphical resources\n     * @class IFScene\n     * @extends IFElement\n  "
  },
  {
    "path": "src/infinity/scene/scenepaintconfiguration.js",
    "chars": 5075,
    "preview": "(function (_) {\n    /**\n     * A paint configuration for model painting\n     * @class IFScenePaintConfiguration\n     * @"
  },
  {
    "path": "src/infinity/scene/selector.js",
    "chars": 26053,
    "preview": "/**\n * IFSelector - Selector Engine for GNodes\n * Based on\n * Sly v1.0rc2 <http://sly.digitarald.com> - (C) 2009 Harald "
  },
  {
    "path": "src/infinity/scene/shape/ellipse.js",
    "chars": 5942,
    "preview": "(function (_) {\n\n    /**\n     * An ellipse shape\n     * @class IFEllipse\n     * @extends IFPathBase\n     * @constructor\n"
  },
  {
    "path": "src/infinity/scene/shape/image.js",
    "chars": 11186,
    "preview": "(function (_) {\n\n    /**\n     * A raster image shape\n     * @class IFImage\n     * @extends IFShape\n     * @constructor\n "
  },
  {
    "path": "src/infinity/scene/shape/path.js",
    "chars": 13782,
    "preview": "(function (_) {\n\n    /**\n     * A path shape\n     * @class IFPath\n     * @extends IFPathBase\n     * @constructor\n     */"
  },
  {
    "path": "src/infinity/scene/shape/pathbase.js",
    "chars": 66577,
    "preview": "(function (_) {\n\n    /**\n     * The base for all path based shapes\n     * @class IFPathBase\n     * @extends IFShape\n    "
  },
  {
    "path": "src/infinity/scene/shape/polygon.js",
    "chars": 5287,
    "preview": "(function (_) {\n\n    /**\n     * A polygon shape\n     * @class IFPolygon\n     * @extends IFPathBase\n     * @constructor\n "
  },
  {
    "path": "src/infinity/scene/shape/rectangle.js",
    "chars": 11133,
    "preview": "(function (_) {\n\n    /**\n     * A rectangle shape\n     * @class IFRectangle\n     * @extends IFPathBase\n     * @construct"
  },
  {
    "path": "src/infinity/scene/shape/shape.js",
    "chars": 11873,
    "preview": "(function (_) {\n\n    /**\n     * A base geometry based on vertices which is transformable and styleable\n     * and may co"
  }
]

// ... and 166 more files (download for full content)

About this extraction

This page contains the full source code of the OliBridgman/gravit GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 366 files (2.6 MB), approximately 690.1k tokens, and a symbol index with 444 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!