Repository: interconnectit/my-eyes-are-up-here
Branch: master
Commit: 268cec7f4d3a
Files: 16
Total size: 48.0 KB
Directory structure:
gitextract_s0unzbn_/
├── .gitignore
├── .jshintrc
├── Gruntfile.js
├── README.md
├── assets/
│ ├── css/
│ │ └── main.css
│ └── js/
│ └── main.js
├── bower.json
├── composer.json
├── includes/
│ ├── class-meauh-admin.php
│ ├── class-meauh-ajax.php
│ └── class-meauh-attachment.php
├── languages/
│ ├── meauh.pot
│ └── my-eyes-are-up-here.pot
├── my-eyes-are-up-here.php
├── package.json
└── readme.txt
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
.svn
bower_components
node_modules
npm-debug.log
================================================
FILE: .jshintrc
================================================
{
"bitwise": true,
"browser": true,
"curly": true,
"eqeqeq": true,
"eqnull": true,
"esnext": true,
"immed": true,
"jquery": true,
"latedef": true,
"newcap": true,
"noarg": true,
"node": true,
"strict": false,
"trailing": true
}
================================================
FILE: Gruntfile.js
================================================
'use strict';
module.exports = function (grunt) {
require('load-grunt-tasks')(grunt);
require('time-grunt')(grunt);
var jsFileList = [
'bower_components/jquery.facedetection/dist/jquery.facedetection.js',
'assets/js/main.js'
];
grunt.initConfig({
makepot: {
options: {
type: 'wp-plugin',
domainPath: 'languages',
potHeaders: {
'report-msgid-bugs-to': 'https://github.com/interconnectit/my-eyes-are-up-here/issues',
'language-team': 'LANGUAGE <EMAIL@ADDRESS>'
}
},
dist: {
options: {
potFilename: 'my-eyes-are-up-here.pot',
exclude: [
'assets/.*'
]
}
}
},
checktextdomain: {
options: {
text_domain: 'my-eyes-are-up-here',
keywords: [
'__:1,2d',
'_e:1,2d',
'_x:1,2c,3d',
'esc_html__:1,2d',
'esc_html_e:1,2d',
'esc_html_x:1,2c,3d',
'esc_attr__:1,2d',
'esc_attr_e:1,2d',
'esc_attr_x:1,2c,3d',
'_ex:1,2c,3d',
'_n:1,2,4d',
'_nx:1,2,4c,5d',
'_n_noop:1,2,3d',
'_nx_noop:1,2,3c,4d'
]
},
files: {
src: [
'**/*.php',
'!assets/**',
'!bower_components/**',
'!node_modules/**',
],
expand: true
}
},
autoprefixer: {
options: {
browsers: [
'last 2 versions',
'ie 8',
'ie 9',
'android 2.3',
'android 4',
'opera 12'
]
},
dist: {
src: 'assets/css/main.min.css'
}
},
cssmin: {
options: {
compatibility: 'ie8',
keepSpecialComments: '*',
noAdvanced: true
},
dist: {
files: [{
expand: true,
cwd: 'assets/css',
src: ['*.css', '!*.min.css'],
dest: 'assets/css',
ext: '.min.css'
}]
}
},
jshint: {
options: {
jshintrc: '.jshintrc'
},
all: [
'Gruntfile.js',
'assets/js/*.js',
'!assets/js/scripts.min.js'
]
},
uglify: {
options: {
preserveComments: 'some'
},
dist: {
src: jsFileList,
dest: 'assets/js/scripts.min.js'
}
},
});
grunt.registerTask('build', [
'makepot',
'autoprefixer',
'cssmin',
'jshint',
'uglify'
]);
};
================================================
FILE: README.md
================================================
My eyes are up here
===================
Face detection for generating cropped thumbnails in WordPress. Avoiding automatically generated crotch shots since 2013.
## Why would I want this?
Consider a common problem with automatically generated thumbnails in WordPress themes. You need an image of a precise width and height to fit into the design but you never know what images people are uploading.
You could control the width and height of the standard WP thumbnail sizes and let folks alter the crop as necessary themselves but if you have more than a few custom image sizes you can't alter the crop of those.
Let's say you have a portrait image of someone and your theme needs a landscape crop of the image. WP centers the crop so you'll get an image of the persons crotch... Not ideal. I assume.
This plugin detects faces in an image and centers the crop using an average of all the faces it finds.
```
Portrait image:
+-----------+
| |
| O |
| --|-- |
| | |
| | | |
| | | |
| |
+-----------+
Cropped landscape version with default WP cropping:
+-----------+
| --|-- |
| | |
| | | |
+-----------+
Cropped landscape version using this plugin:
+-----------+
| |
| O |
| --|-- |
+-----------+
```
================================================
FILE: assets/css/main.css
================================================
.face-detect-panel {
margin-bottom: 15px;
}
.face-detection-ui {
overflow: hidden;
}
.face-detection-image {
position: relative;
overflow: hidden;
float: left;
max-width: 100%;
}
.face-detection-image.active {
cursor: crosshair;
}
.face-detection-image img {
max-width: 100%;
vertical-align: middle;
height: auto;
width: auto;
}
.face-detection-image .hotspot {
position: absolute;
max-width: 150px;
min-width: 10px;
height: 0;
border: 0px solid #ccc;
border: solid 1px rgba(230, 230, 230, .85);
background: rgba(255, 155, 155, .5);
border-radius: 50%;
}
.hotspot.face {
background: rgba(155, 155, 255, .5);
}
.hotspot.normal {
cursor: pointer;
}
.face-detect-panel .button {
margin-right: 5px;
margin-bottom: 5px;
display: inline-block;
}
.status.loading {
padding-left: 20px;
background: url(../img/spin.gif) no-repeat left center;
background-size: contain;
}
.found-faces img,
.found-faces canvas {
position: static;
width: 40px;
height: auto;
margin: 10px 10px 0 0;
display: inline-block;
}
.post-thumbnail-preview {
background: #f5f5f5;
background-image: -webkit-gradient(linear, left bottom, left top, from(#f5f5f5), to(#fafafa));
background-image: -webkit-linear-gradient(bottom, #f5f5f5, #fafafa);
background-image: -moz-linear-gradient(bottom, #f5f5f5, #fafafa);
background-image: -o-linear-gradient(bottom, #f5f5f5, #fafafa);
background-image: linear-gradient(to top, #f5f5f5, #fafafa);
overflow: auto;
margin-bottom: 10px;
padding: 10px;
border: 1px solid #dfdfdf;
border-radius: 3px;
font-size: 12px;
line-height: 0.9;
width: 100%;
box-sizing: border-box;
}
.post-thumbnail-preview .preview-wrap {
display: inline-block;
margin-top: 5px;
margin-right: 5px;
background: url(../img/spin.gif) no-repeat center;
}
.post-thumbnail-preview .preview-wrap img {
max-width: 100%;
width: auto;
max-height: 50px;
height: auto;
}
.media-modal .compat-field-face_detection .label {
margin-right: 0;
}
.face-detect-large-hidden,
.face-detect-large-hidden-copy {
position: absolute;
left: 9999px;
top: -9999px;
}
================================================
FILE: assets/js/main.js
================================================
window.hotspots = {};
// prevent errors while logging to browsers that support it
if (!window.console) {
window.console = {
log: function () {
}
};
}
;(function ($) {
function Hotspots() {
var that = this;
$.extend(that, {
_construct: function () {
// bind behaviour to buttons
$(document).on('click', '.face-detection-activate', function () {
that.set_context(this);
that.get_image(that.detect_faces);
});
$(document).on('click', '.add-hotspots', function () {
that.set_context(this);
that.get_image(that.add_hotspots);
});
},
attachment_id: null,
el: null,
image: null,
hidden: null,
images: null,
$context: null,
$status_box: null,
hotspots: [],
faces: [],
set_context: function (el) {
that.el = el;
that.attachment_id = $(el).data('attachment-id');
that.$ui = $(el).parents('.face-detection-ui');
that.$context = $(el).parents('.face-detect-panel');
that.$status_box = that.$context.find('.status');
},
// request full image
get_image: function (callback) {
callback = callback || function () {
return false;
};
if (that.image && that.$ui.find('.face-detection-image img').length) {
return callback();
}
that.update_status('Loading full size image', true);
$.post(meauh.ajax_url, {
action: 'meauh_get_image',
nonce: meauh.get_image_nonce,
attachment_id: that.attachment_id
}, function (response) {
if (response.success) {
// set our image
that.image = new Image();
// save for later
that.images = response.data;
// set source to original uncropped image
that.image.src = response.data.original[0];
$(that.image)
.appendTo('.face-detection-image')
.on('load', function () {
that.update_status('Image loaded');
// add our large off-screen sampler for pixastic etc...
if (!$('.face-detect-large-hidden').length) {
$('body').append('<img class="face-detect-large-hidden" src="" alt="" />');
}
$('.face-detect-large-hidden').attr('src', response.data.original[0]);
// show current data
that.show_existing($('.face-detection-image').data('hotspots'));
that.show_existing($('.face-detection-image').data('faces'), 'face');
return callback();
});
}
}, 'json');
return false;
},
update_status: function (status, loading) {
loading = loading || false;
that.$status_box.html(status);
if (loading) {
that.$status_box.addClass('loading');
} else {
that.$status_box.removeClass('loading');
}
},
detect_faces: function () {
// Remove the previous copy, end up with one for every button press otherwise.
$('.face-detect-large-hidden-copy').remove();
var $found_box = that.$context.find('.found-faces'),
image = $('.face-detect-large-hidden').get(0),
image_copy = $(image)
.clone()
.removeClass('face-detect-large-hidden')
.addClass('face-detect-large-hidden-copy')
.appendTo('body')
.get(0);
if ($(that.el).hasClass('has-faces')) {
$(image_copy).remove();
//$found_box.html( '' );
$(that.el)
.removeClass('has-faces')
.html('Detect faces');
$('.face-detection-image')
.data('faces', '')
.find('.face')
.remove();
return that.save({faces: 0});
}
// face detection
return $(image_copy).faceDetection({
complete: function (faces) {
// update status - found faces
that.faces = faces;
if (!that.faces.length) {
that.update_status('No faces were found');
return;
}
// allow removal of found faces
$(that.el)
.addClass('has-faces')
.html('Forget found faces');
that.update_status('Found ' + that.faces.length + ' faces, re-cropping thumbnails', true);
that.show_existing(that.faces, 'face');
// cleanup
$(image_copy).remove();
// save data & regen
that.save({faces: that.faces});
},
error: function (img, code, message) {
// update status - error, message
console.log('error', message, img);
that.update_status('Error (' + code + '): ' + message);
}
});
},
show_existing: function (data, type) {
type = type || 'normal';
var width = $(that.image).width(),
correction = that.images.original[1] / width,
hotspot_width;
if ('undefined' !== typeof data && data.length) {
$.each(data, function (i, hotspot) {
that.add_hotspot({
x: (hotspot.x / correction),
y: (hotspot.y / correction),
width: hotspot.width / correction,
type: type
});
});
}
},
add_hotspots: function () {
var width = $(that.image).width(),
hotspot_width = width * 0.15,
correction = that.images.original[1] / width;
// activate hotspots
if (!$('.face-detection-image').hasClass('active')) {
// edit button
$(that.el)
.addClass('active')
.html('Finish adding hotspots');
that.$ui.find('button').not(that.el).attr('disabled', 'disabled');
that.update_status('Click on the image below to add hotspots. Clicking a hotspot will remove it.');
// bind hotspot toggling
$(that.image).on('click.hotspots', that.hotspot_click);
$('.face-detection-image').addClass('active');
// deactivate & save
} else {
// edit button
$(that.el)
.removeClass('active')
.html('Edit hotspots');
// remove hotspot toggling
$(that.image).off('click.hotspots');
that.hotspots = [];
$('.face-detection-image .hotspot').not('.face').each(function () {
that.hotspots.push({
width: Math.round($(this).width() * correction),
x: Math.round(( $(this).position().left ) * correction),
y: Math.round(( $(this).position().top ) * correction)
});
});
$('.face-detection-image').removeClass('active');
if (!that.hotspots.length) {
that.hotspots = 0;
}
// save data
that.save({hotspots: that.hotspots});
}
},
hotspot_click: function (e) {
var width = $(that.image).width(),
hotspot_maxwidth = 150,
hotspot_width = width * 0.15 > hotspot_maxwidth ? hotspot_maxwidth : width * 0.15,
hotspot_offset = hotspot_width / 2;
// Firefox doesn't do offsetX/Y so need to do something a little more complex
that.add_hotspot({
x: ( e.offsetX || e.clientX - ( $(e.target).offset().left - window.scrollX ) ) - hotspot_offset,
y: ( e.offsetY || e.clientY - ( $(e.target).offset().top - window.scrollY ) ) - hotspot_offset
});
},
add_hotspot: function (hotspot) {
var width = $(that.image).width(),
height = $(that.image).height(),
$parent = $('.face-detection-image'),
hotspot_maxwidth = 150,
hotspot_width = width * 0.15 > hotspot_maxwidth ? hotspot_maxwidth : width * 0.15;
hotspot = $.extend({
x: 0,
y: 0,
width: hotspot_width, // default 15% wide, max-width 120px
type: 'normal'
}, hotspot);
// Prevent hotspots from being placed outside edges of image.
hotspot.x = Math.max((0 - (hotspot.width / 2)), Math.min(hotspot.x, (width - (hotspot.width / 2))));
hotspot.y = Math.max((0 - (hotspot.width / 2)), Math.min(hotspot.y, (height - (hotspot.width / 2))));
$('<div class="hotspot ' + hotspot.type + '"></div>')
.css({
left: ( ( hotspot.x / width ) * 100 ) + '%',
top: ( ( hotspot.y / height ) * 100 ) + '%',
width: ( ( hotspot.width / width ) * 100 ) + '%',
paddingBottom: ( ( hotspot.width / width ) * 100 ) + '%'
})
.attr('title', hotspot.type === 'normal' ? 'Click to toggle on/off' : '')
.appendTo($parent)
.click(function () {
if (!$(this).hasClass('face') && $parent.hasClass('active')) {
$(this).remove();
}
});
},
// show a cropped thumbnail preview
preview: function () {
var $previews = $('.post-thumbnail-preview img'),
previews_length = $previews.length;
that.update_status('Updating preview', true);
$previews.each(function (i) {
if (!that.images[$(this).data('size')]) {
return;
}
$(this)
.fadeTo(300, 0.25)
.attr('src', that.images[$(this).data('size')][0] + '?t=' + new Date().getTime())
.on('load', function () {
$(this).fadeTo(300, 1);
if (i === previews_length - 1) {
that.update_status('');
}
});
});
},
save: function (data) {
that.update_status('Re-cropping thumbnails', true);
that.$ui.find('button').attr('disabled', 'disabled');
$.post(meauh.ajax_url, $.extend({
action: 'meauh_save_image',
nonce: meauh.save_image_nonce,
attachment_id: that.attachment_id
}, data), function (response) {
if (response.success) {
that.update_status('Thumbnails re-cropped');
$.extend(that.images, response.data.resized);
that.preview();
} else {
that.update_status('No thumbnails were re-cropped');
}
that.$ui.find('button').removeAttr('disabled');
}, 'json');
}
});
// initialise
that._construct();
return that;
}
// initialise
window.hotspots = new Hotspots();
}(jQuery));
================================================
FILE: bower.json
================================================
{
"name": "my-eyes-are-up-here",
"homepage": "https://interconnectit.com",
"authors": [
"Evgenii Nasyrov <evgenii@interconnectit.com>"
],
"license": "MIT",
"private": true,
"dependencies": {
"jquery.facedetection": "~2.0.2"
}
}
================================================
FILE: composer.json
================================================
{
"name": "interconnectit/my-eyes-are-up-here",
"type": "wordpress-plugin",
"license": "GPLv2",
"description": "My Eyes Are Up Here helps you control how WordPress generates thumbnails.",
"homepage": "https://github.com/interconnectit/my-eyes-are-up-here",
"authors": [
{
"name": "interconnect/it",
"email": "support@interconnectit.com",
"homepage": "https://github.com/interconnectit"
}
],
"keywords": [
"wordpress"
],
"support": {
"issues": "https://github.com/interconnectit/my-eyes-are-up-here/issues"
},
"require": {
"php": ">=5.4.0",
"composer/installers": "~1.0"
}
}
================================================
FILE: includes/class-meauh-admin.php
================================================
<?php
/**
* Admin
*
* @package my-eyes-are-up-here
* @author interconnect/it
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Class MEAUH_Admin
*/
class MEAUH_Admin {
/**
* Init
*/
public static function init() {
$self = new self;
add_action( 'admin_enqueue_scripts', array( $self, 'assets' ) );
}
/**
* Constructor
*/
public function __construct() {
$this->includes();
}
/**
* Assets
*/
public function assets() {
// Main script.
wp_enqueue_script(
'meauh-main',
meauh()->plugin_url() . '/assets/js/scripts.min.js',
array( 'jquery' ),
filemtime( meauh()->plugin_path() . '/assets/js/scripts.min.js' ),
true
);
// Main script variables.
wp_localize_script( 'meauh-main', 'meauh', array(
'ajax_url' => meauh()->ajax_url(),
'get_image_nonce' => wp_create_nonce( MEAUH_Ajax::NONCE_GET_IMAGE ),
'save_image_nonce' => wp_create_nonce( MEAUH_Ajax::NONCE_SAVE_IMAGE ),
) );
// Main style.
wp_enqueue_style(
'meauh-main',
meauh()->plugin_url() . '/assets/css/main.min.css',
array(),
filemtime( meauh()->plugin_path() . '/assets/css/main.min.css' ),
'all'
);
}
/**
* Includes
*/
protected function includes() {
}
}
MEAUH_Admin::init();
================================================
FILE: includes/class-meauh-ajax.php
================================================
<?php
/**
* Ajax
*
* @package my-eyes-are-up-here
* @author interconnect/it
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Class MEAUH_Ajax
*/
class MEAUH_Ajax {
const NONCE_GET_IMAGE = 'meauh-get-image';
const NONCE_SAVE_IMAGE = 'meauh-save-image';
/**
* Ajax events
*
* @var array
*/
protected static $events = array(
'get_image' => false,
'save_image' => false,
);
/**
* Init
*/
public static function init() {
$self = new self;
foreach ( self::$events as $event => $nopriv ) {
add_action( 'wp_ajax_meauh_' . $event, array( $self, $event ) );
if ( $nopriv ) {
add_action( 'wp_ajax_nopriv_meauh_' . $event, array( $self, $event ) );
}
}
add_action( 'wp_ajax_save-attachment-compat', 'save_image', 0, 1 );
}
/**
* Get an image
*/
public function get_image() {
check_ajax_referer( self::NONCE_GET_IMAGE, 'nonce' );
$attachment_id = isset( $_POST['attachment_id'] ) ?
absint( $_POST['attachment_id'] ) :
false;
if ( $attachment_id && $this->is_attachment( $attachment_id ) ) {
wp_send_json_success( array(
'original' => wp_get_attachment_image_src( $attachment_id, 'full' ),
) );
} else {
wp_send_json_error();
}
}
/**
* Save an image
*/
public function save_image() {
check_ajax_referer( self::NONCE_SAVE_IMAGE, 'nonce' );
$attachment_id = isset( $_POST['attachment_id'] ) ?
absint( $_POST['attachment_id'] ) :
false;
if ( ! $this->is_attachment( $attachment_id ) ) {
wp_send_json_error();
}
// WP Offload S3 Compatibility.
$this->as3cf_compatibility( $attachment_id );
// Save faces.
$this->save_image_faces( $attachment_id );
// Save hotspots.
$this->save_image_hotspots( $attachment_id );
// Regenerate thumbs.
$resized = MEAUH_Attachment::regenerate( $attachment_id );
if ( $resized ) {
wp_send_json_success( array(
'resized' => $resized,
) );
}
}
/**
* Is attachment
*
* @param int $attachment_id Attachment id.
*
* @return bool
*/
protected function is_attachment( $attachment_id ) {
return $attachment_id &&
get_post( $attachment_id ) &&
'attachment' === get_post_type( $attachment_id );
}
/**
* WP Offload S3 Compatibility
*
* @param $attachment_id Attachment ID.
*/
protected function as3cf_compatibility( $attachment_id ) {
if ( ! is_plugin_active( 'amazon-s3-and-cloudfront/wordpress-s3.php' ) ) {
return;
}
$file = get_attached_file( $attachment_id );
file_put_contents( $file, file_get_contents( $file ) );
}
/**
* Save image faces
*
* @param int $attachment_id Attachment ID.
*/
protected function save_image_faces( $attachment_id ) {
if ( ! empty( $_POST['faces'] ) ) {
update_post_meta(
$attachment_id,
'faces',
array_filter( $_POST['faces'], array( $this, 'filter' ) )
);
} else {
delete_post_meta( $attachment_id, 'faces' );
}
}
/**
* Save image hotspots
*
* @param int $attachment_id Attachment ID.
*/
protected function save_image_hotspots( $attachment_id ) {
if ( ! empty( $_POST['hotspots'] ) ) {
update_post_meta(
$attachment_id,
'hotspots',
array_filter( $_POST['hotspots'], array( $this, 'filter' ) )
);
} else {
delete_post_meta( $attachment_id, 'hotspots' );
}
}
/**
* Make sure we got solid data
*
* @param array $value Values to filter.
*
* @return array
*/
protected function filter( $value ) {
$allowed_keys = array(
'x',
'y',
'width',
);
$value = array_intersect_key( $value, array_flip( $allowed_keys ) );
if ( isset( $value['x'], $value['y'], $value['width'] ) ) {
return array_filter( $value, 'is_numeric' );
} else {
return array();
}
}
}
MEAUH_Ajax::init();
================================================
FILE: includes/class-meauh-attachment.php
================================================
<?php
/**
* Attachment
*
* @package my-eyes-are-up-here
* @author interconnect/it
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Class MEAUH_Attachment
*/
class MEAUH_Attachment {
/**
* Faces
*
* @var array
*/
protected $faces = array();
/**
* Hotspots
*
* @var array
*/
protected $hotspots = array();
/**
* Init
*/
public static function init() {
$self = new self;
// Current attachment data.
add_filter( 'get_attached_file', array( $self, 'set_attachment_id' ), 10, 2 );
add_filter( 'update_attached_file', array( $self, 'set_attachment_id' ), 10, 2 );
// Image resize dimensions.
add_filter( 'image_resize_dimensions', array( $self, 'crop' ), 11, 6 );
// Add button.
add_filter( 'attachment_fields_to_edit', array( $self, 'edit_fields' ), 10, 2 );
}
/**
* Regenerate thumbnails
*
* @param int $attachment_id Attachment id.
*
* @return array
*/
public static function regenerate( $attachment_id ) {
// 5 minutes per image should be PLENTY.
@set_time_limit( 5 * MINUTE_IN_SECONDS );
// Resize thumbnail.
$file = get_attached_file( $attachment_id );
$metadata = wp_generate_attachment_metadata( $attachment_id, $file );
if ( is_wp_error( $metadata ) ) {
return array( 'id' => $attachment_id, 'error' => $metadata->get_error_message() );
}
if ( empty( $metadata ) ) {
return array( 'id' => $attachment_id, 'error' => __( 'Unknown failure reason', 'my-eyes-are-up-here' ) );
}
// If this fails, then it just means that nothing was changed.
wp_update_attachment_metadata( $attachment_id, $metadata );
$sizes = self::get_cropped_sizes();
$resized = array();
foreach ( $sizes as $size => $atts ) {
$resized[ $size ] = wp_get_attachment_image_src( $attachment_id, $size );
}
return $resized;
}
/**
* Hacky use of attached_file filters to get current attachment ID being resized
* Used to store face location and dimensions
*
* @param string $file File name.
* @param int $attachment_id Attachment id.
*
* @return string
*/
public function set_attachment_id( $file, $attachment_id ) {
$this->faces = (array) get_post_meta( $attachment_id, 'faces', true );
$this->hotspots = (array) get_post_meta( $attachment_id, 'hotspots', true );
return $file;
}
/**
* Alters the crop location of the GD image editor class by detecting faces
* and centering the crop around them
*
* @param array $payload Payload.
* @param int $orig_w Original width.
* @param int $orig_h Original height.
* @param int $dest_w width.
* @param int $dest_h height.
* @param bool $crop Crop.
*
* @return array
*/
public function crop( $payload, $orig_w, $orig_h, $dest_w, $dest_h, $crop ) {
$hotspots = array_filter( array_merge( $this->faces, $this->hotspots ) );
if ( ! $crop || empty( $hotspots ) ) {
return $payload;
}
if ( is_array( $payload ) ) {
list( $dest_x, $dest_y, $src_x, $src_y, $new_w, $new_h, $src_w, $src_h ) = $payload;
}
// Get faces area.
$hotspot_src_x = $hotspot_src_y = PHP_INT_MAX;
$hotspot_src_max_x = $hotspot_src_max_w = 0;
$hotspot_src_max_y = $hotspot_src_max_h = 0;
// Create bounding box.
foreach ( $hotspots as $hotspot ) {
$hotspot = array_map( 'absint', $hotspot );
// Left and top most x,y.
if ( $hotspot_src_x > $hotspot['x'] ) {
$hotspot_src_x = $hotspot['x'];
}
if ( $hotspot_src_y > $hotspot['y'] ) {
$hotspot_src_y = $hotspot['y'];
}
// Right and bottom most x,y.
if ( $hotspot_src_max_x < $hotspot['x'] ) {
$hotspot_src_max_x = $hotspot['x'];
}
if ( $hotspot_src_max_y < $hotspot['y'] ) {
$hotspot_src_max_y = $hotspot['y'];
}
}
$hotspot_src_w = $hotspot_src_max_x - $hotspot_src_x;
$hotspot_src_h = $hotspot_src_max_y - $hotspot_src_y;
// Crop the largest possible portion of the original image that we can size to $dest_w x $dest_h.
$aspect_ratio = $orig_w / $orig_h;
// Preserve settings already filtered in.
if ( null === $payload ) {
$new_w = min( $dest_w, $orig_w );
$new_h = min( $dest_h, $orig_h );
if ( ! $new_w ) {
$new_w = intval( $new_h * $aspect_ratio );
}
if ( ! $new_h ) {
$new_h = intval( $new_w / $aspect_ratio );
}
}
$size_ratio = max( $new_w / $orig_w, $new_h / $orig_h );
$crop_w = round( $new_w / $size_ratio );
$crop_h = round( $new_h / $size_ratio );
$src_x = floor( ( $orig_w - $crop_w ) / 2 );
$src_y = floor( ( $orig_h - $crop_h ) / 2 );
// Bounding box.
if ( 0 == $src_x ) {
$src_y = ( $hotspot_src_y + $hotspot_src_h / 2 ) - $crop_h / 2;
$src_y = min( max( 0, $src_y ), $orig_h - $crop_h );
}
if ( 0 == $src_y ) {
$src_x = ( $hotspot_src_x + $hotspot_src_w / 2 ) - $crop_w / 2;
$src_x = min( max( 0, $src_x ), $orig_w - $crop_w );
}
return array( 0, 0, (int) $src_x, (int) $src_y, (int) $new_w, (int) $new_h, (int) $crop_w, (int) $crop_h );
}
/**
* Edit fields
*
* @param array $form_fields Form fields.
* @param stdClass $attachment Attachment.
*
* @return mixed
*/
public function edit_fields( array $form_fields, $attachment ) {
if ( ! wp_attachment_is_image( $attachment->ID ) ) {
return $form_fields;
}
$faces = get_post_meta( $attachment->ID, 'faces', true );
$hotspots = get_post_meta( $attachment->ID, 'hotspots', true );
$data_atts = '';
if ( $faces ) {
$data_atts .= ' data-faces="' . esc_attr( json_encode( $faces ) ) . '"';
}
if ( $hotspots ) {
$data_atts .= ' data-hotspots="' . esc_attr( json_encode( $hotspots ) ) . '"';
}
$button = '
<div class="face-detection-ui hide-if-no-js">
<div class="post-thumbnail-preview">
<div><strong>' . __( 'Thumb Previews', 'my-eyes-are-up-here' ) . '</strong></div>';
foreach ( self::get_cropped_sizes() as $size => $atts ) {
$src = wp_get_attachment_image_src( $attachment->ID, $size );
$button .= '<div class="preview-wrap"><img src="' . $src[0] . '?v=' . time() . '" alt="' . $size . '" data-size="' . $size . '"></div>';
}
$button .= '
</div>
<div class="face-detection face-detect-panel">';
if ( $faces ) {
$button .= sprintf( '<button class="button face-detection-activate has-faces" type="button" data-attachment-id="%d">%s</button>',
$attachment->ID,
__( 'Forget found faces', 'my-eyes-are-up-here' )
);
} else {
$button .= sprintf( '<button class="button face-detection-activate" type="button" data-attachment-id="%d">%s</button>',
$attachment->ID,
__( 'Detect faces', 'my-eyes-are-up-here' )
);
}
$button .= '<span class="status"></span>';
$button .= sprintf( '<p class="description">%s</p>',
__( "Please note this is basic face detection and won't find everything. Use hotspots to highlight any that were missed.",
'my-eyes-are-up-here' )
);
$button .= '<div class="found-faces"></div>';
if ( false && $faces ) {
$button .= '<p class="detected-faces">';
$button .= sprintf( __( '%d %s found, thumbnails regenerated to fit them into crop area.',
'my-eyes-are-up-here' ),
count( $faces ),
_n( 'face', 'faces', count( $faces ), 'my-eyes-are-up-here' )
);
$button .= '</p>';
}
$button .= '
</div>
<div class="image-hotspots face-detect-panel">';
if ( $hotspots ) {
$button .= sprintf( '<button class="button add-hotspots has-hotspots" type="button" data-attachment-id="%d">%s</button>',
$attachment->ID,
__( 'Edit hotspots', 'my-eyes-are-up-here' )
);
} else {
$button .= sprintf( '<button class="button add-hotspots" type="button" data-attachment-id="%d">%s</button>',
$attachment->ID,
__( 'Add hotspots', 'my-eyes-are-up-here' )
);
}
$button .= '<span class="status"></span>';
$button .= sprintf( '<p class="description">%s</p>',
__( 'Manually add hotspots that you want to avoid cropping.', 'my-eyes-are-up-here' )
);
if ( false && $hotspots ) {
$button .= '<p class="added-hotspots">';
$button .= sprintf( __( '%d %s found, thumbnails regenerated to fit them into crop area.',
'my-eyes-are-up-here' ),
count( $hotspots ),
_n( 'hotspot', 'hotspots', count( $hotspots ), 'my-eyes-are-up-here' )
);
$button .= '</p>';
}
$button .= '
</div>
<div class="face-detection-crop-preview"></div>
<div class="face-detection-image"' . $data_atts . '></div>
</div>
<div class="hide-if-js">
<p>' . __( 'This plugin requires javascript to work', 'my-eyes-are-up-here' ) . '</p>
</div>';
$form_fields['face_detection'] = array(
'label' => __( 'Face detection', 'my-eyes-are-up-here' ),
'input' => 'html',
'html' => $button,
);
return $form_fields;
}
/**
* Get cropped sizes
*
* @return array
*/
protected static function get_cropped_sizes() {
global $_wp_additional_image_sizes;
$sizes = array();
$size_names = get_intermediate_image_sizes();
foreach ( $size_names as $size ) {
if ( in_array( $size, array( 'thumbnail', 'medium', 'large' ) ) ) {
$width = intval( get_option( $size . '_size_w' ) );
$height = intval( get_option( $size . '_size_h' ) );
$crop = get_option( $size . '_crop' );
} else if ( isset( $_wp_additional_image_sizes[ $size ] ) ) {
$width = $_wp_additional_image_sizes[ $size ]['width'];
$height = $_wp_additional_image_sizes[ $size ]['height'];
$crop = $_wp_additional_image_sizes[ $size ]['crop'];
}
if ( $crop ) {
$sizes[ $size ] = array(
'width' => $width,
'height' => $height,
'crop' => $crop,
);
}
}
return $sizes;
}
}
MEAUH_Attachment::init();
================================================
FILE: languages/meauh.pot
================================================
# Copyright (C) 2016 interconnect/it
# This file is distributed under the same license as the My Eyes Are Up Here package.
msgid ""
msgstr ""
"Project-Id-Version: My Eyes Are Up Here 1.1.3\n"
"Report-Msgid-Bugs-To: "
"https://github.com/interconnectit/my-eyes-are-up-here/issues\n"
"POT-Creation-Date: 2016-04-01 11:05:34+00:00\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"PO-Revision-Date: 2016-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <EMAIL@ADDRESS>\n"
"X-Generator: grunt-wp-i18n 0.5.4\n"
#: includes/class-meauh-attachment.php:68
msgid "Unknown failure reason"
msgstr ""
#: includes/class-meauh-attachment.php:220
msgid "Thumb Previews"
msgstr ""
#: includes/class-meauh-attachment.php:234
msgid "Forget found faces"
msgstr ""
#: includes/class-meauh-attachment.php:239
msgid "Detect faces"
msgstr ""
#: includes/class-meauh-attachment.php:245
msgid ""
"Please note this is basic face detection and won't find everything. Use "
"hotspots to highlight any that were missed."
msgstr ""
#: includes/class-meauh-attachment.php:251
#: includes/class-meauh-attachment.php:281
msgid "%d %s found, thumbnails regenerated to fit them into crop area."
msgstr ""
#: includes/class-meauh-attachment.php:253
msgid "face"
msgid_plural "faces"
msgstr[0] ""
msgstr[1] ""
#: includes/class-meauh-attachment.php:265
msgid "Edit hotspots"
msgstr ""
#: includes/class-meauh-attachment.php:270
msgid "Add hotspots"
msgstr ""
#: includes/class-meauh-attachment.php:276
msgid "Manually add hotspots that you want to avoid cropping."
msgstr ""
#: includes/class-meauh-attachment.php:283
msgid "hotspot"
msgid_plural "hotspots"
msgstr[0] ""
msgstr[1] ""
#: includes/class-meauh-attachment.php:294
msgid "This plugin requires javascript to work"
msgstr ""
#: includes/class-meauh-attachment.php:298
msgid "Face detection"
msgstr ""
#. Plugin Name of the plugin/theme
msgid "My Eyes Are Up Here"
msgstr ""
#. Plugin URI of the plugin/theme
msgid "https://github.com/interconnectit/my-eyes-are-up-here"
msgstr ""
#. Description of the plugin/theme
msgid ""
"Detects faces during thumbnail cropping and moves the crop position "
"accordingly."
msgstr ""
#. Author of the plugin/theme
msgid "interconnect/it"
msgstr ""
#. Author URI of the plugin/theme
msgid "http://interconnectit.com"
msgstr ""
================================================
FILE: languages/my-eyes-are-up-here.pot
================================================
# Copyright (C) 2017 interconnect/it
# This file is distributed under the same license as the My Eyes Are Up Here package.
msgid ""
msgstr ""
"Project-Id-Version: My Eyes Are Up Here 1.1.8\n"
"Report-Msgid-Bugs-To: "
"https://github.com/interconnectit/my-eyes-are-up-here/issues\n"
"POT-Creation-Date: 2017-05-15 10:38:31+00:00\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"PO-Revision-Date: 2017-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <EMAIL@ADDRESS>\n"
"X-Generator: grunt-wp-i18n 0.5.4\n"
#: includes/class-meauh-attachment.php:68
msgid "Unknown failure reason"
msgstr ""
#: includes/class-meauh-attachment.php:220
msgid "Thumb Previews"
msgstr ""
#: includes/class-meauh-attachment.php:234
msgid "Forget found faces"
msgstr ""
#: includes/class-meauh-attachment.php:239
msgid "Detect faces"
msgstr ""
#: includes/class-meauh-attachment.php:245
msgid ""
"Please note this is basic face detection and won't find everything. Use "
"hotspots to highlight any that were missed."
msgstr ""
#: includes/class-meauh-attachment.php:252
#: includes/class-meauh-attachment.php:283
msgid "%d %s found, thumbnails regenerated to fit them into crop area."
msgstr ""
#: includes/class-meauh-attachment.php:255
msgid "face"
msgid_plural "faces"
msgstr[0] ""
msgstr[1] ""
#: includes/class-meauh-attachment.php:267
msgid "Edit hotspots"
msgstr ""
#: includes/class-meauh-attachment.php:272
msgid "Add hotspots"
msgstr ""
#: includes/class-meauh-attachment.php:278
msgid "Manually add hotspots that you want to avoid cropping."
msgstr ""
#: includes/class-meauh-attachment.php:286
msgid "hotspot"
msgid_plural "hotspots"
msgstr[0] ""
msgstr[1] ""
#: includes/class-meauh-attachment.php:297
msgid "This plugin requires javascript to work"
msgstr ""
#: includes/class-meauh-attachment.php:301
msgid "Face detection"
msgstr ""
#. Plugin Name of the plugin/theme
msgid "My Eyes Are Up Here"
msgstr ""
#. Plugin URI of the plugin/theme
msgid "https://github.com/interconnectit/my-eyes-are-up-here"
msgstr ""
#. Description of the plugin/theme
msgid ""
"Detects faces during thumbnail cropping and moves the crop position "
"accordingly."
msgstr ""
#. Author of the plugin/theme
msgid "interconnect/it"
msgstr ""
#. Author URI of the plugin/theme
msgid "http://interconnectit.com"
msgstr ""
================================================
FILE: my-eyes-are-up-here.php
================================================
<?php
/**
* Plugin Name: My Eyes Are Up Here
* Plugin URI: https://github.com/interconnectit/my-eyes-are-up-here
* Description: Detects faces during thumbnail cropping and moves the crop position accordingly.
* Version: 1.1.9
* Author: interconnect/it
* Author URI: http://interconnectit.com
*
* Text Domain: my-eyes-are-up-here
* Domain Path: /languages/
*
* @package my-eyes-are-up-here
* @author interconnect/it
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Class MyEyesAreUpHere
*/
final class MyEyesAreUpHere {
const REQUEST_ADMIN = 'admin';
const REQUEST_AJAX = 'ajax';
/**
* Instance
*
* @var MyEyesAreUpHere
*/
private static $_instance;
/**
* Get instance
*
* @return MyEyesAreUpHere
*/
public static function instance() {
if ( is_null( self::$_instance ) ) {
self::$_instance = new self;
}
return self::$_instance;
}
/**
* Constructor
*/
public function __construct() {
$this->includes();
$this->init_hooks();
}
/**
* Determine request type
*
* @param string $type Request type.
*
* @return bool
*/
public function is_request( $type ) {
switch ( $type ) {
case self::REQUEST_ADMIN:
return is_admin();
case self::REQUEST_AJAX:
return defined( 'DOING_AJAX' );
}
}
/**
* Get plugin path
*
* @return string
*/
public function plugin_path() {
return untrailingslashit( plugin_dir_path( __FILE__ ) );
}
/**
* Get plugin URL
*
* @return string
*/
public function plugin_url() {
return untrailingslashit( plugins_url( '/', __FILE__ ) );
}
/**
* Get ajax URL
*
* @return string
*/
public function ajax_url() {
return admin_url( 'admin-ajax.php', 'relative' );
}
/**
* Load localisation
*/
public function localisation() {
$locale = apply_filters( 'plugin_locale', get_locale(), 'my-eyes-are-up-here' );
load_textdomain( 'my-eyes-are-up-here', WP_LANG_DIR . '/my-eyes-are-up-here/my-eyes-are-up-here-' . $locale . '.mo' );
load_plugin_textdomain( 'my-eyes-are-up-here', false, plugin_basename( dirname( __FILE__ ) ) . '/languages' );
}
/**
* Includes
*/
protected function includes() {
require_once 'includes/class-meauh-ajax.php';
require_once 'includes/class-meauh-attachment.php';
if ( $this->is_request( self::REQUEST_ADMIN ) ) {
require_once 'includes/class-meauh-admin.php';
}
}
/**
* Init hooks
*/
protected function init_hooks() {
add_action( 'init', array( $this, 'localisation' ), 0 );
}
}
/**
* Get instance
*
* @return MyEyesAreUpHere
*/
function meauh() {
return MyEyesAreUpHere::instance();
}
// Global for backwards compatibility.
$GLOBALS['meauh'] = meauh();
================================================
FILE: package.json
================================================
{
"name": "my-eyes-are-up-here",
"version": "1.1.9",
"author": "interconnect/it <support@interconnectit.com>",
"homepage": "https://interconnectit.com",
"private": true,
"repository": {
"type": "git",
"url": "git://github.com/interconnectit/my-eyes-are-up-here.git"
},
"bugs": {
"url": "https://github.com/interconnectit/my-eyes-are-up-here/issues"
},
"licenses": [
{
"type": "GPLv2",
"url": "http://www.gnu.org/licenses/gpl-2.0.html"
}
],
"scripts": {
"postinstall": "bower install && grunt build"
},
"devDependencies": {
"bower": "^1.7.1",
"grunt": "^0.4.5",
"grunt-autoprefixer": "^3.0.3",
"grunt-contrib-cssmin": "^0.14.0",
"grunt-contrib-jshint": "^0.11.3",
"grunt-contrib-uglify": "^0.11.0",
"grunt-wp-i18n": "^0.5.4",
"load-grunt-tasks": "^3.3.0",
"time-grunt": "^1.2.2"
}
}
================================================
FILE: readme.txt
================================================
=== My Eyes Are Up Here ===
Contributors: interconnectit, sanchothefat, spectacula, AndyWalmsley
Donate link: https://myeyesareuphere.interconnectit.com/donate/
Tags: thumbnails, image editing, image, featured image
Requires at least: 3.8.1
Tested up to: 4.7.4
Stable tag: 1.1.9
License: GPLv2
License URI: http://www.gnu.org/licenses/gpl-2.0.html
My Eyes Are Up Here helps you control how WordPress generates thumbnails.
== Description ==
= What is it? =
A fantastic new plugin that helps you control how WordPress generates thumbnails.
= Why use it? =
When WordPress automatically generates thumbnails, it sometimes doesn't crop them in a way that is suitable for the image you've uploaded. If your image isn't the correct format, and let's face it, you never know what images people are uploading - you'll run the risk of a badly cropped image. Not good.
If you have a full portrait image of a person that you've uploaded, but you need the image to appear landscape, you're in trouble! WordPress will centre the image so that you end up with person's crotch. Not good. Or let's say you have a landscape image, with a person's face on the right hand side, but you need it to display in a square thumbnail. You'll end up with half a face as WordPress centres the image.
= What does the plugin do? =
You can control how you want your WordPress thumbnails to appear on your website. Regardless of the image format you upload, you can either use the automatic face detector or if you want even more control, you can manually add hotspots.
= How do I use it? =
Navigate to your media library then click on the image you want to edit. Use the detect faces or edit hotspots option to edit your image. You'll see thumbnail previews when you've applied these edits, when you're happy hit update. Simple.
== Installation ==
= The install =
1. You can install the plugin using the auto-install tool from the WordPress back-end.
2. To manually install, upload the folder `/myeyesareuphere/` to `/wp-content/plugins/` directory.
3. Activate the plugin through the 'Plugins' menu in WordPress
== Usage ==
1. Once the plugin is activated, navigate to your 'Media Library'.
2. Click on the image you want to edit to bring up your 'Edit Media' options.
3. You should now be able to see extra image editing options, below the 'Description' box.
4. By clicking 'Detect faces' or 'Add hotspots' you can now start to edit your image thumbnails.
5. If you click the 'Detect faces' button, it will centre the crop using an average of all the faces it finds.
5. Please note this is basic face detection and won't find everything.
6. You can click and create 1 or several hotspots to centre the crop of your thumbnails, if 'Detect faces' doesn't work.
7. If you're happy with your 'Thumb Previews' hit save, and you're done. Simple.
== Frequently Asked Questions ==
= What happens when there are multiple hotspots/faces detected? =
This will crop the image to get as many hotspots in the thumbnail as possible or crop around the center of the hotspots if not.
= How do I report a problem? =
You can email us at cases@interconnectit.fogbugz.com with "My Eyes Are Up Here" in the subject and the following information:
1. What browser and version is this problem occurring with?
2. What WordPress version are you using?
3. What version of My Eyes Are Up Here are you using?
4. Are there any errors in the javascript console?
* Chrome and Firefox: ctrl + shift + j (Win) or alt + cmd + j (Mac)
* Internet Explorer: F12 and click on 'Script' then 'Console'
5. What are the steps you used to produce this problem?
== Screenshots ==
1. Default WordPress cropping where the thumbnail is cropped to the centre of the image.
2. Thumbnail after My Eyes Are Up Here has detected any faces in the image.
3. How WordPress crops the image without My Eyes Are Up Here.
4. How the image appears once My Eyes Are Up Here has been installed and applied.
== Changelog ==
= 1.1.9 =
* Fix the js error for WP Customizer
= 1.1.8 =
* Remove deprecated jQuery methods
= 1.1.7 =
* Remove PHP warnings
* Add composer support
= 1.1.6 =
* Prevent hotspots from being placed outside edges of image
= 1.1.5 =
* AS3CF compatibility
= 1.1.4 =
* Fix translation file
= 1.1.3 =
* Fix text domain
= 1.1.2 =
* Add translation file
= 1.1.1 =
* Better ajax validation
* Fix regenerate thumbnail issue
= 1.1.0 =
* Complete plugin refactoring
= 1.0.2 =
* Fixed for versions 4.4.*
= 1.0.1 =
* Now only runs on image attachments
= 1.0 =
* Release version
= 0.4 =
* Bugfixes, play nicely with other plugins/themes that modify image sizes
= 0.3 =
* Hotspots!
= 0.2: =
* jQuery option for speed
== Upgrade Notice ==
= 1.0.1 =
* No longer runs on non image media
= 1.0 =
* Release version
= 0.4 =
* Lot's of bugfixes
gitextract_s0unzbn_/ ├── .gitignore ├── .jshintrc ├── Gruntfile.js ├── README.md ├── assets/ │ ├── css/ │ │ └── main.css │ └── js/ │ └── main.js ├── bower.json ├── composer.json ├── includes/ │ ├── class-meauh-admin.php │ ├── class-meauh-ajax.php │ └── class-meauh-attachment.php ├── languages/ │ ├── meauh.pot │ └── my-eyes-are-up-here.pot ├── my-eyes-are-up-here.php ├── package.json └── readme.txt
SYMBOL INDEX (33 symbols across 5 files)
FILE: assets/js/main.js
function Hotspots (line 13) | function Hotspots() {
FILE: includes/class-meauh-admin.php
class MEAUH_Admin (line 16) | class MEAUH_Admin {
method init (line 20) | public static function init() {
method __construct (line 29) | public function __construct() {
method assets (line 36) | public function assets() {
method includes (line 66) | protected function includes() {
FILE: includes/class-meauh-ajax.php
class MEAUH_Ajax (line 16) | class MEAUH_Ajax {
method init (line 33) | public static function init() {
method get_image (line 50) | public function get_image() {
method save_image (line 69) | public function save_image() {
method is_attachment (line 105) | protected function is_attachment( $attachment_id ) {
method as3cf_compatibility (line 116) | protected function as3cf_compatibility( $attachment_id ) {
method save_image_faces (line 131) | protected function save_image_faces( $attachment_id ) {
method save_image_hotspots (line 148) | protected function save_image_hotspots( $attachment_id ) {
method filter (line 167) | protected function filter( $value ) {
FILE: includes/class-meauh-attachment.php
class MEAUH_Attachment (line 16) | class MEAUH_Attachment {
method init (line 34) | public static function init() {
method regenerate (line 55) | public static function regenerate( $attachment_id ) {
method set_attachment_id (line 93) | public function set_attachment_id( $file, $attachment_id ) {
method crop (line 113) | public function crop( $payload, $orig_w, $orig_h, $dest_w, $dest_h, $c...
method edit_fields (line 201) | public function edit_fields( array $form_fields, $attachment ) {
method get_cropped_sizes (line 314) | protected static function get_cropped_sizes() {
FILE: my-eyes-are-up-here.php
class MyEyesAreUpHere (line 24) | final class MyEyesAreUpHere {
method instance (line 40) | public static function instance() {
method __construct (line 51) | public function __construct() {
method is_request (line 63) | public function is_request( $type ) {
method plugin_path (line 78) | public function plugin_path() {
method plugin_url (line 87) | public function plugin_url() {
method ajax_url (line 96) | public function ajax_url() {
method localisation (line 103) | public function localisation() {
method includes (line 113) | protected function includes() {
method init_hooks (line 125) | protected function init_hooks() {
function meauh (line 135) | function meauh() {
Condensed preview — 16 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (53K chars).
[
{
"path": ".gitignore",
"chars": 48,
"preview": ".svn\nbower_components\nnode_modules\nnpm-debug.log"
},
{
"path": ".jshintrc",
"chars": 256,
"preview": "{\n \"bitwise\": true,\n \"browser\": true,\n \"curly\": true,\n \"eqeqeq\": true,\n \"eqnull\": true,\n \"esnext\": true,\n \"immed\""
},
{
"path": "Gruntfile.js",
"chars": 3300,
"preview": "'use strict';\nmodule.exports = function (grunt) {\n require('load-grunt-tasks')(grunt);\n require('time-grunt')(grun"
},
{
"path": "README.md",
"chars": 1300,
"preview": "My eyes are up here\n===================\n\nFace detection for generating cropped thumbnails in WordPress. Avoiding automat"
},
{
"path": "assets/css/main.css",
"chars": 2261,
"preview": ".face-detect-panel {\n margin-bottom: 15px;\n}\n\n.face-detection-ui {\n overflow: hidden;\n}\n\n.face-detection-image {\n "
},
{
"path": "assets/js/main.js",
"chars": 13335,
"preview": "window.hotspots = {};\n\n// prevent errors while logging to browsers that support it\nif (!window.console) {\n window.con"
},
{
"path": "bower.json",
"chars": 252,
"preview": "{\n \"name\": \"my-eyes-are-up-here\",\n \"homepage\": \"https://interconnectit.com\",\n \"authors\": [\n \"Evgenii Nasyrov <evge"
},
{
"path": "composer.json",
"chars": 641,
"preview": "{\n \"name\": \"interconnectit/my-eyes-are-up-here\",\n \"type\": \"wordpress-plugin\",\n \"license\": \"GPLv2\",\n \"description\": \""
},
{
"path": "includes/class-meauh-admin.php",
"chars": 1244,
"preview": "<?php\n/**\n * Admin\n *\n * @package my-eyes-are-up-here\n * @author interconnect/it\n */\n\nif ( ! defined( 'ABSPATH' ) ) {\n\te"
},
{
"path": "includes/class-meauh-ajax.php",
"chars": 3749,
"preview": "<?php\n/**\n * Ajax\n *\n * @package my-eyes-are-up-here\n * @author interconnect/it\n */\n\nif ( ! defined( 'ABSPATH' ) ) {\n\tex"
},
{
"path": "includes/class-meauh-attachment.php",
"chars": 9593,
"preview": "<?php\n/**\n * Attachment\n *\n * @package my-eyes-are-up-here\n * @author interconnect/it\n */\n\nif ( ! defined( 'ABSPATH' ) )"
},
{
"path": "languages/meauh.pot",
"chars": 2401,
"preview": "# Copyright (C) 2016 interconnect/it\n# This file is distributed under the same license as the My Eyes Are Up Here packag"
},
{
"path": "languages/my-eyes-are-up-here.pot",
"chars": 2401,
"preview": "# Copyright (C) 2017 interconnect/it\n# This file is distributed under the same license as the My Eyes Are Up Here packag"
},
{
"path": "my-eyes-are-up-here.php",
"chars": 2672,
"preview": "<?php\n/**\n * Plugin Name: My Eyes Are Up Here\n * Plugin URI: https://github.com/interconnectit/my-eyes-are-up-here\n * De"
},
{
"path": "package.json",
"chars": 884,
"preview": "{\n \"name\": \"my-eyes-are-up-here\",\n \"version\": \"1.1.9\",\n \"author\": \"interconnect/it <support@interconnectit.com>\",\n \""
},
{
"path": "readme.txt",
"chars": 4798,
"preview": "=== My Eyes Are Up Here ===\nContributors: interconnectit, sanchothefat, spectacula, AndyWalmsley\nDonate link: https://my"
}
]
About this extraction
This page contains the full source code of the interconnectit/my-eyes-are-up-here GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 16 files (48.0 KB), approximately 13.1k tokens, and a symbol index with 33 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.