markup.push({

	"TileView": {
		nsClass: "Farrago.IBTileView", 
		gxDraggable: true, 
		selectable: true,
		multipleSelection: true,
		axIsIgnored: false,
		gxMaintainSelectionOnContextMenu: true,
		gxSelectOnContextMenu: true,
		axRoleDescription: "Audio Tile",
        axTitleKeyPath : "title",
		axDescriptionKeyPath : "axDescription",
		axHelpKeyPath  : "axHelp",
		gxToolTipKeyPath: "title",
		gxDragController: { nsClass: "Farrago.IBDragTileController" },
		gxMenuItemsKeyPath: "contextualMenuItems", 
		gxLayout: [
			`V:[self(${Constants.tileHeight}@600)]`,
		],
		backLayer: makeLayers({foreground: false}), // Built with function
		foreLayer: makeLayers({foreground: true}), // Built with function
        "hotKeyLayer": {
            nsClass: "GXDrawLayer",
            gxValueKeyPath: "hotKey",
			gxShowOnKey: "hasHotKey",
			gxShowSpeedSeconds: 0.1,
            gxLayerZPosition: 40,
            gxFrameRect: "18,53,28,15",  // moved by code
			"gxContentsGravity": "center",
			"gxSublayers": {
                "stopped": { "+" : "tileStopped",
                },
                "playing": { "+" : "tilePlaying",
                },
                "pressed" : { "+" : "tilePressed",
                },
            },
        },
        "midiNoteLayer": {
            nsClass: "GXDrawLayer",
            gxValueKeyPath: "hasMidi",
            gxLayerZPosition: 30,
            gxFrameRect: "18,53,28,15",  // moved by code
			"gxContentsGravity": "center",
            "gxSublayers": {
                "disabled": {
                    nsClass: "GXStringLayer",
                    gxValueKeyPath: "midiResponder.midiNoteValue",
                    gxShowOnKey: "showMidiNoteDisabled",
                    gxFrameRect: "0,0,28,15",
					gxExtraPainter: {
						"+": "LabelFore.centerx.size12",
						gxFontColor:"name:pillTextDisabledTile",
					},
                    gxForePainter: {
                        "+": "ImagePainter.midiPill.disabled",
                        gxTintColor: "name:pillBackgroundDisabledTile",
                    },
					gxBackPainter: {
						nsClass: "GXRoundRectPainter",
                        gxEdgeInsets: "0,1,0,1",
						gxCornerRadii: "2, 2, 2, 2",
						gxFrameColor: "name:backgroundTile",
						gxFillColor: "name:backgroundTile",
					},
                },
                "stopped": { "+" : "tileStopped",
                    gxValueKeyPath: "midiResponder.midiNoteValue",
                    gxShowOnKey: "showMidiNoteStopped",
                },
                "playing": { "+" : "tilePlaying",
                    gxValueKeyPath: "midiResponder.midiNoteValue",
                    gxShowOnKey: "showMidiNotePlayingEnabled",
                },
                "pressed" : { "+" : "tilePressed",
                    gxValueKeyPath: "midiResponder.midiNoteValue",
                    gxShowOnKey: "showMidiNotePressed",
                },
            },
        },
        "selectionLayer": "selectionLayer", // this name is referenced in code
		"queueLayer": {
			"+" : "selectionLayer",
			gxShowOnSelection: false,
			gxHideSpeedSeconds: 0.0,
			gxShowSpeedSeconds: 0.5,
			gxShowOnKey: "showQueued",
			gxBackPainter: {
				"+" : "selectionLayerBack",
				gxFrameColor: "name:tertiary",
			}
		}, // this name is referenced in code
		"notesBackLayer": // this name is referenced in code
        {
            nsClass: "GXDrawLayer",
            gxFrameRect: `0,0,100,${Constants.tileHeight}`,    // initial size sublayers are relative to
            gxLayerZPosition: 11,
			gxNeedsDisplayOnResize: true,
			drawsSelected: true,
			gxValueKeyPath: "",
			"gxContentsGravity": "bottomLeft",  // actually top left to any sane person
            gxBackPainter: {
                nsClass: "GXRoundRectPainter",
                gxEdgeInsets: "4, 4, 4, 3",
                gxCornerRadii: "2, 2, 2, 2",
                gxFrameColor: "name:secondaryBluish",
				"selected" : {
					gxStrokeWidth: 2,
					gxFrameColor: "name:tileTint",
				},
            },
        },
		"notesDashLayer": // this name is referenced in code
		{
			nsClass: "GXDrawLayer",
			gxFrameRect: "0,0,13,2", // initial size sublayers are relative to
			gxNeedsDisplayOnResize: false,          // don't redraw when stretched
			gxValueKeyPath : "",
			gxLayerZPosition: 11,
			drawsSelected: true,
			gxBackPainter: {
				nsClass: "GXPainter",
				gxFillColor: "name:secondaryBluish",
				gxEdgeInsets: "0,0.5,0,0.5",
				"selected" : {
					gxFillColor: "name:tileTint",
					gxEdgeInsets: "0,0,0,0",
				},
			},
		},
		"notesMeasurementPainter":    // this name is referenced in code
		{
			"+": "LabelFore.left.top.size125",  // should match notesLayer forepainter above
            gxLineBreakMode: 0,    // word wrap
		},
        "notesViewContainer": // this name is referenced in code
        {
			nsClass: "GXDrawView",
			gxValueKeyPath: "",
			gxChildViews: {
				"notesView" : {
					"+" : "TextView",
					gxFrameRect: `0,0,100,${Constants.tileHeight}`,    // initial size sublayers are relative to
					gxLayout: [
						"H:|-0-[self]-0-|",
						"V:|-0-[self]-0-|",
					],
					gxAcceptableDragTypes: ["NSPasteboardTypeString"],
					gxTextContainerAlignment:"{-2,1}",
					gxEditable: true,
					gxValueKeyPath : "notes",
					drawsSelected: true,
					gxBackPainter: null,
					gxFocusRingInsets: "1,1,1,1",
					gxPlaceholderString: "Click to add note...",
					gxPlaceholderTextPainter: {
						"+" : "TextFieldFore",
						gxFontColor: "gray:150,100",
					},
					gxTextPainter: {
						"+" : "TextFieldFore",
						gxBackColor: "name:boardBackground",
					},
				}
			}
        },
		"playStateLayer": { // this name is referenced in code
			nsClass: "GXDrawLayer",
			gxFrameRect: "0,32,12,12", // initial size sublayers are relative to
			gxNeedsDisplayOnResize: false,          // don't redraw when stretched
			gxShowSpeedSeconds: 0.3,
			gxValueKeyPath : "",
			gxShowOnKey: "showPlayState",
			gxLayerZPosition: 0,
			gxStylesKeyPath: "playStateStyle",
			gxBackPainter: {
				nsClass: "GXRoundRectPainter",
				gxCornerRadius: 6,
				gxFillColor: "name:hiliteActive",
				"played" : {
					gxFillColor: "name:boardBackground",
					gxStrokeWidth: 1,
					gxFrameColor: "name:tertiary",
				}, 
			},
		},
		"playingSpinner" : {
			nsClass: "GXSpinnerView",
			gxValueKeyPath: "",
			drawsSelected: true,
			gxAnimateOnKey: "playSpinnerActive",
			gxShowWhenStopped: true,
			gxFrameRect: "0,32,12,12", // initial size sublayers are relative to
			gxSpinAngleScale: 640, // Match speed of system spinner
			gxSpinLayer: {
				nsClass: "GXDrawLayer",
				gxLayerZPosition: 1,
				gxBackPainter: {
					nsClass: "GXRoundRectPainter",
					gxCornerRadius: 6,
					gxFillColor: "name:boardBackground",
				},
				gxForePainter: {
					"+" : "ImagePainter.playingSpinner",
					gxTintColor: "name:hiliteActive",
				},
				gxFrameRect: "0,0,12,12",
				gxAnchorPoint: "0.5, 0.5",	// spin around center
				gxOpacity: 0,	// start invisible
			},
		},
		"disabledOverlay" : {
			nsClass: "GXDrawLayer",
			gxLayerZPosition: 100,
			"gxResizeMask": 2,
			gxFrameRect: "0,0,60,75", 
			gxBackPainter: 	{
				nsClass: "GXRoundRectPainter",
				gxEdgeInsets: "2,2,2,2",
				gxCornerRadius: 6,
				gxFillColor: "name:boardBackgroundAlpha50",
			},
		}
	},

	"GhostTileView": {
		nsClass: "Farrago.IBGhostView",
        selectable: true,
        multipleSelection: true,
        axIsIgnored: false,
		axRoleDescription: "Tile",
		axTitleKeyPath: "title",
		gxLayout: [
			`V:[self(${Constants.tileHeight})]`,
		],
		"backLayer": {
			nsClass: "GXDrawLayer",
			gxFrameRect: `0,0,60,${Constants.tileHeight}`,	// initial size sublayers are relative to
            "gxSetLayerContentImage": true,         // make an image for layer content
            gxNeedsDisplayOnResize: false,          // don't redraw when stretched
            gxContentsCenter: "0.5, 0.5, 0, 0",     // resize by stretching center pixel h and v
			"gxContentsGravity": "resize",  // resize content
            gxLayerZPosition: 0,
			gxBackPainter: 	{
				nsClass: "GXRoundRectPainter",
				gxEdgeInsets: "7, 4, 7, 5",
				gxCornerRadii: "5, 5, 5, 5",
				gxStrokeWidth: 0.5,
				gxFrameColor: "name:secondaryBluish",
				gxFillColor: "name:boardBackground",
                gxDefaultSize: "40,40",        // size to make content image
			},
			"gxSublayers": {
				"hotKey": {
					nsClass: "GXStringLayer",
					gxLayerZPosition: 5,
					gxFrameRect: "15,53,28,15",
					"gxContentsGravity": "center",
					gxForePainter: {
						"+": "LabelFore.bottom.centerx.size13",
                        gxFontColor: "name:secondaryBluish",
						gxBackColor: "name:boardBackground",
					},
					gxNeedsDisplayOnResize: false,
				},
			},
		},
	},
		 
	"DropTileView": {
		nsClass: "Farrago.IBTileView",
		gxDragController: { nsClass: "Farrago.IBDragTileController" },
		selectable: true,
		multipleSelection: true,
		axIsIgnored: false,
		axRoleDescription: "Tile",
		axTitleKeyPath: "title",
        gxLayerZPosition: 50,
		gxLayout: [
			`V:[self(${Constants.tileHeight})]`,
		],
		"backLayer": {
			nsClass: "GXDrawLayer",
			gxFrameRect: `0,0,60,${Constants.tileHeight}`,	// initial size sublayers are relative to
			gxBackPainter: 	{
				nsClass: "GXRoundRectPainter",
				gxEdgeInsets: "2, 2, 2, 2",
				gxCornerRadii: "5, 5, 5, 5",
				gxStrokeWidth: 2,
				gxFrameColor: "name:dropTileOutline",
				gxFillColor: "name:boardBackground",
                gxDefaultSize: "40,40",        // size to make content image
			},
            "gxSetLayerContentImage": true,         // make an image for layer content
            gxNeedsDisplayOnResize: false,          // don't redraw when stretched
            gxContentsCenter: "0.5, 0.5, 0, 0",     // resize by stretching center pixel h and v
			"gxContentsGravity": "resize",  // resize content
			gxLayerZPosition: 0,
			"gxSublayers": {
				"title": {
					nsClass: "GXStringLayer",
					gxValueKeyPath: "title",
					gxLayerZPosition: 3,
					gxFrameRect: "9,5,42,15",
					"gxResizeMask": 2,
					gxNeedsDisplayOnResize: false,
					gxForePainter: {
						"+": "LabelFore.left.centery.bold.size12",
						gxFontColor: "name:dropTileOutline",
					},
				},
				"titleSmall": {
					nsClass: "GXStringLayer",
					gxValueKeyPath: "title",
					gxLayerZPosition: 2,
					gxFrameRect: "9,5,42,15",
					"gxResizeMask": 2,
					gxNeedsDisplayOnResize: false,
					gxForePainter: {
						"+": "LabelFore.left.centery.medium.size10",
						gxFontColor: "name:dropTileOutline",
					},
				},
			},
		}, 
        "hotKeyLayer": {
            nsClass: "GXStringLayer",
            gxValueKeyPath: "hotKey",
            gxLayerZPosition: 30,
            gxFrameRect: "18,53,28,15",  // moved by code
			"gxContentsGravity": "center",
			gxShowOnKey: "hasHotKey",
            gxBackPainter: {
                nsClass: "GXRoundRectPainter",
                gxCornerRadii: "2, 2, 2, 2",
                gxEdgeInsets: "0,1,0,1",
                gxFrameColor: "name:dropTileOutline",
                gxFillColor: "name:dropTileOutline", 
            },
            gxForePainter: {
                "+": "LabelFore.centerx.size12",
                gxFontColor: "name:boardBackground",
            },
            gxNeedsDisplayOnResize: false,
        },
		"selectionLayer" : "selectionLayer", // this name is referenced in code
		"loadingSpinner" : {
			nsClass: "GXSpinnerView",
			gxValueKeyPath: "",
			gxLayerZPosition: 2,
			gxAnimateOnKey: "hasSpinner",
			gxStepSize: 30, // Rotate 30 degree increments
			gxSpinAngleScale: 640, // Match speed of system spinner
			gxFrameRect: "0,0,30,30",
			gxShowOnKey: "hasSpinner",
			gxSpinLayer: {
				nsClass: "GXDrawLayer",
				gxForePainter: {
					"+" : "ImagePainter.spinner",
					gxTintColor: "name:secondary",
				},
				gxFrameRect: "0,0,30,30",
				gxAnchorPoint: "0.5, 0.5",	// spin around center
				gxOpacity: 0,	// start invisible
			},
		},
	},
	"selectionLayer":
	{
		nsClass: "GXDrawLayer",
		gxFrameRect: `0,0,60,${Constants.tileHeight}`,    // initial size sublayers are relative to
		gxValueKeyPath: "",
		gxShowOnSelection: true,
		"gxSetLayerContentImage": true,         // make an image for layer content
		gxNeedsDisplayOnResize: false,          // don't redraw when stretched
		gxContentsCenter: "0.5, 0.5, 0, 0",     // resize by stretching center pixel h and v
		"gxContentsGravity": "resize",  // resize content
		gxLayerZPosition: 15,
		gxBackPainter: "selectionLayerBack"
	},
	"selectionLayerBack" :
	{
		nsClass: "GXRoundRectPainter",
		gxEdgeInsets: "2, 2, 2, 2",
		gxCornerRadii: "6, 6, 6, 6",
		gxStrokeWidth: 2,
		gxFrameColor: "name:tileTint",
		gxDefaultSize: "40,40",        // size to make content image
	},
	"tileStopped": {
        nsClass: "GXStringLayer",
        gxValueKeyPath: "hotKey",
        gxFrameRect: "0,0,28,15",
        gxBackPainter: {
            nsClass: "GXRoundRectPainter",
            gxCornerRadii: "2, 2, 2, 2",
			gxEdgeInsets: "0,1,0,1",
			gxStrokeWidth: 1,
            gxFrameColor: "name:backgroundTile",
            gxFillColor: "name:tileTintMedium",
        },
        gxForePainter: {
            "+": "LabelFore.centerx.size12",
            gxFontColor: "name:tileTintDark",
        },
    },
	"tilePlaying": {
        nsClass: "GXStringLayer",
        gxValueKeyPath: "hotKey",
        gxShowOnKey: "playing",
        gxLayerZPosition: 5,
        gxFrameRect: "0,0,28,15",
        gxBackPainter: {
            nsClass: "GXRoundRectPainter",
            gxCornerRadii: "2, 2, 2, 2",
            gxEdgeInsets: "0,1,0,1",
            gxFrameColor: "name:backgroundTile",
            gxFillColor: "name:tileTint",
        },
        gxForePainter: {
            "+": "LabelFore.centerx.size12",
            gxFontColor: "name:backgroundTile",
        },
    },
	"tilePressed": {
        nsClass: "GXStringLayer",
        gxValueKeyPath: "hotKey",
        gxShowOnKey: "pressed",
        gxLayerZPosition: 5,
        gxFrameRect: "0,0,28,15",
        gxBackPainter: {
            nsClass: "GXRoundRectPainter",
            gxCornerRadii: "2, 2, 2, 2",
            gxEdgeInsets: "0,1,0,1",
            gxFrameColor: "name:backgroundTile",
            gxFillColor: "name:tileTintDark",
        },
        gxForePainter: {
            "+": "LabelFore.centerx.size12",
            gxFontColor: "name:backgroundTile"
        },
    },
			
	"ErrorLayer": {
		nsClass: "GXStringLayer",
		gxDefaultValue: Constants.warningEmoji,
		gxLayerZPosition: 10,
		gxFrameRect: "0,0,23,20",
		gxValueKeyPath: "",
		gxForePainter: {
			"+": "LabelFore.left.size20",
			gxFontColor: "name:black",
		},
	},
});

// MARK: Function for building the mostly duplicated foreground and background layers

function makeLayers({foreground})
{
	
	let textBackColor = {
		gxFontColor: foreground ? "name:backgroundTile" : "name:tileTint",
		gxBackColor: foreground ? "name:tileTint" : "name:backgroundTile",
	}
	
	return {
		nsClass: "GXDrawLayer",
            gxValueKeyPath: "",                         // receive update for tint color changes
			gxFrameRect: `0,0,60,${Constants.tileHeight}`,	// initial size sublayers are relative to
			gxBackPainter: 	{
				nsClass: "GXRoundRectPainter",
				gxEdgeInsets: "4, 4, 4, 4",
				gxCornerRadii: "3, 3, 3, 3",
				gxFrameColor: foreground ? "name:tileTint" : "name:backgroundTile",
				gxFillColor: foreground ? "name:tileTint" : "name:backgroundTile",
                gxDefaultSize: "40,40",        // the size of content image
			},
            "gxSetLayerContentImage": true,         // make an image for layer content
            gxNeedsDisplayOnResize: false,          // don't redraw when stretched
			gxContentsCenter: "0.5, 0.5, 0, 0",     // resize by stretching center pixel h and v
			"gxContentsGravity": "resize",  // resize content
			gxLayerZPosition: foreground ? 20 : 10,
			gxSublayers: {
				"wave": {
					nsClass: "GXWaveLayer",
					gxValueKeyPath: "shapedWaveData",
					"trimWaveform": true,   // fill the tile with wave between start and end points
					gxFrameRect: "4,21,52,32",    // initial size sublayers are relative to
					"gxResizeMask": 2,
					gxNeedsDisplayOnResize: false,
					gxBackPainter: {
						nsClass: "GXWavePainter",
						gxFillColor: foreground ? "name:backgroundTile" : "name:tileTintMedium",
					},
				},
				"title": {
					nsClass: "GXStringLayer",
					gxHideSpeedSeconds: 0.1,
					gxShowSpeedSeconds: 0.1,
					gxValueKeyPath: "title",
					gxLayerZPosition: 2,
					gxFrameRect: "9,4,70,15",
					"gxResizeMask": 0,
					gxNeedsDisplayOnResize: false,
					gxForePainter: merge({
						"+": "LabelFore.left.centery.bold.size12",
					}, textBackColor),
				},
				"titleSmall": {
					nsClass: "GXStringLayer",
					gxHideSpeedSeconds: 0.1,
					gxShowSpeedSeconds: 0.1,
					gxValueKeyPath: "title",
					gxLayerZPosition: 2,
					gxFrameRect: "9,6,70,15",
					"gxResizeMask": 0,
					gxNeedsDisplayOnResize: false,
					gxForePainter: merge({
						"+": "LabelFore.left.centery.medium.size95",
						gxFontKerning: -0.5,
					}, textBackColor),
				},
				"leftReadout": {
					nsClass: "GXStringLayer",
					gxValueKeyPath: "currentTimeString",
					gxAnimateOnKey: "playing",
					gxLayerZPosition: 2,
					gxHideSpeedSeconds: 0.1,
					gxShowSpeedSeconds: 0.1,
					gxFrameRect: "9,55,41,14",
					"gxResizeMask": 4,
					gxForePainter: merge({
						"+": "LabelFore.left.size10",
					}, textBackColor)
				},
				"rightReadout": {
					nsClass: "GXStringLayer",
					gxValueKeyPath: "remainingTimeString",
					gxAnimateOnKey: "playing",
					gxLayerZPosition: 2,
					gxFrameRect: "10,55,41,14",
					"gxResizeMask": 1,
					gxDefaultValue: "+++",
					gxForePainter: merge({
						"+": "LabelFore.right.size10",
					}, textBackColor)
				},
				"loopIcon" : {
					nsClass: "GXDrawLayer",
					gxFrameRect: "37,5,14,14",
					"gxResizeMask": 1,
					gxValueKeyPath: "", 
					gxLayerZPosition: 3,
					gxForePainter: {
						"+" : "ImagePainter.icon-loop",
						gxTintColor: foreground ? "name:backgroundTile" : "name:tileTintDark",
					}
				},
				"errorLayer": {
					"+" : "ErrorLayer",
					gxShowOnKey: "hasError"
				},
			}
	};
};
