[
  {
    "path": ".eslintrc",
    "content": "{\n    \"ecmaFeatures\": {\n        \"modules\": true,\n        \"experimentalObjectRestSpread\": true\n    },\n    \"rules\": {\n        \"accessor-pairs\": 0,\n        \"comma-dangle\": [1, \"never\"],\n        \"comma-spacing\": [1, {\"before\": false, \"after\": true}],\n        \"comma-style\": [2, \"last\"],\n        \"consistent-return\": 2,\n        \"dot-location\": [\n            2,\n            \"property\"\n        ],\n        \"dot-notation\": 2,\n        \"eol-last\": 2,\n        \"indent\": [\n            2,\n            2,\n            {\n                \"SwitchCase\": 1\n            }\n        ],\n        \"keyword-spacing\": 2,\n        \"no-bitwise\": 0,\n        \"no-console\": 2,\n        \"no-const-assign\": 2,\n        \"no-debugger\": 2,\n        \"no-empty\": 2,\n        'no-missing-import': 0,\n        \"no-multi-spaces\": 2,\n        \"no-shadow\": 0,\n        \"no-undef\": 2,\n        \"no-unreachable\": 1,\n        \"no-unused-expressions\": 2,\n        \"no-unused-vars\": [\n            2,\n            {\n                \"args\": \"none\",\n                \"varsIgnorePattern\": \"_\",\n                \"argsIgnorePattern\": \"_\"\n            }\n        ],\n        \"object-curly-spacing\": [1, \"never\"],\n        \"quotes\": [\n            1,\n            \"single\",\n            \"avoid-escape\"\n        ],\n        \"semi\": 2,\n        \"space-before-blocks\": 2,\n        \"space-in-parens\": [1, \"never\"],\n        \"strict\": [\n            2,\n            \"never\"\n        ]\n    },\n    \"env\": {\n        \"es6\": true\n    },\n  \"globals\": {\n      Alert: true,\n      Label: true,\n      PopUpButton: true,\n      TextField: true,\n      createConfirmHandler: true,\n      createPluginHandler: true,\n      lowLevelSetImage: true,\n      NSAlert: true,\n      NSView: true,\n      NSMakeRect: true,\n      NSImage: true,\n      NSURL: true,\n      MSImageData: true,\n      NSTextField: true,\n      NSPopUpButton: true,\n      log: true\n  }\n}\n"
  },
  {
    "path": ".gitignore",
    "content": "npm-debug.log\nnode_modules/\nDay Player.sketchplugin\nDayPlayer.zip\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: node_js\nnode_js:\n- 5.12.0\nsudo: false\ncache:\n  directories:\n  - /home/travis/.local\n  - node_modules\n  - $(npm config get cache)\nbefore_install:\n- npm config set progress false\n- npm config set spin false\ninstall:\n- pip install --user -U awscli\n- npm install\nscript:\n- make lint\ndeploy:\n- provider: script\n  script: make publish\n  on:\n    tags: true\nenv:\n  global:\n  - AWS_ACCESS_KEY_ID=AKIAIRMRYU635FO4D26A\n  - secure: PqKc8FFSOiIQvEMkQtPv9b55NwdMreHEF4njCnnk/pcBgUIep8Ew2JYmdp/KuSx1gyZBRys0hJqSTJ6FVZrCkI7pHD3bkKrNGq807c5yVFHtTzTUoQZ62zYEC/fnsbvKdd6sGtznABB1boiuiV1Crj0v2gtkA/kUJ431XUFqBw4=\n"
  },
  {
    "path": "LICENSE.md",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2016 Tyler Gaw\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "Makefile",
    "content": "PLUGIN_PATH := ~/Library/Application\\ Support/com.bohemiancoding.sketch3/Plugins/\nPLUGIN_DIR := Day\\ Player.sketchplugin\nPLUGIN_NAME := Day Player\nZIP_NAME := DayPlayer.zip\nS3_BUCKET := s3://day-player\n\nSRC_DIR := ./src\nRESOURCES_DIR := ./resources\nLICENSE := LICENSE.md\nMANIFEST := manifest.json\n\nclean:\n\t@rm -rf $(PLUGIN_DIR)\n\t@rm -f $(ZIP_NAME)\n\t@rm -rf $(PLUGIN_PATH)$(PLUGIN_DIR)\n\nbuild:\n\t@make clean\n\t@mkdir $(PLUGIN_DIR)\n\t@mkdir $(PLUGIN_DIR)/Contents\n\t@cp $(LICENSE) $(PLUGIN_DIR)/Contents/$(LICENSE)\n\t@cp -r $(RESOURCES_DIR) $(PLUGIN_DIR)/Contents/Resources\n\t@cp -r $(SRC_DIR) $(PLUGIN_DIR)/Contents/Sketch\n\t@cp -r $(MANIFEST) $(PLUGIN_DIR)/Contents/Sketch/$(MANIFEST)\n\ninstall:\n\t@make clean\n\t@echo \"Installing $(PLUGIN_NAME)...\"\n\t@make build\n\t@mv $(PLUGIN_DIR) $(PLUGIN_PATH)\n\t@echo \"$(PLUGIN_NAME) installed\"\n\npackage:\n\tmake build\n\t@zip -rm $(ZIP_NAME) $(PLUGIN_DIR)\n\t@echo \"$(ZIP_NAME) created\"\n\nchanged:\n\t@echo \"Changes detected...\"\n\t@make install\n\nlint:\n\t@echo \"Linting with eslint...\"\n\t@./node_modules/.bin/eslint ./src/**/*.js\n\nwatch:\n\t@echo \"Watching src directory for changes...\"\n\t@./node_modules/.bin/watch 'make changed' ./src ./resources\n\nrelease:\n\t@node scripts/release.js\n\npublish:\n\t@make package\n\t@aws s3 cp $(ZIP_NAME) $(S3_BUCKET)/releases/DayPlayer-$(TRAVIS_TAG).zip\n"
  },
  {
    "path": "README.md",
    "content": "[![Day Player Download](https://d3vv6lp55qjaqc.cloudfront.net/items/1u3H0M0L1j281F0R2E39/dayplayer-sketch.png) Download the latest version (3.0.0)](http://day-player.s3-website-us-east-1.amazonaws.com/releases/DayPlayer-3.0.0.zip)\n\n## Day Player [![Build Status](https://travis-ci.org/tylergaw/day-player.svg)](https://travis-ci.org/tylergaw/day-player)\nA Sketch Plugin for creating placeholder images from online services.\n\n## Installation\n\n- [ Download the latest version (3.0.0)](http://day-player.s3-website-us-east-1.amazonaws.com/releases/DayPlayer-3.0.0.zip)\n- Unzip the file\n- Double-click Day Player.sketchplugin to install\n\n## What Does It Do?\nIt allows you to insert customized placeholder images into any Sketch document from a number\nof different placeholder image services:\n\n - [unsplash.it](http://unsplash.it/)\n - [placehold.it](http://placehold.it/)\n - [fillmurray.com](http://www.fillmurray.com/)\n - [placecage.com](http://www.placecage.com/)\n - [placekitten.com](http://placekitten.com/)\n\n## How to use it\n - Open a new or existing Sketch document\n - Plugins > Day Player > Service...\n - Update the options to your liking, OK/Enter\n - The image is created on the canvas\n\n![Animated gif showing basic Day Player usage](https://d3vv6lp55qjaqc.cloudfront.net/items/1q2S3E2B333G2m382A1v/Screen%20Recording%202016-11-13%20at%2001.52%20PM.gif)\n\n### Appending images to an Artboard or Group\n- Open a new or existing Sketch document\n- Select the Artboard or Group\n- Plugins > Day Player > Service...\n- Update the options to your liking, OK/Enter\n- The image is created within the Artboard or Group\n- Image will be placed in the top left corner of the Artboard or Group\n\n**Artboard:**\n\n![Animated gif showing Day Player usage on Artboard](https://d3vv6lp55qjaqc.cloudfront.net/items/2P1n0t0H1o0y0E1J1I3t/Screen%20Recording%202016-11-13%20at%2001.58%20PM.gif)\n\n**Group:**\n\n![Animated gif showing Day Player usage on Group](https://d3vv6lp55qjaqc.cloudfront.net/items/1y2c3Z3m3K2F0b1T2720/Screen%20Recording%202016-11-13%20at%2003.20%20PM.gif)\n\n### Creating images with dimensions and position of existing Layers\n- Open a new or existing Sketch document\n- Select the desired Layer\n- Plugins > Day Player > Service...\n- Update the options to your liking, OK/Enter\n- The image is created in the parent group of the selected layer\n- Image will inherit the x, y, width, and height of the selected layer\n\n![Animated gif showing Day Player existing layer usage](https://d3vv6lp55qjaqc.cloudfront.net/items/0o1M3n07223o223D2C3R/Screen%20Recording%202016-11-13%20at%2003.23%20PM.gif)\n\n### Advanced Service Options\n\nAll services offer width, height, and black & white / color options. Unsplash.it and Placehold.it offer further options to customize the placeholder images.\n\n**Unsplash.it:**\n\n![Animated gif showing Day Player Unsplash.it usage](https://d3vv6lp55qjaqc.cloudfront.net/items/3a1g161P1S0r0J2g030Y/Screen%20Recording%202016-11-13%20at%2002.15%20PM.gif)\n\n**Placehold.it:**\n\n![Animated gif showing Day Player Placehold.it usage](https://d3vv6lp55qjaqc.cloudfront.net/items/2h0x3M1S250N1i1g081M/Screen%20Recording%202016-11-13%20at%2003.26%20PM.gif)\n\n-------\n\n## Contributing to this project\n\nAs with most open source projects, pull requests for bug fixes, and new functionality are always welcome.\n\nPrerequisites\n\n- Node `5.x.x`+\n\nFork this repo and clone a local copy of your fork.\n\nInstall dependencies:\n\n```\nnpm install\n```\n\nCreate necessary application bundle from source by running:\n\n```\nmake install\n```\n\nWatch the `src` and `resources` directories and recompile when changes are made by running:\n\n```\nmake watch\n```\n\n`make install` and `make watch` will copy the application bundle to the default Sketch plugins location `~/Library/Application Support/com.bohemiancoding.sketch3/Plugins/` as `Day Player.sketchplugin`.\n\nSee the `Makefile` for further details on the build process.\n"
  },
  {
    "path": "manifest.json",
    "content": "{\n  \"name\": \"Day Player\",\n  \"description\": \"A Plugin for creating images from placeholder services.\",\n  \"author\": \"Tyler Gaw\",\n  \"homepage\": \"https://github.com/tylergaw/day-player\",\n  \"version\": \"3.0.0\",\n  \"identifier\": \"com.sketchapp.tylergaw.day-player\",\n  \"compatibleVersion\": 41,\n  \"commands\" : [\n    {\n      \"name\": \"Unsplash.it\",\n      \"script\": \"index.cocoascript\",\n      \"handler\": \"unsplashIt\",\n      \"identifier\": \"unsplash-it\"\n    },\n    {\n      \"name\": \"Fill Murray\",\n      \"script\": \"index.cocoascript\",\n      \"handler\": \"fillMurray\",\n      \"identifier\": \"fill-murray\"\n    },\n    {\n      \"name\": \"Place Cage\",\n      \"script\": \"index.cocoascript\",\n      \"handler\": \"placeCage\",\n      \"identifier\": \"place-cage\"\n    },\n    {\n      \"name\": \"Placehold.it\",\n      \"script\": \"index.cocoascript\",\n      \"handler\": \"placeHoldIt\",\n      \"identifier\": \"place-hold-it\"\n    },\n    {\n      \"name\": \"Placekitten\",\n      \"script\": \"index.cocoascript\",\n      \"handler\": \"placeKitten\",\n      \"identifier\": \"place-kitten\"\n    }\n  ],\n  \"menu\": {\n    \"title\": \"Day Player\",\n    \"items\": [\n      \"unsplash-it\",\n      \"place-hold-it\",\n      \"fill-murray\",\n      \"place-cage\",\n      \"place-kitten\"\n    ]\n  }\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"day-player-plugin-sketchapp\",\n  \"version\": \"3.0.0\",\n  \"description\": \"A Sketch Plugin for creating placeholder images\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/tylergaw/day-player.git\"\n  },\n  \"scripts\": {\n    \"test\": \"make lint\"\n  },\n  \"keywords\": [\n    \"sketch\",\n    \"placeholder\",\n    \"images\"\n  ],\n  \"author\": \"Tyler Gaw\",\n  \"license\": \"MIT\",\n  \"bugs\": {\n    \"url\": \"https://github.com/tylergaw/day-player/issues\"\n  },\n  \"homepage\": \"https://github.com/tylergaw/day-player#readme\",\n  \"devDependencies\": {\n    \"eslint\": \"3.8.0\",\n    \"pre-commit\": \"1.1.3\",\n    \"prompt\": \"1.0.0\",\n    \"watch\": \"1.0.1\"\n  }\n}\n"
  },
  {
    "path": "scripts/release.js",
    "content": "/* eslint-env node */\n/* eslint-disable strict, no-console */\n'use strict';\n\nconst version = require('../package.json').version;\nconst prompt = require('prompt');\nconst exec = require('child_process').exec;\nconst dryRun = process.env.DRY_RUN || false;\nconst schema = {\n  properties: {\n    confirmation: {\n      required: true,\n      pattern: /^(y|n|yes|no)+$/ig,\n      description: `You are about to release version ${version}, is that OK? (yes|no)`\n    }\n  }\n};\n\nconst pushTag = tag => {\n  const cmd = `git push origin ${tag}`;\n\n  exec(cmd, (error, stdout, stderr) => {\n    console.log('Tag pushed to origin', tag);\n    if (error !== null) {\n      console.log(stderr);\n    }\n  });\n};\n\nconst createTag = n => {\n  const cmd = `git tag -a ${n} -m \"Releasing version: ${n}\"`;\n\n  if (dryRun) {\n    console.log('Pretending to create new tag', n);\n  } else {\n    exec(cmd, (error, stdout, stderr) => {\n      console.log('New git tag created', n);\n      pushTag(n);\n      if (error !== null) {\n        console.log(stderr);\n      }\n    });\n  }\n};\n\nprompt.start();\nprompt.get(schema, (err, result) => {\n  if (err) {\n    throw new Error(err);\n  }\n\n  const res = result.confirmation.toLowerCase();\n\n  if (res === 'y' || res === 'yes') {\n    createTag(version);\n  } else {\n    console.log('Release aborted');\n    process.exit(0);\n  }\n\n  return true;\n});\n"
  },
  {
    "path": "src/components/Alert.js",
    "content": "/**\n * Alert A facade for NSAlert. Includes a number of convenience methods and a\n * chainable interface.\n *\n * @param {Object} props Options for building the Alert and handling user input\n * @return {Object} alert\n */\n// eslint-disable-next-line no-unused-vars\nconst Alert = function(props) {\n  // eslint-disable-next-line no-unused-vars\n  const propTypes = {\n    message: String,\n    info: String,\n    iconUrl: String,\n    onConfirm: Function,\n    onCancel: Function\n  };\n\n  const alert = {\n    views: [],\n    el: NSAlert.alloc().init(),\n    is: function(type) {\n      return type === 'alert';\n    }\n  };\n\n  const buttons = props.buttons || ['OK', 'Cancel'];\n\n  alert.layout = function() {\n    var containerHeight = 1;\n    const container = NSView.alloc().initWithFrame(\n      NSMakeRect(25, 100, 350, containerHeight)\n    );\n\n    alert.views.forEach(function(view) {\n      const el = view.el;\n      const bounds = el.bounds();\n      bounds.origin.y = containerHeight;\n      containerHeight += bounds.size.height + 8;\n      el.setFrame(bounds);\n      container.addSubview(el);\n    });\n\n    const containerFrame = container.frame();\n    containerFrame.size.height = containerHeight;\n    container.setFrame(containerFrame);\n\n    alert.el.setAccessoryView(container);\n  };\n\n  alert.runModal = function() {\n    // Call layout before running opening the modal to account for any\n    // dynamic layout\n    alert.layout();\n\n    const onConfirm = props.onConfirm || function() {};\n    const onCancel = props.onCancel || function() {};\n    const res = parseInt(alert.el.runModal(), 10);\n    const resHandler = (res === 1000) ? onConfirm : onCancel;\n    resHandler(alert);\n\n    return alert;\n  };\n\n  /**\n   * append Adds an element or elements to the Alert\n   *\n   * @param {Object|Array} newEl Either a single element or an array of elements.\n   * @return {Object} alert\n   */\n  alert.append = function(newEl) {\n    const newViews = Array.isArray(newEl) ? newEl.reverse() : [newEl];\n\n    newViews.forEach(function(el) {\n      alert.views.push(el);\n    });\n\n    return alert;\n  };\n\n  if (buttons.length) {\n    for (var i = 0; i < buttons.length; i += 1) {\n      alert.el.addButtonWithTitle(buttons[i]);\n    }\n  }\n\n  if (props.message) {\n    alert.el.setMessageText(props.message);\n  }\n\n  if (props.info) {\n    alert.el.setInformativeText(props.info);\n  }\n\n  if (props.iconUrl) {\n    alert.el.setIcon(NSImage.alloc().initByReferencingURL(props.iconUrl));\n  }\n\n  return alert;\n};\n"
  },
  {
    "path": "src/components/Label.js",
    "content": "/**\n * Label A facade for NSTextField without any editable styling. Includes a\n * number of convenience methods and a chainable interface.\n *\n * @param {Object} props Options for building the Label\n * @return {Object} label\n */\n// eslint-disable-next-line no-unused-vars\nconst Label = function(props) {\n  // eslint-disable-next-line no-unused-vars\n  const propTypes = {\n    frame: Object,\n    value: String\n  };\n\n  /**\n   * createLabel Internal method for creating new NSTextField\n   *\n   * @return {Object} NSTextField\n   */\n  const createLabel = function(frame) {\n    const f = frame || {\n      x: 4,\n      y: 100,\n      width: 350,\n      height: 16\n    };\n\n    const textField = NSTextField.alloc().initWithFrame(\n      NSMakeRect(f.x, f.y, f.width, f.height));\n\n    textField.setDrawsBackground(false);\n    textField.setEditable(false);\n    textField.setBezeled(false);\n    textField.setSelectable(true);\n\n    return textField;\n  };\n\n  const label = {\n    el: createLabel(props.frame),\n    is: function(type) {\n      return type === 'label';\n    }\n  };\n\n  /**\n   * setStringValue Sets the string value for the label.\n   *\n   * @param {String} value\n   * @return {Object} label\n   */\n  label.setStringValue = function(value) {\n    label.el.setStringValue(value);\n    return label;\n  };\n\n  if (props.value) {\n    label.setStringValue(props.value);\n  }\n\n  return label;\n};\n"
  },
  {
    "path": "src/components/PopUpButton.js",
    "content": "/**\n * PopUpButton A facade for NSPopUpButton. Includes a number of convenience\n * methods and a chainable interface.\n *\n * @param {Object} props Options for building the PopUpButton\n * @return {Object} popUpButton\n */\n// eslint-disable-next-line no-unused-vars\nconst PopUpButton = function(props) {\n  // eslint-disable-next-line no-unused-vars\n  const propTypes = {\n    items: Array\n  };\n\n  const popUpButton = {\n    // NOTE: The documented signature is initWithFrame:pullsDown: but that\n    // is undefined here. Think that's maybe a difference in CocoaScript?\n    el: NSPopUpButton.alloc().initWithFrame(\n      NSMakeRect(25, 100, 350, 28)\n    ),\n    is: function(type) {\n      return type === 'select';\n    },\n    name: props.name || '',\n    val: function() {\n      return this.el.titleOfSelectedItem();\n    }\n  };\n\n  if (props.items) {\n    popUpButton.el.addItemsWithTitles(props.items);\n  }\n\n  return popUpButton;\n};\n"
  },
  {
    "path": "src/components/TextField.js",
    "content": "/**\n * TextField A facade for NSTextField. Includes a number of convenience\n * methods and a chainable interface.\n *\n * @param {Object} props Options for building the TextField\n * @return {Object} textField\n */\n// eslint-disable-next-line no-unused-vars\nconst TextField = function(props) {\n  // eslint-disable-next-line no-unused-vars\n  const propTypes = {\n    frame: Object,\n    value: String\n  };\n\n  /**\n   * createTextField Internal method for creating new NSTextField\n   *\n   * @return {Object} NSTextField\n   */\n  const createTextField = function(frame) {\n    const f = frame || {\n      x: 25,\n      y: 100,\n      width: 350,\n      height: 24\n    };\n\n    return NSTextField.alloc().initWithFrame(\n      NSMakeRect(f.x, f.y, f.width, f.height));\n  };\n\n  const textField = {\n    el: createTextField(props.frame),\n    is: function(type) {\n      return type === 'input';\n    },\n    name: props.name || '',\n    val: function() {\n      return this.el.stringValue();\n    }\n  };\n\n  /**\n   * setStringValue Sets the string value for the textField.\n   *\n   * @param {String} value\n   * @return {Object} textField\n   */\n  textField.setStringValue = function(value) {\n    textField.el.setStringValue(value);\n    return textField;\n  };\n\n  if (props.value) {\n    textField.setStringValue(props.value);\n  }\n\n  return textField;\n};\n"
  },
  {
    "path": "src/handlers/fillMurray.js",
    "content": "// eslint-disable-next-line no-unused-vars\nconst fillMurray = createPluginHandler(function(props) {\n  const GREY_TITLE = 'Black & white';\n  const initOpts = props.newImgFrame;\n\n  const elements = [\n    new Label({\n      value: 'Width:'\n    }),\n    new TextField({\n      name: 'width',\n      value: initOpts.width\n    }),\n    new Label({\n      value: 'Height:'\n    }),\n    new TextField({\n      name: 'height',\n      value: initOpts.height\n    }),\n    new Label({\n      value: 'Type:'\n    }),\n    new PopUpButton({\n      name: 'type',\n      items: ['Color', GREY_TITLE]\n    })\n  ];\n\n  const onConfirm = createConfirmHandler({\n    api: props.api,\n    group: props.target.group,\n    host: 'fillmurray.com',\n    initOpts: initOpts,\n    urlBuilder: function(parts) {\n      const base = `${parts.protocol}${parts.host}`;\n      // Cast as a string because the value coming back is an object\n      const type = (String(parts.allParts.type) === GREY_TITLE) ? '/g' : '';\n      return `${base}${type}/${parts.width}/${parts.height}`;\n    }\n  });\n\n  new Alert({\n    message: 'Fill Murray Options',\n    info: 'Customize the wonderful Bill Murray image that will be created.',\n    iconUrl: props.api.resourceNamed('fillmurray.icns'),\n    onConfirm: onConfirm\n  }).append(elements).runModal();\n});\n"
  },
  {
    "path": "src/handlers/placeCage.js",
    "content": "// eslint-disable-next-line no-unused-vars\nconst placeCage = createPluginHandler(function(props) {\n  const GREY_TITLE = 'Black & white';\n  const initOpts = props.newImgFrame;\n\n  const elements = [\n    new Label({\n      value: 'Width:'\n    }),\n    new TextField({\n      name: 'width',\n      value: initOpts.width\n    }),\n    new Label({\n      value: 'Height:'\n    }),\n    new TextField({\n      name: 'height',\n      value: initOpts.height\n    }),\n    new Label({\n      value: 'Type:'\n    }),\n    new PopUpButton({\n      name: 'type',\n      items: ['Color', GREY_TITLE]\n    })\n  ];\n\n  const onConfirm = createConfirmHandler({\n    api: props.api,\n    group: props.target.group,\n    host: 'placecage.com',\n    initOpts: initOpts,\n    urlBuilder: function(parts) {\n      const base = `${parts.protocol}${parts.host}`;\n      // Cast as a string because the value coming back is an object\n      const type = (String(parts.allParts.type) === GREY_TITLE) ? '/g' : '';\n      return `${base}${type}/${parts.width}/${parts.height}`;\n    }\n  });\n\n  new Alert({\n    message: 'Place Cage Options',\n    info: 'Customize the image of the best actor of all time that will be created.',\n    iconUrl: props.api.resourceNamed('placecage.icns'),\n    onConfirm: onConfirm\n  }).append(elements).runModal();\n});\n"
  },
  {
    "path": "src/handlers/placeHoldIt.js",
    "content": "// eslint-disable-next-line no-unused-vars\nconst placeHoldIt = createPluginHandler(function(props) {\n  const initOpts = props.newImgFrame;\n\n  const elements = [\n    new Label({\n      value: 'Width:'\n    }),\n    new TextField({\n      name: 'width',\n      value: initOpts.width\n    }),\n    new Label({\n      value: 'Height:'\n    }),\n    new TextField({\n      name: 'height',\n      value: initOpts.height\n    }),\n    new Label({\n      value: 'Text:'\n    }),\n    new TextField({\n      name: 'text',\n      value: 'placeholder'\n    }),\n    new Label({\n      value: 'Background color:'\n    }),\n    new TextField({\n      name: 'bgColor',\n      value: 'aeaeae'\n    }),\n    new Label({\n      value: 'Text color:'\n    }),\n    new TextField({\n      name: 'color',\n      value: '949494'\n    })\n  ];\n\n  const onConfirm = createConfirmHandler({\n    api: props.api,\n    group: props.target.group,\n    host: 'placehold.it',\n    initOpts: initOpts,\n    urlBuilder: function(parts) {\n      var url = `${parts.protocol}${parts.host}/${parts.width}x${parts.height}/${parts.allParts.bgColor}/${parts.allParts.color}`;\n      const text = parts.allParts.text;\n\n      if (text.length) {\n        url += `?text=${encodeURIComponent(text)}`;\n      }\n\n      return url;\n    }\n  });\n\n  new Alert({\n    message: 'Placehold.it Options',\n    info: 'Customize the image that will be created.',\n    iconUrl: props.api.resourceNamed('placeholdit.icns'),\n    onConfirm: onConfirm\n  }).append(elements).runModal();\n});\n"
  },
  {
    "path": "src/handlers/placeKitten.js",
    "content": "// eslint-disable-next-line no-unused-vars\nconst placeKitten = createPluginHandler(function(props) {\n  const GREY_TITLE = 'Black & white';\n  const initOpts = props.newImgFrame;\n\n  const elements = [\n    new Label({\n      value: 'Width:'\n    }),\n    new TextField({\n      name: 'width',\n      value: initOpts.width\n    }),\n    new Label({\n      value: 'Height:'\n    }),\n    new TextField({\n      name: 'height',\n      value: initOpts.height\n    }),\n    new Label({\n      value: 'Type:'\n    }),\n    new PopUpButton({\n      name: 'type',\n      items: ['Color', GREY_TITLE]\n    })\n  ];\n\n  const onConfirm = createConfirmHandler({\n    api: props.api,\n    group: props.target.group,\n    host: 'placekitten.com',\n    initOpts: initOpts,\n    urlBuilder: function(parts) {\n      const base = `${parts.protocol}${parts.host}`;\n      // Cast as a string because the value coming back is an object\n      const type = (String(parts.allParts.type) === GREY_TITLE) ? '/g' : '';\n      return `${base}${type}/${parts.width}/${parts.height}`;\n    }\n  });\n\n  new Alert({\n    message: 'Place Kitten Options',\n    info: 'Customize the kewl kitten image that will be created.',\n    iconUrl: props.api.resourceNamed('placekitten.icns'),\n    onConfirm: onConfirm\n  }).append(elements).runModal();\n});\n"
  },
  {
    "path": "src/handlers/unsplashIt.js",
    "content": "// eslint-disable-next-line no-unused-vars\nconst unsplashIt = createPluginHandler(function(props) {\n  const GREY_TITLE = 'Black & white';\n  const BLUR_TITLE = 'Blurry';\n  const initOpts = props.newImgFrame;\n\n  const elements = [\n    new Label({\n      value: 'Width:'\n    }),\n    new TextField({\n      name: 'width',\n      value: initOpts.width\n    }),\n    new Label({\n      value: 'Height:'\n    }),\n    new TextField({\n      name: 'height',\n      value: initOpts.height\n    }),\n    new Label({\n      value: 'Type:'\n    }),\n    new PopUpButton({\n      name: 'type',\n      items: ['Color', GREY_TITLE]\n    }),\n    new Label({\n      value: 'Sharpness:'\n    }),\n    new PopUpButton({\n      name: 'blur',\n      items: ['Focused', BLUR_TITLE]\n    })\n  ];\n\n  const onConfirm = createConfirmHandler({\n    api: props.api,\n    group: props.target.group,\n    host: 'unsplash.it',\n    initOpts: initOpts,\n    urlBuilder: function(parts) {\n      const base = `${parts.protocol}${parts.host}`;\n      // Cast as a string because the value coming back is an object\n      const type = (String(parts.allParts.type) === GREY_TITLE) ? '/g' : '';\n      const blur = (String(parts.allParts.blur) === BLUR_TITLE) ? '&blur' : '';\n      return `${base}${type}/${parts.width}/${parts.height}?random${blur}`;\n    }\n  });\n\n  new Alert({\n    message: 'Unsplash.it Options',\n    info: 'Customize the image that will be created.',\n    iconUrl: props.api.resourceNamed('unsplashit.icns'),\n    onConfirm: onConfirm\n  }).append(elements).runModal();\n});\n"
  },
  {
    "path": "src/index.cocoascript",
    "content": "@import './utils.js';\n@import './components/Label.js';\n@import './components/TextField.js';\n@import './components/PopUpButton.js';\n@import './components/Alert.js';\n@import './handlers/fillMurray.js';\n@import './handlers/placeCage.js';\n@import './handlers/placeHoldIt.js';\n@import './handlers/placeKitten.js';\n@import './handlers/unsplashIt.js';\n"
  },
  {
    "path": "src/utils.js",
    "content": "/**\n * createPluginHandler - Creates a handler function that takes the context\n * parameter required by Sketch plugins and enhances it with a number of\n * helpful properties.\n *\n * @param {Function} func\n * @return {Function} A function suitable to be used as a Plugin handler\n */\n// eslint-disable-next-line no-unused-vars\nconst createPluginHandler = function(func) {\n  return function(context) {\n    const api = context.api();\n    const document = api.selectedDocument;\n    const selection = document.selectedLayers;\n    const target = (selection.isEmpty) ? {group: document.selectedPage} :\n      getTargetLayer(selection, context);\n\n    const newImgFrame = (target.frame) ? {\n      x: target.frame.x,\n      y: target.frame.y,\n      width: target.frame.width,\n      height: target.frame.height\n    } : {\n      x: 0,\n      y: 0,\n      width: 400,\n      height: 300\n    };\n\n    const props = {\n      api: api,\n      document: document,\n      page: document.selectedPage,\n      selection: selection,\n      target: target,\n      newImgFrame: newImgFrame\n    };\n\n    func(props);\n  };\n};\n\n/**\n * createConfirmHandler - Creates a standard Day Player alert confirmation handler\n * function that takes a single param, the Alert being used at the time.\n *\n * @param {Object} props Configuration for the handler\n * @param {Function} func Optional function to run after all standard bits\n * @return {Function} A function suitable to be used as an Alert.onConfirm\n */\n// eslint-disable-next-line no-unused-vars\nconst createConfirmHandler = function(props, func) {\n  const urlBuilder = props.urlBuilder || function(parts) {\n    return `${parts.protocol}${parts.host}/${parts.width}/${parts.height}`;\n  };\n\n  return function(alert) {\n    const userChosenOptions = alert.views.filter(function(view) {\n      return view.is('input') || view.is('select');\n    }).reduce(function(obj, view, i) {\n      obj[view.name] = view.val();\n      return obj;\n    }, {});\n\n    const opts = Object.assign({}, props.initOpts, userChosenOptions);\n    const sizeDisplay = `${opts.width}x${opts.height}`;\n    props.api.message(`Creating a ${sizeDisplay}px image from ${props.host}...`);\n\n    const url = urlBuilder({\n      protocol: 'https://',\n      host: props.host,\n      width: opts.width,\n      height: opts.height,\n      allParts: opts\n    });\n\n    const img = props.group.newImage({\n      frame: new props.api.Rectangle(opts.x, opts.y, opts.width, opts.height),\n      name: `${props.host}-${sizeDisplay}`\n    });\n\n    img.imageURL = NSURL.URLWithString(url);\n  };\n};\n\n/**\n * Determine if appending image to an artboard, group, layer, or none.\n *\n * @param {Selection} selection The current Selection\n * @param {Object} context The current context\n * @return {Object} target The selected layers\n */\nconst getTargetLayer = function(selection, context) {\n  var layers = [];\n\n  selection.iterate(function(layer) {\n    layers.push(layer);\n  });\n\n  // TODO: Currently we'll only create an image for a single layer selected. We\n  // only grab the first one off the list of selections. In the future, we should\n  // create an image for each selection.\n  const firstLayer = layers[0];\n  \n  var target = {\n    selection: selection,\n    frame: (firstLayer.isShape) ? firstLayer.frame : null\n  };\n\n  if (firstLayer.isGroup) {\n    target.group = firstLayer;\n  } else {\n    // FIXME: If the user has selected layer(s) that do not count as a group;\n    // (shape, text, line, etc) we need to set the target.group to the parent\n    // group of the selected layer(s).\n    // The JS API does not currently offer a way to access the parentGroup of\n    // a Layer object. To get around this, we use the low-level _object and\n    // parentGroup() method.\n    // Doing so gives us an unwrapped Sketch Object. We must use wrapObject to\n    // ensure we return a wrapped object for the target.group.\n    // In the future, it would be good to have a Layer.parentGroup getter.\n    const api = context.api();\n    const document = api.selectedDocument;\n    target.group = api.wrapObject(firstLayer._object.parentGroup(), document);\n  }\n\n  return target;\n};\n\n/**\n * dumpObj - Introspect objects\n * @param {Object} obj\n */\n// eslint-disable-next-line no-unused-vars\nconst dumpObj = function(obj) {\n  log('------------------------');\n  log('## Dumping object ' + obj);\n  log('------------------------');\n  log('obj.properties:');\n  log(obj.class().mocha().properties());\n  log('obj.propertiesWithAncestors:');\n  log(obj.class().mocha().propertiesWithAncestors());\n  log('obj.classMethods:');\n  log(obj.class().mocha().classMethods());\n  log('obj.classMethodsWithAncestors:');\n  log(obj.class().mocha().classMethodsWithAncestors());\n  log('obj.instanceMethods:');\n  log(obj.class().mocha().instanceMethods());\n  log('obj.instanceMethodsWithAncestors:');\n  log(obj.class().mocha().instanceMethodsWithAncestors());\n  log('obj.protocols:');\n  log(obj.class().mocha().protocols());\n  log('obj.protocolsWithAncestors:');\n  log(obj.class().mocha().protocolsWithAncestors());\n  log('obj.treeAsDictionary():');\n  log(obj.treeAsDictionary());\n};\n"
  }
]