├── .gitignore
├── tests
├── CropTest.php
├── images
│ ├── side.png
│ └── test.png
└── CropEntropyTest.php
├── docs
├── img
│ ├── favicon.ico
│ ├── loader.gif
│ ├── icons
│ │ ├── ok.png
│ │ ├── class.png
│ │ ├── file.gif
│ │ ├── folder.gif
│ │ ├── method.png
│ │ ├── search.gif
│ │ ├── constant.png
│ │ ├── favicon.ico
│ │ ├── file-php.png
│ │ ├── function.png
│ │ ├── property.png
│ │ ├── variable.png
│ │ ├── arrow_down.png
│ │ ├── arrow_right.png
│ │ ├── icon-th-big.png
│ │ ├── interface.png
│ │ ├── view_source.png
│ │ ├── visibility_private.png
│ │ ├── visibility_public.png
│ │ ├── icon-folder-open-big.png
│ │ ├── visibility_protected.png
│ │ └── icon_template.svg
│ ├── iviewer
│ │ ├── grab.cur
│ │ ├── hand.cur
│ │ ├── iviewer.zoom_fit.png
│ │ ├── iviewer.zoom_in.png
│ │ ├── iviewer.zoom_in2.gif
│ │ ├── iviewer.zoom_out.png
│ │ ├── iviewer.rotate_left.png
│ │ ├── iviewer.zoom_fit2.gif
│ │ ├── iviewer.zoom_out2.gif
│ │ ├── iviewer.zoom_zero.png
│ │ ├── iviewer.zoom_zero2.gif
│ │ └── iviewer.rotate_right.png
│ ├── apple-touch-icon.png
│ ├── apple-touch-icon-72x72.png
│ ├── glyphicons-halflings.png
│ ├── apple-touch-icon-114x114.png
│ └── glyphicons-halflings-white.png
├── js
│ ├── prettify
│ │ ├── lang-go.js
│ │ ├── lang-ml.js
│ │ ├── lang-vb.js
│ │ ├── lang-lua.js
│ │ ├── lang-scala.js
│ │ ├── lang-sql.js
│ │ ├── lang-tex.js
│ │ ├── lang-vhdl.js
│ │ ├── lang-wiki.js
│ │ ├── lang-apollo.js
│ │ ├── lang-proto.js
│ │ ├── lang-yaml.js
│ │ ├── lang-hs.js
│ │ ├── lang-lisp.js
│ │ ├── lang-css.js
│ │ ├── lang-n.js
│ │ └── lang-clj.js
│ ├── menu.js
│ ├── jquery.mousewheel.min.js
│ ├── sidebar.js
│ ├── jquery.cookie.js
│ ├── SVGPan.js
│ ├── template.js
│ ├── jquery.treeview.js
│ └── jquery.splitter.js
├── phpdoc-cache-2e
│ └── phpdoc-cache-settings.dat
├── css
│ ├── prettify.css
│ ├── jquery.iviewer.css
│ ├── bootstrap-responsive.min.css
│ └── template.css
├── phpdoc-cache-56
│ └── phpdoc-cache-file_ecc0bc6e0c3c88779fc339498217f0a4.dat
├── phpdoc-cache-10
│ └── phpdoc-cache-file_bcba727f46776285921554058dfc1886.dat
├── classes.svg
├── deprecated.html
├── graph_class.html
├── markers.html
├── index.html
├── phpdoc-cache-51
│ └── phpdoc-cache-file_28dd9fff2c7f32cbf7c5a9d47b836f35.dat
├── namespaces
│ ├── stojg.crop.html
│ ├── stojg.html
│ └── default.html
└── packages
│ └── default.html
├── .scrutinizer.yml
├── composer.json
├── phpunit.xml
├── LICENCE
├── src
└── stojg
│ └── crop
│ ├── CropCenter.php
│ ├── CropFace.php
│ ├── Crop.php
│ ├── CropBalanced.php
│ └── CropEntropy.php
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | docs/phpdoc-cache*
--------------------------------------------------------------------------------
/tests/CropTest.php:
--------------------------------------------------------------------------------
1 | ?|]+/,a,":|>?"],["dec",/^%(?:YAML|TAG)[^\n\r#]+/,a,"%"],["typ",/^&\S+/,a,"&"],["typ",/^!\S*/,a,"!"],["str",/^"(?:[^"\\]|\\.)*(?:"|$)/,a,'"'],["str",/^'(?:[^']|'')*(?:'|$)/,a,"'"],["com",/^#[^\n\r]*/,a,"#"],["pln",/^\s+/,a," \t\r\n"]],[["dec",/^(?:---|\.\.\.)(?:[\n\r]|$)/],["pun",/^-/],["kwd",/^\w+:[\n\r ]/],["pln",/^\w+/]]),["yaml","yml"]);
3 |
--------------------------------------------------------------------------------
/docs/js/prettify/lang-hs.js:
--------------------------------------------------------------------------------
1 | PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t-\r ]+/,null,"\t\n\r "],["str",/^"(?:[^\n\f\r"\\]|\\[\S\s])*(?:"|$)/,null,'"'],["str",/^'(?:[^\n\f\r'\\]|\\[^&])'?/,null,"'"],["lit",/^(?:0o[0-7]+|0x[\da-f]+|\d+(?:\.\d+)?(?:e[+-]?\d+)?)/i,null,"0123456789"]],[["com",/^(?:--+[^\n\f\r]*|{-(?:[^-]|-+[^}-])*-})/],["kwd",/^(?:case|class|data|default|deriving|do|else|if|import|in|infix|infixl|infixr|instance|let|module|newtype|of|then|type|where|_)(?=[^\d'A-Za-z]|$)/,
2 | null],["pln",/^(?:[A-Z][\w']*\.)*[A-Za-z][\w']*/],["pun",/^[^\d\t-\r "'A-Za-z]+/]]),["hs"]);
3 |
--------------------------------------------------------------------------------
/docs/css/prettify.css:
--------------------------------------------------------------------------------
1 | .pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee}
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "stojg/crop",
3 | "type": "library",
4 | "description": "Image cropping classes",
5 | "keywords": ["image"],
6 | "homepage": "http://github.com/stojg/crop",
7 | "license": "BSD-2",
8 | "authors": [
9 | {
10 | "name": "Stig Lindqvist",
11 | "email": "stojg.lindqvist@gmail.com",
12 | "homepage": "https://stojg.se/"
13 | },
14 | {
15 | "name": "Julien Deniau jdeniau",
16 | "email": "julien.deniau@gmail.com",
17 | "homepage": "http://www.mapado.com"
18 | }
19 | ],
20 | "require": {
21 | "php": ">=5.3.0"
22 | },
23 | "autoload": {
24 | "psr-0": { "stojg\\crop": "src/" }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/docs/js/prettify/lang-lisp.js:
--------------------------------------------------------------------------------
1 | var a=null;
2 | PR.registerLangHandler(PR.createSimpleLexer([["opn",/^\(+/,a,"("],["clo",/^\)+/,a,")"],["com",/^;[^\n\r]*/,a,";"],["pln",/^[\t\n\r \xa0]+/,a,"\t\n\r \xa0"],["str",/^"(?:[^"\\]|\\[\S\s])*(?:"|$)/,a,'"']],[["kwd",/^(?:block|c[ad]+r|catch|con[ds]|def(?:ine|un)|do|eq|eql|equal|equalp|eval-when|flet|format|go|if|labels|lambda|let|load-time-value|locally|macrolet|multiple-value-call|nil|progn|progv|quote|require|return-from|setq|symbol-macrolet|t|tagbody|the|throw|unwind)\b/,a],
3 | ["lit",/^[+-]?(?:[#0]x[\da-f]+|\d+\/\d+|(?:\.\d+|\d+(?:\.\d*)?)(?:[de][+-]?\d+)?)/i],["lit",/^'(?:-*(?:\w|\\[!-~])(?:[\w-]*|\\[!-~])[!=?]?)?/],["pln",/^-*(?:[_a-z]|\\[!-~])(?:[\w-]*|\\[!-~])[!=?]?/i],["pun",/^[^\w\t\n\r "'-);\\\xa0]+/]]),["cl","el","lisp","scm"]);
4 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 | tests
6 |
7 |
8 |
9 |
10 | src
11 |
12 | tests/
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/docs/js/menu.js:
--------------------------------------------------------------------------------
1 | var timeout = 500;
2 | var closetimer = 0;
3 | var ddmenuitem = 0;
4 |
5 | function menu_open() {
6 | menu_canceltimer();
7 | menu_close();
8 | ddmenuitem = $(this).find('ul').css('visibility', 'visible');
9 | }
10 |
11 | function menu_close() {
12 | if (ddmenuitem) ddmenuitem.css('visibility', 'hidden');
13 | }
14 |
15 | function menu_timer() {
16 | closetimer = window.setTimeout(menu_close, timeout);
17 | }
18 |
19 | function menu_canceltimer() {
20 | if (closetimer) {
21 | window.clearTimeout(closetimer);
22 | closetimer = null;
23 | }
24 | }
25 |
26 | $(document).ready(function() {
27 | $('#file-nav > li').bind('mouseover', menu_open);
28 | $('#file-nav > li').bind('mouseout', menu_timer);
29 | });
30 |
31 | document.onclick = menu_close;
--------------------------------------------------------------------------------
/docs/js/prettify/lang-css.js:
--------------------------------------------------------------------------------
1 | PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\f\r ]+/,null," \t\r\n"]],[["str",/^"(?:[^\n\f\r"\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*"/,null],["str",/^'(?:[^\n\f\r'\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*'/,null],["lang-css-str",/^url\(([^"')]*)\)/i],["kwd",/^(?:url|rgb|!important|@import|@page|@media|@charset|inherit)(?=[^\w-]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*)\s*:/i],["com",/^\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//],["com",
2 | /^(?:<\!--|--\>)/],["lit",/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],["lit",/^#[\da-f]{3,6}/i],["pln",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i],["pun",/^[^\s\w"']+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[["kwd",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[["str",/^[^"')]+/]]),["css-str"]);
3 |
--------------------------------------------------------------------------------
/docs/phpdoc-cache-56/phpdoc-cache-file_ecc0bc6e0c3c88779fc339498217f0a4.dat:
--------------------------------------------------------------------------------
1 | O:39:"phpDocumentor\Descriptor\FileDescriptor":20:{s:7:" * hash";s:32:"ce407ff5715c837d02b1aba7975bf512";s:9:" * source";s:6:"]*(?:#>|$)/,a],["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,a],["com",/^\/\/[^\n\r]*/,a],["com",/^\/\*[\S\s]*?(?:\*\/|$)/,
3 | a],["kwd",/^(?:abstract|and|as|base|catch|class|def|delegate|enum|event|extern|false|finally|fun|implements|interface|internal|is|macro|match|matches|module|mutable|namespace|new|null|out|override|params|partial|private|protected|public|ref|sealed|static|struct|syntax|this|throw|true|try|type|typeof|using|variant|virtual|volatile|when|where|with|assert|assert2|async|break|checked|continue|do|else|ensures|for|foreach|if|late|lock|new|nolate|otherwise|regexp|repeat|requires|return|surroundwith|unchecked|unless|using|while|yield)\b/,
4 | a],["typ",/^(?:array|bool|byte|char|decimal|double|float|int|list|long|object|sbyte|short|string|ulong|uint|ufloat|ulong|ushort|void)\b/,a],["lit",/^@[$_a-z][\w$@]*/i,a],["typ",/^@[A-Z]+[a-z][\w$@]*/,a],["pln",/^'?[$_a-z][\w$@]*/i,a],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,a,"0123456789"],["pun",/^.[^\s\w"-$'./@`]*/,a]]),["n","nemerle"]);
5 |
--------------------------------------------------------------------------------
/docs/js/jquery.mousewheel.min.js:
--------------------------------------------------------------------------------
1 | /*! Copyright (c) 2011 Brandon Aaron (http://brandonaaron.net)
2 | * Licensed under the MIT License (LICENSE.txt).
3 | *
4 | * Thanks to: http://adomas.org/javascript-mouse-wheel/ for some pointers.
5 | * Thanks to: Mathias Bank(http://www.mathias-bank.de) for a scope bug fix.
6 | * Thanks to: Seamus Leahy for adding deltaX and deltaY
7 | *
8 | * Version: 3.0.6
9 | *
10 | * Requires: 1.2.2+
11 | */
12 | (function(d){function e(a){var b=a||window.event,c=[].slice.call(arguments,1),f=0,e=0,g=0,a=d.event.fix(b);a.type="mousewheel";b.wheelDelta&&(f=b.wheelDelta/120);b.detail&&(f=-b.detail/3);g=f;b.axis!==void 0&&b.axis===b.HORIZONTAL_AXIS&&(g=0,e=-1*f);b.wheelDeltaY!==void 0&&(g=b.wheelDeltaY/120);b.wheelDeltaX!==void 0&&(e=-1*b.wheelDeltaX/120);c.unshift(a,f,e,g);return(d.event.dispatch||d.event.handle).apply(this,c)}var c=["DOMMouseScroll","mousewheel"];if(d.event.fixHooks)for(var h=c.length;h;)d.event.fixHooks[c[--h]]=
13 | d.event.mouseHooks;d.event.special.mousewheel={setup:function(){if(this.addEventListener)for(var a=c.length;a;)this.addEventListener(c[--a],e,false);else this.onmousewheel=e},teardown:function(){if(this.removeEventListener)for(var a=c.length;a;)this.removeEventListener(c[--a],e,false);else this.onmousewheel=null}};d.fn.extend({mousewheel:function(a){return a?this.bind("mousewheel",a):this.trigger("mousewheel")},unmousewheel:function(a){return this.unbind("mousewheel",a)}})})(jQuery);
--------------------------------------------------------------------------------
/src/stojg/crop/CropCenter.php:
--------------------------------------------------------------------------------
1 | getCenterOffset($original, $targetWidth, $targetHeight);
28 | }
29 |
30 | /**
31 | * Get the cropping offset for the image based on the center of the image
32 | *
33 | * @param \Imagick $image
34 | * @param int $targetWidth
35 | * @param int $targetHeight
36 | * @return array
37 | */
38 | protected function getCenterOffset(\Imagick $image, $targetWidth, $targetHeight)
39 | {
40 | $size = $image->getImageGeometry();
41 | $originalWidth = $size['width'];
42 | $originalHeight = $size['height'];
43 | $goalX = (int) (($originalWidth-$targetWidth)/2);
44 | $goalY = (int) (($originalHeight-$targetHeight)/2);
45 |
46 | return array('x' => $goalX, 'y' => $goalY);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/docs/js/sidebar.js:
--------------------------------------------------------------------------------
1 | jQuery.expr[':'].Contains = function(a, i, m) {
2 | return jQuery(a).text().toUpperCase().indexOf(m[3].toUpperCase()) >= 0;
3 | };
4 |
5 | $(function() {
6 | $("#sidebar-nav").accordion({
7 | autoHeight: false,
8 | navigation: true,
9 | collapsible: true
10 | }).accordion("activate", false)
11 | .find('a.link').unbind('click').click(
12 | function(ev) {
13 | ev.cancelBubble = true; // IE
14 | if (ev.stopPropagation) {
15 | ev.stopPropagation(); // the rest
16 | }
17 |
18 | return true;
19 | }).prev().prev().remove();
20 |
21 | $("#sidebar-nav>h3").click(function() {
22 | if ($(this).attr('initialized') == 'true') return;
23 |
24 | $(this).next().find(".sidebar-nav-tree").treeview({
25 | collapsed: true,
26 | persist: "cookie"
27 | });
28 | $(this).attr('initialized', true);
29 | });
30 | });
31 |
32 | function tree_search(input) {
33 | treeview = $(input).parent().parent().next();
34 |
35 | // Expand all items
36 | treeview.find('.expandable-hitarea').click();
37 |
38 | // make all items visible again
39 | treeview.find('li:hidden').show();
40 |
41 | // hide all items that do not match the given search criteria
42 | if ($(input).val()) {
43 | treeview.find('li').not(':has(a:Contains(' + $(input).val() + '))').hide();
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/docs/js/prettify/lang-clj.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2011 Google Inc.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 | var a=null;
17 | PR.registerLangHandler(PR.createSimpleLexer([["opn",/^[([{]+/,a,"([{"],["clo",/^[)\]}]+/,a,")]}"],["com",/^;[^\n\r]*/,a,";"],["pln",/^[\t\n\r \xa0]+/,a,"\t\n\r \xa0"],["str",/^"(?:[^"\\]|\\[\S\s])*(?:"|$)/,a,'"']],[["kwd",/^(?:def|if|do|let|quote|var|fn|loop|recur|throw|try|monitor-enter|monitor-exit|defmacro|defn|defn-|macroexpand|macroexpand-1|for|doseq|dosync|dotimes|and|or|when|not|assert|doto|proxy|defstruct|first|rest|cons|defprotocol|deftype|defrecord|reify|defmulti|defmethod|meta|with-meta|ns|in-ns|create-ns|import|intern|refer|alias|namespace|resolve|ref|deref|refset|new|set!|memfn|to-array|into-array|aset|gen-class|reduce|map|filter|find|nil?|empty?|hash-map|hash-set|vec|vector|seq|flatten|reverse|assoc|dissoc|list|list?|disj|get|union|difference|intersection|extend|extend-type|extend-protocol|prn)\b/,a],
18 | ["typ",/^:[\dA-Za-z-]+/]]),["clj"]);
19 |
--------------------------------------------------------------------------------
/docs/css/jquery.iviewer.css:
--------------------------------------------------------------------------------
1 | .iviewer_common {
2 | position:absolute;
3 | bottom:10px;
4 | border: 1px solid #000;
5 | height: 28px;
6 | z-index: 5000;
7 | }
8 |
9 | .iviewer_cursor {
10 | cursor: url(../img/iviewer/hand.cur) 6 8, pointer;
11 | }
12 |
13 | .iviewer_drag_cursor {
14 | cursor: url(../img/iviewer/grab.cur) 6 8, pointer;
15 | }
16 |
17 | .iviewer_button {
18 | width: 28px;
19 | cursor: pointer;
20 | background-position: center center;
21 | background-repeat: no-repeat;
22 | }
23 |
24 | .iviewer_zoom_in {
25 | left: 20px;
26 | background: url(../img/iviewer/iviewer.zoom_in.png);
27 | }
28 |
29 | .iviewer_zoom_out {
30 | left: 55px;
31 | background: url(../img/iviewer/iviewer.zoom_out.png);
32 | }
33 |
34 | .iviewer_zoom_zero {
35 | left: 90px;
36 | background: url(../img/iviewer/iviewer.zoom_zero.png);
37 | }
38 |
39 | .iviewer_zoom_fit {
40 | left: 125px;
41 | background: url(../img/iviewer/iviewer.zoom_fit.png);
42 | }
43 |
44 | .iviewer_zoom_status {
45 | left: 160px;
46 | font: 1em/28px Sans;
47 | color: #000;
48 | background-color: #fff;
49 | text-align: center;
50 | width: 60px;
51 | }
52 |
53 | .iviewer_rotate_left {
54 | left: 227px;
55 | background: #fff url(../img/iviewer/iviewer.rotate_left.png) center center no-repeat;
56 | }
57 |
58 | .iviewer_rotate_right {
59 | left: 262px;
60 | background: #fff url(../img/iviewer/iviewer.rotate_right.png) center center no-repeat;
61 | }
62 |
63 | .viewer
64 | {
65 | width: 100%;
66 | height: 500px;
67 | position: relative;
68 | background: transparent url('../img/loader.gif') no-repeat center center;
69 | }
70 |
71 | .viewer img
72 | {
73 | max-width: none;
74 | }
75 |
76 | .wrapper
77 | {
78 | overflow: hidden;
79 | }
80 |
81 | .iviewer_common
82 | {
83 | border: 0;
84 | bottom: auto;
85 | top: 10px;
86 | }
87 |
88 | .iviewer_zoom_status
89 | {
90 | border: 1px solid black;
91 | }
92 |
--------------------------------------------------------------------------------
/tests/CropEntropyTest.php:
--------------------------------------------------------------------------------
1 | markTestSkipped('The imagick extension is not available.');
22 | return;
23 | }
24 | $this->tempDir = sys_get_temp_dir().DIRECTORY_SEPARATOR.'croptest';
25 |
26 | if(file_exists($this->tempDir)) {
27 | $this->cleanup();
28 | }
29 |
30 | if(!mkdir($this->tempDir)) {
31 | $this->markTestSkipped('Can\'t create temp directory '. $this->tempDir .' skipping test');
32 | }
33 | }
34 |
35 | /**
36 | *
37 | */
38 | public function tearDown() {
39 | $this->cleanup();
40 | }
41 |
42 | public function testEntropy() {
43 | $center = new CropEntropy(__DIR__ . self::EXAMPLE_IMAGE);
44 | $croppedImage = $center->resizeAndCrop(200, 200);
45 | $croppedImage->writeimage($this->tempDir.'/entropy-test.png');
46 | }
47 |
48 | public function testEntropyWithPreviusImagick() {
49 | $image = new Imagick(__DIR__ . self::EXAMPLE_IMAGE);
50 |
51 | $center = new CropEntropy();
52 | $center->setImage($image);
53 |
54 | $croppedImage = $center->resizeAndCrop(200, 200);
55 |
56 | $this->assertSame($image, $croppedImage);
57 |
58 | $croppedImage->writeimage($this->tempDir.'/entropy-test.png');
59 | }
60 |
61 | private function cleanup() {
62 | $testFiles = glob($this->tempDir.DIRECTORY_SEPARATOR.'*');
63 | foreach($testFiles as $file) {
64 | unlink($file);
65 | }
66 | rmdir($this->tempDir);
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Crop
2 |
3 | This is a small set of image croppers that I created for testing automated cropping.
4 |
5 | [](http://travis-ci.org/stojg/crop)
6 | [](https://scrutinizer-ci.com/g/stojg/crop/)
7 | [](https://scrutinizer-ci.com/g/stojg/crop/)
8 |
9 | ## Requirements
10 |
11 | - PHP 5.3 with Imagick extension
12 |
13 | ## Description
14 |
15 | This little project includes three functional image cropers:
16 |
17 | ### CropCenter
18 |
19 | This is the most basic of cropping techniques:
20 |
21 | 1. Find the exact center of the image
22 | 2. Trim any edges that is bigger than the targetWidth and targetHeight
23 |
24 | ### CropEntropy
25 |
26 | This class finds the a position in the picture with the most "energy" in it. Energy (or entropy) in
27 | images are defined by 'edginess' in the image. For example a image of the sky have low edginess and
28 | an image of an anthill has very high edginess.
29 |
30 | Energy is in this case calculated like this
31 |
32 | 1. Take the image and turn it into black and white
33 | 2. Run a edge filter so that we're left with only edges.
34 | 3. Find a piece in the picture that has the highest entropy (i.e. most edges)
35 | 4. Return coordinates that makes sure that this piece of the picture is not cropped 'away'
36 |
37 | ### CropBalanced
38 |
39 | Crop balanced is a variant of CropEntropy where I tried to the cropping a bit more balanced.
40 |
41 | 1. Dividing the image into four equally squares
42 | 2. Find the most energetic point per square
43 | 3. Finding the images weighted mean interest point for all squares
44 |
45 | ### CropFace
46 |
47 | Crop face uses [PHP Facedetect Extension](http://www.xarg.org/project/php-facedetect/) (which uses OpenCV).
48 |
49 | In details, the FaceCrop uses Entropy Crop but puts blocking "limits" on the faces.
50 | If the program faces two limits, we let the entropy decide the best crop.
51 |
52 |
53 | ## Usage
54 |
55 | $center = new \stojg\crop\CropCenter($filepath);
56 | $croppedImage = $center->resizeAndCrop($width, $height);
57 | $croppedImage->writeimage('assets/thumbs/cropped-center.jpg');
58 |
--------------------------------------------------------------------------------
/docs/phpdoc-cache-10/phpdoc-cache-file_bcba727f46776285921554058dfc1886.dat:
--------------------------------------------------------------------------------
1 | O:39:"phpDocumentor\Descriptor\FileDescriptor":20:{s:7:" * hash";s:32:"61267d3c38aefbb5756a356ddc64ab8d";s:9:" * source";s:321:"
2 |
3 |
4 |
21 |
23 |
42 |
49 |
50 |
52 |
53 |
55 | image/svg+xml
56 |
58 |
59 |
60 |
61 |
62 |
67 |
73 |
79 | Co
92 |
93 |
94 |
--------------------------------------------------------------------------------
/src/stojg/crop/CropFace.php:
--------------------------------------------------------------------------------
1 | imagePath = $imagePath;
42 | parent::__construct($imagePath);
43 | }
44 |
45 | /**
46 | * getFaceList get faces positions and sizes
47 | *
48 | * @access protected
49 | * @return array
50 | */
51 | protected function getFaceList()
52 | {
53 | if (!function_exists('face_detect')) {
54 | $msg = 'PHP Facedetect extension must be installed.
55 | See http://www.xarg.org/project/php-facedetect/ for more details';
56 | throw new \Exception($msg);
57 | }
58 |
59 | $faceList = $this->getFaceListFromClassifier(self::CLASSIFIER_FACE);
60 |
61 | $profileList = $this->getFaceListFromClassifier(self::CLASSIFIER_PROFILE);
62 |
63 | $faceList = array_merge($faceList, $profileList);
64 |
65 | return $faceList;
66 | }
67 |
68 | /**
69 | * getFaceListFromClassifier
70 | *
71 | * @param string $classifier
72 | * @access protected
73 | * @return array
74 | */
75 | protected function getFaceListFromClassifier($classifier)
76 | {
77 | $faceList = face_detect($this->imagePath, __DIR__ . $classifier);
78 |
79 | return $faceList;
80 | }
81 |
82 | /**
83 | * getSafeZoneList
84 | *
85 | * @access private
86 | * @return array
87 | */
88 | protected function getSafeZoneList()
89 | {
90 | if (!isset($this->safeZoneList)) {
91 | $this->safeZoneList = [];
92 | }
93 | // the local key is the current image width-height
94 | $key = $this->originalImage->getImageWidth() . '-' . $this->originalImage->getImageHeight();
95 |
96 | if (!isset($this->safeZoneList[$key])) {
97 | $faceList = $this->getFaceList();
98 |
99 | // getFaceList works on the main image, so we use a ratio between main/current image
100 | $xRatio = $this->getBaseDimension('width') / $this->originalImage->getImageWidth();
101 | $yRatio = $this->getBaseDimension('height') / $this->originalImage->getImageHeight();
102 |
103 | $safeZoneList = array();
104 | foreach ($faceList as $face) {
105 | $hw = ceil($face['w'] / 2);
106 | $hh = ceil($face['h'] / 2);
107 | $safeZone = array(
108 | 'left' => $face['x'] - $hw,
109 | 'right' => $face['x'] + $face['w'] + $hw,
110 | 'top' => $face['y'] - $hh,
111 | 'bottom' => $face['y'] + $face['h'] + $hh
112 | );
113 |
114 | $safeZoneList[] = [
115 | 'left' => round($safeZone['left'] / $xRatio),
116 | 'right' => round($safeZone['right'] / $xRatio),
117 | 'top' => round($safeZone['top'] / $yRatio),
118 | 'bottom' => round($safeZone['bottom'] / $yRatio),
119 | ];
120 | }
121 | $this->safeZoneList[$key] = $safeZoneList;
122 | }
123 |
124 | return $this->safeZoneList[$key];
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/docs/js/jquery.cookie.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Cookie plugin
3 | *
4 | * Copyright (c) 2006 Klaus Hartl (stilbuero.de)
5 | * Dual licensed under the MIT and GPL licenses:
6 | * http://www.opensource.org/licenses/mit-license.php
7 | * http://www.gnu.org/licenses/gpl.html
8 | *
9 | */
10 |
11 | /**
12 | * Create a cookie with the given name and value and other optional parameters.
13 | *
14 | * @example $.cookie('the_cookie', 'the_value');
15 | * @desc Set the value of a cookie.
16 | * @example $.cookie('the_cookie', 'the_value', {expires: 7, path: '/', domain: 'jquery.com', secure: true});
17 | * @desc Create a cookie with all available options.
18 | * @example $.cookie('the_cookie', 'the_value');
19 | * @desc Create a session cookie.
20 | * @example $.cookie('the_cookie', null);
21 | * @desc Delete a cookie by passing null as value.
22 | *
23 | * @param String name The name of the cookie.
24 | * @param String value The value of the cookie.
25 | * @param Object options An object literal containing key/value pairs to provide optional cookie attributes.
26 | * @option Number|Date expires Either an integer specifying the expiration date from now on in days or a Date object.
27 | * If a negative value is specified (e.g. a date in the past), the cookie will be deleted.
28 | * If set to null or omitted, the cookie will be a session cookie and will not be retained
29 | * when the the browser exits.
30 | * @option String path The value of the path atribute of the cookie (default: path of page that created the cookie).
31 | * @option String domain The value of the domain attribute of the cookie (default: domain of page that created the cookie).
32 | * @option Boolean secure If true, the secure attribute of the cookie will be set and the cookie transmission will
33 | * require a secure protocol (like HTTPS).
34 | * @type undefined
35 | *
36 | * @name $.cookie
37 | * @cat Plugins/Cookie
38 | * @author Klaus Hartl/klaus.hartl@stilbuero.de
39 | */
40 |
41 | /**
42 | * Get the value of a cookie with the given name.
43 | *
44 | * @example $.cookie('the_cookie');
45 | * @desc Get the value of a cookie.
46 | *
47 | * @param String name The name of the cookie.
48 | * @return The value of the cookie.
49 | * @type String
50 | *
51 | * @name $.cookie
52 | * @cat Plugins/Cookie
53 | * @author Klaus Hartl/klaus.hartl@stilbuero.de
54 | */
55 | jQuery.cookie = function(name, value, options)
56 | {
57 | if (typeof value != 'undefined')
58 | { // name and value given, set cookie
59 | options = options || {};
60 | if (value === null)
61 | {
62 | value = '';
63 | options.expires = -1;
64 | }
65 | var expires = '';
66 | if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString))
67 | {
68 | var date;
69 | if (typeof options.expires == 'number')
70 | {
71 | date = new Date();
72 | date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000));
73 | }
74 | else
75 | {
76 | date = options.expires;
77 | }
78 | expires = '; expires=' + date.toUTCString(); // use expires attribute, max-age is not supported by IE
79 | }
80 | var path = options.path ? '; path=' + options.path : '';
81 | var domain = options.domain ? '; domain=' + options.domain : '';
82 | var secure = options.secure ? '; secure' : '';
83 | document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join('');
84 | }
85 | else
86 | { // only name given, get cookie
87 | var cookieValue = null;
88 | if (document.cookie && document.cookie != '')
89 | {
90 | var cookies = document.cookie.split(';');
91 | for (var i = 0; i < cookies.length; i++)
92 | {
93 | var cookie = jQuery.trim(cookies[i]);
94 | // Does this cookie string begin with the name we want?
95 | if (cookie.substring(0, name.length + 1) == (name + '='))
96 | {
97 | cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
98 | break;
99 | }
100 | }
101 | }
102 | return cookieValue;
103 | }
104 | };
--------------------------------------------------------------------------------
/docs/classes.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 | G
11 |
12 | cluster_Global
13 |
14 | \
15 |
16 | cluster_\stojg
17 |
18 | stojg
19 |
20 | cluster_\stojg\crop
21 |
22 | crop
23 |
24 |
25 | \\stojg\\crop\\CropFace
26 |
27 | CropFace
28 |
29 |
30 | \\stojg\\crop\\CropEntropy
31 |
32 | CropEntropy
33 |
34 |
35 | \\stojg\\crop\\CropFace->\\stojg\\crop\\CropEntropy
36 |
37 |
38 |
39 |
40 | \\stojg\\crop\\CropCenter
41 |
42 | CropCenter
43 |
44 |
45 | \\stojg\\crop\\Crop
46 |
47 | «abstract»
48 | Crop
49 |
50 |
51 | \\stojg\\crop\\CropCenter->\\stojg\\crop\\Crop
52 |
53 |
54 |
55 |
56 | \\stojg\\crop\\CropBalanced
57 |
58 | CropBalanced
59 |
60 |
61 | \\stojg\\crop\\CropBalanced->\\stojg\\crop\\Crop
62 |
63 |
64 |
65 |
66 | \\stojg\\crop\\CropEntropy->\\stojg\\crop\\Crop
67 |
68 |
69 |
70 |
71 | \\ClassEntropyTest
72 |
73 | ClassEntropyTest
74 |
75 |
76 | \\PHPUnit_Framework_TestCase
77 |
78 | \PHPUnit_Framework_TestCase
79 |
80 |
81 | \\ClassEntropyTest->\\PHPUnit_Framework_TestCase
82 |
83 |
84 |
85 |
86 |
87 |
--------------------------------------------------------------------------------
/docs/deprecated.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | » Deprecated elements
7 |
8 |
9 |
10 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
90 |
91 |
92 |
93 |
94 | Javascript is disabled; several features are only available if Javascript is enabled.
95 |
96 |
97 |
98 |
99 |
100 |
101 |
106 |
107 |
108 |
109 | \
110 | Deprecated elements
111 |
112 |
113 |
114 |
No deprecated elements have been found in this project.
115 |
116 |
117 |
118 |
119 |
120 |
126 |
127 |
128 |
--------------------------------------------------------------------------------
/docs/graph_class.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | API Documentation
7 |
8 |
9 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
89 |
90 |
91 |
92 |
93 | Javascript is disabled; several features are only available if Javascript is enabled.
94 |
95 |
96 |
97 |
98 |
105 |
106 |
107 |
119 |
120 |
121 |
127 |
128 |
129 |
--------------------------------------------------------------------------------
/src/stojg/crop/Crop.php:
--------------------------------------------------------------------------------
1 | setImage(new \Imagick($imagePath));
62 | }
63 | }
64 |
65 | /**
66 | * Sets the object Image to be croped
67 | *
68 | * @param \Imagick $image
69 | * @return null
70 | */
71 | public function setImage(\Imagick $image)
72 | {
73 | $this->originalImage = $image;
74 |
75 | // set base image dimensions
76 | $this->setBaseDimensions(
77 | $this->originalImage->getImageWidth(),
78 | $this->originalImage->getImageHeight()
79 | );
80 | }
81 |
82 | /**
83 | * Get the area in pixels for this image
84 | *
85 | * @param \Imagick $image
86 | * @return int
87 | */
88 | protected function area(\Imagick $image)
89 | {
90 | $size = $image->getImageGeometry();
91 |
92 | return $size['height'] * $size['width'];
93 | }
94 |
95 | /**
96 | * Resize and crop the image so it dimensions matches $targetWidth and $targetHeight
97 | *
98 | * @param int $targetWidth
99 | * @param int $targetHeight
100 | * @return boolean|\Imagick
101 | */
102 | public function resizeAndCrop($targetWidth, $targetHeight)
103 | {
104 | // First get the size that we can use to safely trim down the image without cropping any sides
105 | $crop = $this->getSafeResizeOffset($this->originalImage, $targetWidth, $targetHeight);
106 | // Resize the image
107 | $this->originalImage->resizeImage($crop['width'], $crop['height'], \Imagick::FILTER_CUBIC, .5);
108 | // Get the offset for cropping the image further
109 | $offset = $this->getSpecialOffset($this->originalImage, $targetWidth, $targetHeight);
110 | // Crop the image
111 | $this->originalImage->cropImage($targetWidth, $targetHeight, $offset['x'], $offset['y']);
112 |
113 | return $this->originalImage;
114 | }
115 |
116 | /**
117 | * Returns width and height for resizing the image, keeping the aspect ratio
118 | * and allow the image to be larger than either the width or height
119 | *
120 | * @param \Imagick $image
121 | * @param int $targetWidth
122 | * @param int $targetHeight
123 | * @return array
124 | */
125 | protected function getSafeResizeOffset(\Imagick $image, $targetWidth, $targetHeight)
126 | {
127 | $source = $image->getImageGeometry();
128 | if (($source['width'] / $source['height']) < ($targetWidth / $targetHeight)) {
129 | $scale = $source['width'] / $targetWidth;
130 | } else {
131 | $scale = $source['height'] / $targetHeight;
132 | }
133 |
134 | return array('width' => (int) ($source['width'] / $scale), 'height' => (int) ($source['height'] / $scale));
135 | }
136 |
137 | /**
138 | * Returns a YUV weighted greyscale value
139 | *
140 | * @param int $r
141 | * @param int $g
142 | * @param int $b
143 | * @return int
144 | * @see http://en.wikipedia.org/wiki/YUV
145 | */
146 | protected function rgb2bw($r, $g, $b)
147 | {
148 | return ($r*0.299)+($g*0.587)+($b*0.114);
149 | }
150 |
151 | /**
152 | *
153 | * @param array $histogram - a value[count] array
154 | * @param int $area
155 | * @return float
156 | */
157 | protected function getEntropy($histogram, $area)
158 | {
159 | $value = 0.0;
160 |
161 | $colors = count($histogram);
162 | for ($idx = 0; $idx < $colors; $idx++) {
163 | // calculates the percentage of pixels having this color value
164 | $p = $histogram[$idx]->getColorCount() / $area;
165 | // A common way of representing entropy in scalar
166 | $value = $value + $p * log($p, 2);
167 | }
168 | // $value is always 0.0 or negative, so transform into positive scalar value
169 | return -$value;
170 | }
171 |
172 | /**
173 | * setBaseDimensions
174 | *
175 | * @param int $width
176 | * @param int $height
177 | * @access protected
178 | * @return $this
179 | */
180 | protected function setBaseDimensions($width, $height)
181 | {
182 | $this->baseDimension = ['width' => $width, 'height' => $height];
183 |
184 | return $this;
185 | }
186 |
187 | /**
188 | * getBaseDimension
189 | *
190 | * @param string $key width|height
191 | * @access protected
192 | * @return int
193 | */
194 | protected function getBaseDimension($key)
195 | {
196 | if (isset($this->baseDimension)) {
197 | return $this->baseDimension[$key];
198 | } elseif ($key == 'width') {
199 | return $this->originalImage->getImageWidth();
200 | } else {
201 | return $this->originalImage->getImageHeight();
202 | }
203 | }
204 |
205 | /**
206 | * get special offset for class
207 | *
208 | * @param \Imagick $original
209 | * @param int $targetWidth
210 | * @param int $targetHeight
211 | * @return array
212 | */
213 | abstract protected function getSpecialOffset(\Imagick $original, $targetWidth, $targetHeight);
214 | }
215 |
--------------------------------------------------------------------------------
/src/stojg/crop/CropBalanced.php:
--------------------------------------------------------------------------------
1 | getRandomEdgeOffset($original, $targetWidth, $targetHeight);
30 | }
31 |
32 | /**
33 | *
34 | * @param \Imagick $original
35 | * @param int $targetWidth
36 | * @param int $targetHeight
37 | * @return array
38 | */
39 | protected function getRandomEdgeOffset(\Imagick $original, $targetWidth, $targetHeight)
40 | {
41 | $measureImage = clone($original);
42 | // Enhance edges with radius 1
43 | $measureImage->edgeimage(1);
44 | // Turn image into a grayscale
45 | $measureImage->modulateImage(100, 0, 100);
46 | // Turn everything darker than this to pitch black
47 | $measureImage->blackThresholdImage("#101010");
48 | // Get the calculated offset for cropping
49 | return $this->getOffsetBalanced($targetWidth, $targetHeight);
50 | }
51 |
52 | /**
53 | *
54 | * @param int $targetWidth
55 | * @param int $targetHeight
56 | * @return array
57 | * @todo refactor so it follows DRY
58 | */
59 | public function getOffsetBalanced($targetWidth, $targetHeight)
60 | {
61 |
62 | $size = $this->originalImage->getImageGeometry();
63 |
64 | $points = array();
65 |
66 | $halfWidth = ceil($size['width']/2);
67 | $halfHeight = ceil($size['height']/2);
68 |
69 | // First quadrant
70 | $clone = clone($this->originalImage);
71 | $clone->cropimage($halfWidth, $halfHeight, 0, 0);
72 | $point = $this->getHighestEnergyPoint($clone);
73 | $points[] = array('x' => $point['x'], 'y' => $point['y'], 'sum' => $point['sum']);
74 |
75 | // Second quadrant
76 | $clone = clone($this->originalImage);
77 | $clone->cropimage($halfWidth, $halfHeight, $halfWidth, 0);
78 | $point = $this->getHighestEnergyPoint($clone);
79 | $points[] = array('x' => $point['x']+$halfWidth, 'y' => $point['y'], 'sum' => $point['sum']);
80 |
81 | // Third quadrant
82 | $clone = clone($this->originalImage);
83 | $clone->cropimage($halfWidth, $halfHeight, 0, $halfHeight);
84 | $point = $this->getHighestEnergyPoint($clone);
85 | $points[] = array('x' => $point['x'], 'y' => $point['y']+$halfHeight, 'sum' => $point['sum']);
86 |
87 | // Fourth quadrant
88 | $clone = clone($this->originalImage);
89 | $clone->cropimage($halfWidth, $halfHeight, $halfWidth, $halfHeight);
90 | $point = $point = $this->getHighestEnergyPoint($clone);
91 | $points[] = array('x' => $point['x']+$halfWidth, 'y' => $point['y']+$halfHeight, 'sum' => $point['sum']);
92 |
93 | // get the totalt sum value so we can find out a mean center point
94 | $totalWeight = array_reduce(
95 | $points,
96 | function ($result, $array) {
97 | return $result + $array['sum'];
98 | }
99 | );
100 |
101 | $centerX = 0;
102 | $centerY = 0;
103 |
104 | // Calulate the mean weighted center x and y
105 | $totalPoints = count($points);
106 | for ($idx=0; $idx < $totalPoints; $idx++) {
107 | $centerX += $points[$idx]['x'] * ($points[$idx]['sum'] / $totalWeight);
108 | $centerY += $points[$idx]['y'] * ($points[$idx]['sum'] / $totalWeight);
109 | }
110 |
111 | // From the weighted center point to the topleft corner of the crop would be
112 | $topleftX = max(0, ($centerX - $targetWidth / 2));
113 | $topleftY = max(0, ($centerY - $targetHeight / 2));
114 |
115 | // If we don't have enough width for the crop, back up $topleftX until
116 | // we can make the image meet $targetWidth
117 | if ($topleftX + $targetWidth > $size['width']) {
118 | $topleftX -= ($topleftX+$targetWidth) - $size['width'];
119 | }
120 | // If we don't have enough height for the crop, back up $topleftY until
121 | // we can make the image meet $targetHeight
122 | if ($topleftY+$targetHeight > $size['height']) {
123 | $topleftY -= ($topleftY+$targetHeight) - $size['height'];
124 | }
125 |
126 | return array('x'=>$topleftX, 'y'=>$topleftY);
127 | }
128 |
129 | /**
130 | * By doing random sampling from the image, find the most energetic point on the passed in
131 | * image
132 | *
133 | * @param \Imagick $image
134 | * @return array
135 | */
136 | protected function getHighestEnergyPoint(\Imagick $image)
137 | {
138 | $size = $image->getImageGeometry();
139 | // It's more performant doing random pixel uplook via GD
140 | $tmpFile = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'image' . rand();
141 | $image->writeimage($tmpFile);
142 | $im = imagecreatefromjpeg($tmpFile);
143 | $xcenter = 0;
144 | $ycenter = 0;
145 | $sum = 0;
146 | // Sample only sample 1/50 of of all the pixels in the image
147 | $sampleSize = round($size['height']*$size['width'])/50;
148 |
149 | for ($k=0; $k<$sampleSize; $k++) {
150 | $i = mt_rand(0, $size['width']-1);
151 | $j = mt_rand(0, $size['height']-1);
152 |
153 | $rgb = imagecolorat($im, $i, $j);
154 | $r = ($rgb >> 16) & 0xFF;
155 | $g = ($rgb >> 8) & 0xFF;
156 | $b = $rgb & 0xFF;
157 |
158 | $val = $this->rgb2bw($r, $g, $b);
159 | $sum += $val;
160 | $xcenter += ($i+1)*$val;
161 | $ycenter += ($j+1)*$val;
162 | }
163 |
164 | if ($sum) {
165 | $xcenter /= $sum;
166 | $ycenter /= $sum;
167 | }
168 |
169 | $point = array('x' => $xcenter, 'y' => $ycenter, 'sum' => $sum/round($size['height']*$size['width']));
170 |
171 | return $point;
172 | }
173 | }
174 |
--------------------------------------------------------------------------------
/docs/markers.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | » Markers
7 |
8 |
9 |
10 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
90 |
91 |
92 |
93 |
94 | Javascript is disabled; several features are only available if Javascript is enabled.
95 |
96 |
97 |
98 |
99 |
100 |
105 |
106 |
107 |
108 |
109 | \
110 | Markers
111 |
112 |
113 |
No markers have been found in this project.
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
127 |
128 |
129 |
--------------------------------------------------------------------------------
/docs/css/bootstrap-responsive.min.css:
--------------------------------------------------------------------------------
1 |
2 | .hidden{display:none;visibility:hidden;}
3 | @media (max-width:480px){.nav-collapse{-webkit-transform:translate3d(0, 0, 0);} .page-header h1 small{display:block;line-height:18px;} input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input{display:block;width:100%;height:28px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;} .input-prepend input[class*="span"],.input-append input[class*="span"]{width:auto;} input[type="checkbox"],input[type="radio"]{border:1px solid #ccc;} .form-horizontal .control-group>label{float:none;width:auto;padding-top:0;text-align:left;} .form-horizontal .controls{margin-left:0;} .form-horizontal .control-list{padding-top:0;} .form-horizontal .form-actions{padding-left:10px;padding-right:10px;} .modal{position:absolute;top:10px;left:10px;right:10px;width:auto;margin:0;}.modal.fade.in{top:auto;} .modal-header .close{padding:10px;margin:-10px;} .carousel-caption{position:static;}}@media (max-width:768px){.container{width:auto;padding:0 20px;} .row-fluid{width:100%;} .row{margin-left:0;} .row>[class*="span"],.row-fluid>[class*="span"]{float:none;display:block;width:auto;margin:0;}}@media (min-width:768px) and (max-width:980px){.row{margin-left:-20px;*zoom:1;}.row:before,.row:after{display:table;content:"";} .row:after{clear:both;} [class*="span"]{float:left;margin-left:20px;} .span1{width:42px;} .span2{width:104px;} .span3{width:166px;} .span4{width:228px;} .span5{width:290px;} .span6{width:352px;} .span7{width:414px;} .span8{width:476px;} .span9{width:538px;} .span10{width:600px;} .span11{width:662px;} .span12,.container{width:724px;} .offset1{margin-left:82px;} .offset2{margin-left:144px;} .offset3{margin-left:206px;} .offset4{margin-left:268px;} .offset5{margin-left:330px;} .offset6{margin-left:392px;} .offset7{margin-left:454px;} .offset8{margin-left:516px;} .offset9{margin-left:578px;} .offset10{margin-left:640px;} .offset11{margin-left:702px;} .row-fluid{width:100%;*zoom:1;}.row-fluid:before,.row-fluid:after{display:table;content:"";} .row-fluid:after{clear:both;} .row-fluid>[class*="span"]{float:left;margin-left:2.762430939%;} .row-fluid>[class*="span"]:first-child{margin-left:0;} .row-fluid .span1{width:5.801104972%;} .row-fluid .span2{width:14.364640883%;} .row-fluid .span3{width:22.928176794%;} .row-fluid .span4{width:31.491712705%;} .row-fluid .span5{width:40.055248616%;} .row-fluid .span6{width:48.618784527%;} .row-fluid .span7{width:57.182320438000005%;} .row-fluid .span8{width:65.74585634900001%;} .row-fluid .span9{width:74.30939226%;} .row-fluid .span10{width:82.87292817100001%;} .row-fluid .span11{width:91.436464082%;} .row-fluid .span12{width:99.999999993%;} input.span1,textarea.span1,.uneditable-input.span1{width:32px;} input.span2,textarea.span2,.uneditable-input.span2{width:94px;} input.span3,textarea.span3,.uneditable-input.span3{width:156px;} input.span4,textarea.span4,.uneditable-input.span4{width:218px;} input.span5,textarea.span5,.uneditable-input.span5{width:280px;} input.span6,textarea.span6,.uneditable-input.span6{width:342px;} input.span7,textarea.span7,.uneditable-input.span7{width:404px;} input.span8,textarea.span8,.uneditable-input.span8{width:466px;} input.span9,textarea.span9,.uneditable-input.span9{width:528px;} input.span10,textarea.span10,.uneditable-input.span10{width:590px;} input.span11,textarea.span11,.uneditable-input.span11{width:652px;} input.span12,textarea.span12,.uneditable-input.span12{width:714px;}}@media (max-width:980px){body{padding-top:0;} .navbar-fixed-top{position:static;margin-bottom:18px;} .navbar-fixed-top .navbar-inner{padding:5px;} .navbar .container{width:auto;padding:0;} .navbar .brand{padding-left:10px;padding-right:10px;margin:0 0 0 -5px;} .navbar .nav-collapse{clear:left;} .navbar .nav{float:none;margin:0 0 9px;} .navbar .nav>li{float:none;} .navbar .nav>li>a{margin-bottom:2px;} .navbar .nav>.divider-vertical{display:none;} .navbar .nav>li>a,.navbar .dropdown-menu a{padding:6px 15px;font-weight:bold;color:#999999;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;} .navbar .dropdown-menu li+li a{margin-bottom:2px;} .navbar .nav>li>a:hover,.navbar .dropdown-menu a:hover{background-color:#222222;} .navbar .dropdown-menu{position:static;top:auto;left:auto;float:none;display:block;max-width:none;margin:0 15px;padding:0;background-color:transparent;border:none;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;} .navbar .dropdown-menu:before,.navbar .dropdown-menu:after{display:none;} .navbar .dropdown-menu .divider{display:none;} .navbar-form,.navbar-search{float:none;padding:9px 15px;margin:9px 0;border-top:1px solid #222222;border-bottom:1px solid #222222;-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.1);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.1);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.1);} .navbar .nav.pull-right{float:none;margin-left:0;} .navbar-static .navbar-inner{padding-left:10px;padding-right:10px;} .btn-navbar{display:block;} .nav-collapse{overflow:hidden;height:0;}}@media (min-width:980px){.nav-collapse.collapse{height:auto !important;}}@media (min-width:1200px){.row{margin-left:-30px;*zoom:1;}.row:before,.row:after{display:table;content:"";} .row:after{clear:both;} [class*="span"]{float:left;margin-left:30px;} .span1{width:70px;} .span2{width:170px;} .span3{width:270px;} .span4{width:370px;} .span5{width:470px;} .span6{width:570px;} .span7{width:670px;} .span8{width:770px;} .span9{width:870px;} .span10{width:970px;} .span11{width:1070px;} .span12,.container{width:1170px;} .offset1{margin-left:130px;} .offset2{margin-left:230px;} .offset3{margin-left:330px;} .offset4{margin-left:430px;} .offset5{margin-left:530px;} .offset6{margin-left:630px;} .offset7{margin-left:730px;} .offset8{margin-left:830px;} .offset9{margin-left:930px;} .offset10{margin-left:1030px;} .offset11{margin-left:1130px;} .row-fluid{width:100%;*zoom:1;}.row-fluid:before,.row-fluid:after{display:table;content:"";} .row-fluid:after{clear:both;} .row-fluid>[class*="span"]{float:left;margin-left:2.564102564%;} .row-fluid>[class*="span"]:first-child{margin-left:0;} .row-fluid .span1{width:5.982905983%;} .row-fluid .span2{width:14.529914530000001%;} .row-fluid .span3{width:23.076923077%;} .row-fluid .span4{width:31.623931624%;} .row-fluid .span5{width:40.170940171000005%;} .row-fluid .span6{width:48.717948718%;} .row-fluid .span7{width:57.264957265%;} .row-fluid .span8{width:65.81196581200001%;} .row-fluid .span9{width:74.358974359%;} .row-fluid .span10{width:82.905982906%;} .row-fluid .span11{width:91.45299145300001%;} .row-fluid .span12{width:100%;} input.span1,textarea.span1,.uneditable-input.span1{width:60px;} input.span2,textarea.span2,.uneditable-input.span2{width:160px;} input.span3,textarea.span3,.uneditable-input.span3{width:260px;} input.span4,textarea.span4,.uneditable-input.span4{width:360px;} input.span5,textarea.span5,.uneditable-input.span5{width:460px;} input.span6,textarea.span6,.uneditable-input.span6{width:560px;} input.span7,textarea.span7,.uneditable-input.span7{width:660px;} input.span8,textarea.span8,.uneditable-input.span8{width:760px;} input.span9,textarea.span9,.uneditable-input.span9{width:860px;} input.span10,textarea.span10,.uneditable-input.span10{width:960px;} input.span11,textarea.span11,.uneditable-input.span11{width:1060px;} input.span12,textarea.span12,.uneditable-input.span12{width:1160px;} .thumbnails{margin-left:-30px;} .thumbnails>li{margin-left:30px;}}
4 |
--------------------------------------------------------------------------------
/docs/js/SVGPan.js:
--------------------------------------------------------------------------------
1 | /**
2 | * SVGPan library 1.2 - phpDocumentor1
3 | * ====================
4 | *
5 | * Given an unique existing element with id "viewport", including the
6 | * the library into any SVG adds the following capabilities:
7 | *
8 | * - Mouse panning
9 | * - Mouse zooming (using the wheel)
10 | * - Object dargging
11 | *
12 | * Known issues:
13 | *
14 | * - Zooming (while panning) on Safari has still some issues
15 | *
16 | * Releases:
17 | *
18 | * 1.2 - phpDocumentor1, Fri Apr 08 19:19:00 CET 2011, Mike van Riel
19 | * Increased zoom speed with 20%
20 | * Disabled element moving functionality
21 | *
22 | * 1.2, Sat Mar 20 08:42:50 GMT 2010, Zeng Xiaohui
23 | * Fixed a bug with browser mouse handler interaction
24 | *
25 | * 1.1, Wed Feb 3 17:39:33 GMT 2010, Zeng Xiaohui
26 | * Updated the zoom code to support the mouse wheel on Safari/Chrome
27 | *
28 | * 1.0, Andrea Leofreddi
29 | * First release
30 | *
31 | * This code is licensed under the following BSD license:
32 | *
33 | * Copyright 2009-2010 Andrea Leofreddi . All rights reserved.
34 | *
35 | * Redistribution and use in source and binary forms, with or without modification, are
36 | * permitted provided that the following conditions are met:
37 | *
38 | * 1. Redistributions of source code must retain the above copyright notice, this list of
39 | * conditions and the following disclaimer.
40 | *
41 | * 2. Redistributions in binary form must reproduce the above copyright notice, this list
42 | * of conditions and the following disclaimer in the documentation and/or other materials
43 | * provided with the distribution.
44 | *
45 | * THIS SOFTWARE IS PROVIDED BY Andrea Leofreddi ``AS IS'' AND ANY EXPRESS OR IMPLIED
46 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
47 | * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Andrea Leofreddi OR
48 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
49 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
50 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
51 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
52 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
53 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
54 | *
55 | * The views and conclusions contained in the software and documentation are those of the
56 | * authors and should not be interpreted as representing official policies, either expressed
57 | * or implied, of Andrea Leofreddi.
58 | */
59 |
60 | var root = document.documentElement;
61 |
62 | var state = 'none', stateTarget, stateOrigin, stateTf;
63 |
64 | setupHandlers(root);
65 |
66 | /**
67 | * Register handlers
68 | */
69 | function setupHandlers(root){
70 | setAttributes(root, {
71 | "onmouseup" : "add(evt)",
72 | "onmousedown" : "handleMouseDown(evt)",
73 | "onmousemove" : "handleMouseMove(evt)",
74 | "onmouseup" : "handleMouseUp(evt)",
75 | // "onmouseout" : "handleMouseUp(evt)" // Decomment this to stop the pan functionality when dragging out of the SVG element
76 | });
77 |
78 | if(navigator.userAgent.toLowerCase().indexOf('webkit') >= 0)
79 | window.addEventListener('mousewheel', handleMouseWheel, false); // Chrome/Safari
80 | else
81 | window.addEventListener('DOMMouseScroll', handleMouseWheel, false); // Others
82 | }
83 |
84 | /**
85 | * Instance an SVGPoint object with given event coordinates.
86 | */
87 | function getEventPoint(evt) {
88 | var p = root.createSVGPoint();
89 |
90 | p.x = evt.clientX;
91 | p.y = evt.clientY;
92 |
93 | return p;
94 | }
95 |
96 | /**
97 | * Sets the current transform matrix of an element.
98 | */
99 | function setCTM(element, matrix) {
100 | var s = "matrix(" + matrix.a + "," + matrix.b + "," + matrix.c + "," + matrix.d + "," + matrix.e + "," + matrix.f + ")";
101 |
102 | element.setAttribute("transform", s);
103 | }
104 |
105 | /**
106 | * Dumps a matrix to a string (useful for debug).
107 | */
108 | function dumpMatrix(matrix) {
109 | var s = "[ " + matrix.a + ", " + matrix.c + ", " + matrix.e + "\n " + matrix.b + ", " + matrix.d + ", " + matrix.f + "\n 0, 0, 1 ]";
110 |
111 | return s;
112 | }
113 |
114 | /**
115 | * Sets attributes of an element.
116 | */
117 | function setAttributes(element, attributes){
118 | for (i in attributes)
119 | element.setAttributeNS(null, i, attributes[i]);
120 | }
121 |
122 | /**
123 | * Handle mouse move event.
124 | */
125 | function handleMouseWheel(evt) {
126 | if(evt.preventDefault)
127 | evt.preventDefault();
128 |
129 | evt.returnValue = false;
130 |
131 | var svgDoc = evt.target.ownerDocument;
132 |
133 | var delta;
134 |
135 | if(evt.wheelDelta)
136 | delta = evt.wheelDelta / 3600; // Chrome/Safari
137 | else
138 | delta = evt.detail / -90; // Mozilla
139 |
140 | var z = 1 + (delta * 1.2); // Zoom factor: 0.9/1.1
141 |
142 | var g = svgDoc.getElementById("viewport");
143 |
144 | var p = getEventPoint(evt);
145 |
146 | p = p.matrixTransform(g.getCTM().inverse());
147 |
148 | // Compute new scale matrix in current mouse position
149 | var k = root.createSVGMatrix().translate(p.x, p.y).scale(z).translate(-p.x, -p.y);
150 |
151 | setCTM(g, g.getCTM().multiply(k));
152 |
153 | stateTf = stateTf.multiply(k.inverse());
154 | }
155 |
156 | /**
157 | * Handle mouse move event.
158 | */
159 | function handleMouseMove(evt) {
160 | if(evt.preventDefault)
161 | evt.preventDefault();
162 |
163 | evt.returnValue = false;
164 |
165 | var svgDoc = evt.target.ownerDocument;
166 |
167 | var g = svgDoc.getElementById("viewport");
168 |
169 | if(state == 'pan') {
170 | // Pan mode
171 | var p = getEventPoint(evt).matrixTransform(stateTf);
172 |
173 | setCTM(g, stateTf.inverse().translate(p.x - stateOrigin.x, p.y - stateOrigin.y));
174 | } else if(state == 'move') {
175 | // Move mode
176 | var p = getEventPoint(evt).matrixTransform(g.getCTM().inverse());
177 |
178 | setCTM(stateTarget, root.createSVGMatrix().translate(p.x - stateOrigin.x, p.y - stateOrigin.y).multiply(g.getCTM().inverse()).multiply(stateTarget.getCTM()));
179 |
180 | stateOrigin = p;
181 | }
182 | }
183 |
184 | /**
185 | * Handle click event.
186 | */
187 | function handleMouseDown(evt) {
188 | if(evt.preventDefault)
189 | evt.preventDefault();
190 |
191 | evt.returnValue = false;
192 |
193 | var svgDoc = evt.target.ownerDocument;
194 |
195 | var g = svgDoc.getElementById("viewport");
196 |
197 | // if(evt.target.tagName == "svg") {
198 | // Pan mode
199 | state = 'pan';
200 |
201 | stateTf = g.getCTM().inverse();
202 |
203 | stateOrigin = getEventPoint(evt).matrixTransform(stateTf);
204 | // } else {
205 | // Move mode
206 | // state = 'move';
207 | //
208 | // stateTarget = evt.target;
209 | //
210 | // stateTf = g.getCTM().inverse();
211 | //
212 | // stateOrigin = getEventPoint(evt).matrixTransform(stateTf);
213 | // }
214 | }
215 |
216 | /**
217 | * Handle mouse button release event.
218 | */
219 | function handleMouseUp(evt) {
220 | if(evt.preventDefault)
221 | evt.preventDefault();
222 |
223 | evt.returnValue = false;
224 |
225 | var svgDoc = evt.target.ownerDocument;
226 |
227 | if(state == 'pan' || state == 'move') {
228 | // Quit pan mode
229 | state = '';
230 | }
231 | }
232 |
233 |
--------------------------------------------------------------------------------
/docs/js/template.js:
--------------------------------------------------------------------------------
1 | $.browser.chrome = /chrome/.test(navigator.userAgent.toLowerCase());
2 | $.browser.ipad = /ipad/.test(navigator.userAgent.toLowerCase());
3 |
4 | /**
5 | * Initializes page contents for progressive enhancement.
6 | */
7 | function initializeContents()
8 | {
9 | // hide all more buttons because they are not needed with JS
10 | $(".element a.more").hide();
11 |
12 | $(".clickable.class,.clickable.interface").click(function() {
13 | document.location = $("a.more", this).attr('href');
14 | });
15 |
16 | // change the cursor to a pointer to make it more explicit that this it clickable
17 | // do a background color change on hover to emphasize the clickability eveb more
18 | // we do not use CSS for this because when JS is disabled this behaviour does not
19 | // apply and we do not want the hover
20 | $(".element.method,.element.function,.element.class.clickable,.element.interface.clickable")
21 | .css("cursor", "pointer")
22 | .hover(function() {
23 | $(this).css('backgroundColor', '#F8FDF6')
24 | }, function(){
25 | $(this).css('backgroundColor', 'white')}
26 | );
27 |
28 | // do not show tooltips on iPad; it will cause the user having to click twice
29 | if (!$.browser.ipad) {
30 | $('.btn-group.visibility,.btn-group.view,.btn-group.type-filter')
31 | .tooltip({'placement':'bottom'});
32 | }
33 |
34 | $('.btn-group.visibility,.btn-group.view,.btn-group.type-filter')
35 | .show()
36 | .find('button')
37 | .find('i').click(function(){ $(this).parent().click(); });
38 |
39 | // set the events for the visibility buttons and enable by default.
40 | $('.visibility button.public').click(function(){
41 | $('.element.public,.side-nav li.public').toggle($(this).hasClass('active'));
42 | }).click();
43 | $('.visibility button.protected').click(function(){
44 | $('.element.protected,.side-nav li.protected').toggle($(this).hasClass('active'));
45 | }).click();
46 | $('.visibility button.private').click(function(){
47 | $('.element.private,.side-nav li.private').toggle($(this).hasClass('active'));
48 | }).click();
49 | $('.visibility button.inherited').click(function(){
50 | $('.element.inherited,.side-nav li.inherited').toggle($(this).hasClass('active'));
51 | }).click();
52 |
53 | $('.type-filter button.critical').click(function(){
54 | $('tr.critical').toggle($(this).hasClass('active'));
55 | });
56 | $('.type-filter button.error').click(function(){
57 | $('tr.error').toggle($(this).hasClass('active'));
58 | });
59 | $('.type-filter button.notice').click(function(){
60 | $('tr.notice').toggle($(this).hasClass('active'));
61 | });
62 |
63 | $('.view button.details').click(function(){
64 | $('.side-nav li.view-simple').removeClass('view-simple');
65 | }).button('toggle').click();
66 |
67 | $('.view button.details').click(function(){
68 | $('.side-nav li.view-simple').removeClass('view-simple');
69 | }).button('toggle').click();
70 | $('.view button.simple').click(function(){
71 | $('.side-nav li').addClass('view-simple');
72 | });
73 |
74 | // sorting example
75 | // $('ol li').sort(
76 | // function(a, b) { return a.innerHTML.toLowerCase() > b.innerHTML.toLowerCase() ? 1 : -1; }
77 | // ).appendTo('ol');
78 | }
79 |
80 | $(document).ready(function() {
81 | prettyPrint();
82 |
83 | initializeContents();
84 |
85 | // do not show tooltips on iPad; it will cause the user having to click twice
86 | if(!$.browser.ipad) {
87 | $(".side-nav a").tooltip({'placement': 'top'});
88 | }
89 |
90 | // chrome cannot deal with certain situations; warn the user about reduced features
91 | if ($.browser.chrome && (window.location.protocol == 'file:')) {
92 | $("body > .container").prepend(
93 | ''
97 | );
98 | }
99 |
100 | $('ul.nav-namespaces li a, ul.nav-packages li a').click(function(){
101 | // Google Chrome does not do Ajax locally
102 | if ($.browser.chrome && (window.location.protocol == 'file:'))
103 | {
104 | return true;
105 | }
106 |
107 | $(this).parents('.side-nav').find('.active').removeClass('active');
108 | $(this).parent().addClass('active');
109 | $('div.namespace-contents').load(
110 | this.href + ' div.namespace-contents', function(){
111 | initializeContents();
112 | $(window).scrollTop($('div.namespace-contents').position().top);
113 | }
114 | );
115 | $('div.package-contents').load(
116 | this.href + ' div.package-contents', function(){
117 | initializeContents();
118 | $(window).scrollTop($('div.package-contents').position().top);
119 | }
120 | );
121 |
122 | return false;
123 | });
124 |
125 | function filterPath(string)
126 | {
127 | return string
128 | .replace(/^\//, '')
129 | .replace(/(index|default).[a-zA-Z]{3,4}$/, '')
130 | .replace(/\/$/, '');
131 | }
132 |
133 | var locationPath = filterPath(location.pathname);
134 |
135 | // the ipad already smoothly scrolls and does not detect the scrollable
136 | // element if top=0; as such we disable this behaviour for the iPad
137 | if (!$.browser.ipad) {
138 | $('a[href*=#]').each(function ()
139 | {
140 | var thisPath = filterPath(this.pathname) || locationPath;
141 | if (locationPath == thisPath && (location.hostname == this.hostname || !this.hostname) && this.hash.replace(/#/, ''))
142 | {
143 | var target = decodeURIComponent(this.hash.replace(/#/,''));
144 | // note: I'm using attribute selector, because id selector can't match elements with '$'
145 | var $target = $('[id="'+target+'"]');
146 |
147 | if ($target.length > 0)
148 | {
149 | $(this).click(function (event)
150 | {
151 | var scrollElem = scrollableElement('html', 'body');
152 | var targetOffset = $target.offset().top;
153 |
154 | event.preventDefault();
155 | $(scrollElem).animate({scrollTop:targetOffset}, 400, function ()
156 | {
157 | location.hash = target;
158 | });
159 | });
160 | }
161 | }
162 | });
163 | }
164 |
165 | // use the first element that is "scrollable"
166 | function scrollableElement(els)
167 | {
168 | for (var i = 0, argLength = arguments.length; i < argLength; i++)
169 | {
170 | var el = arguments[i], $scrollElement = $(el);
171 | if ($scrollElement.scrollTop() > 0)
172 | {
173 | return el;
174 | }
175 | else
176 | {
177 | $scrollElement.scrollTop(1);
178 | var isScrollable = $scrollElement.scrollTop() > 0;
179 | $scrollElement.scrollTop(0);
180 | if (isScrollable)
181 | {
182 | return el;
183 | }
184 | }
185 | }
186 | return [];
187 | }
188 |
189 | // Hide API Documentation menu if it's empty
190 | $('.nav .dropdown a[href=#api]').next().filter(function(el) {
191 | if ($(el).children().length == 0) {
192 | return true;
193 | }
194 | }).parent().hide();
195 | });
196 |
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | API Documentation
7 |
8 |
9 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
89 |
90 |
91 |
92 |
93 | Javascript is disabled; several features are only available if Javascript is enabled.
94 |
95 |
96 |
97 |
98 |
API Documentation
99 | Documentation
100 |
101 |
102 |
103 |
104 |
111 |
112 |
118 |
119 |
120 |
145 |
146 |
147 |
148 |
154 |
155 |
156 |
--------------------------------------------------------------------------------
/docs/js/jquery.treeview.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Treeview 1.5pre - jQuery plugin to hide and show branches of a tree
3 | *
4 | * http://bassistance.de/jquery-plugins/jquery-plugin-treeview/
5 | * http://docs.jquery.com/Plugins/Treeview
6 | *
7 | * Copyright (c) 2007 Jörn Zaefferer
8 | *
9 | * Dual licensed under the MIT and GPL licenses:
10 | * http://www.opensource.org/licenses/mit-license.php
11 | * http://www.gnu.org/licenses/gpl.html
12 | *
13 | * Revision: $Id: jquery.treeview.js 5759 2008-07-01 07:50:28Z joern.zaefferer $
14 | *
15 | */
16 |
17 | ;(function($) {
18 |
19 | // TODO rewrite as a widget, removing all the extra plugins
20 | $.extend($.fn, {
21 | swapClass: function(c1, c2) {
22 | var c1Elements = this.filter('.' + c1);
23 | this.filter('.' + c2).removeClass(c2).addClass(c1);
24 | c1Elements.removeClass(c1).addClass(c2);
25 | return this;
26 | },
27 | replaceClass: function(c1, c2) {
28 | return this.filter('.' + c1).removeClass(c1).addClass(c2).end();
29 | },
30 | hoverClass: function(className) {
31 | className = className || "hover";
32 | return this.hover(function() {
33 | $(this).addClass(className);
34 | }, function() {
35 | $(this).removeClass(className);
36 | });
37 | },
38 | heightToggle: function(animated, callback) {
39 | animated ?
40 | this.animate({ height: "toggle" }, animated, callback) :
41 | this.each(function(){
42 | jQuery(this)[ jQuery(this).is(":hidden") ? "show" : "hide" ]();
43 | if(callback)
44 | callback.apply(this, arguments);
45 | });
46 | },
47 | heightHide: function(animated, callback) {
48 | if (animated) {
49 | this.animate({ height: "hide" }, animated, callback);
50 | } else {
51 | this.hide();
52 | if (callback)
53 | this.each(callback);
54 | }
55 | },
56 | prepareBranches: function(settings) {
57 | if (!settings.prerendered) {
58 | // mark last tree items
59 | this.filter(":last-child:not(ul)").addClass(CLASSES.last);
60 | // collapse whole tree, or only those marked as closed, anyway except those marked as open
61 | this.filter((settings.collapsed ? "" : "." + CLASSES.closed) + ":not(." + CLASSES.open + ")").find(">ul").hide();
62 | }
63 | // return all items with sublists
64 | return this.filter(":has(>ul)");
65 | },
66 | applyClasses: function(settings, toggler) {
67 | // TODO use event delegation
68 | this.filter(":has(>ul):not(:has(>a))").find(">span").unbind("click.treeview").bind("click.treeview", function(event) {
69 | // don't handle click events on children, eg. checkboxes
70 | if ( this == event.target )
71 | toggler.apply($(this).next());
72 | }).add( $("a", this) ).hoverClass();
73 |
74 | if (!settings.prerendered) {
75 | // handle closed ones first
76 | this.filter(":has(>ul:hidden)")
77 | .addClass(CLASSES.expandable)
78 | .replaceClass(CLASSES.last, CLASSES.lastExpandable);
79 |
80 | // handle open ones
81 | this.not(":has(>ul:hidden)")
82 | .addClass(CLASSES.collapsable)
83 | .replaceClass(CLASSES.last, CLASSES.lastCollapsable);
84 |
85 | // create hitarea if not present
86 | var hitarea = this.find("div." + CLASSES.hitarea);
87 | if (!hitarea.length)
88 | hitarea = this.prepend("
").find("div." + CLASSES.hitarea);
89 | hitarea.removeClass().addClass(CLASSES.hitarea).each(function() {
90 | var classes = "";
91 | $.each($(this).parent().attr("class").split(" "), function() {
92 | classes += this + "-hitarea ";
93 | });
94 | $(this).addClass( classes );
95 | })
96 | }
97 |
98 | // apply event to hitarea
99 | this.find("div." + CLASSES.hitarea).click( toggler );
100 | },
101 | treeview: function(settings) {
102 |
103 | settings = $.extend({
104 | cookieId: "treeview"
105 | }, settings);
106 |
107 | if ( settings.toggle ) {
108 | var callback = settings.toggle;
109 | settings.toggle = function() {
110 | return callback.apply($(this).parent()[0], arguments);
111 | };
112 | }
113 |
114 | // factory for treecontroller
115 | function treeController(tree, control) {
116 | // factory for click handlers
117 | function handler(filter) {
118 | return function() {
119 | // reuse toggle event handler, applying the elements to toggle
120 | // start searching for all hitareas
121 | toggler.apply( $("div." + CLASSES.hitarea, tree).filter(function() {
122 | // for plain toggle, no filter is provided, otherwise we need to check the parent element
123 | return filter ? $(this).parent("." + filter).length : true;
124 | }) );
125 | return false;
126 | };
127 | }
128 | // click on first element to collapse tree
129 | $("a:eq(0)", control).click( handler(CLASSES.collapsable) );
130 | // click on second to expand tree
131 | $("a:eq(1)", control).click( handler(CLASSES.expandable) );
132 | // click on third to toggle tree
133 | $("a:eq(2)", control).click( handler() );
134 | }
135 |
136 | // handle toggle event
137 | function toggler() {
138 | $(this)
139 | .parent()
140 | // swap classes for hitarea
141 | .find(">.hitarea")
142 | .swapClass( CLASSES.collapsableHitarea, CLASSES.expandableHitarea )
143 | .swapClass( CLASSES.lastCollapsableHitarea, CLASSES.lastExpandableHitarea )
144 | .end()
145 | // swap classes for parent li
146 | .swapClass( CLASSES.collapsable, CLASSES.expandable )
147 | .swapClass( CLASSES.lastCollapsable, CLASSES.lastExpandable )
148 | // find child lists
149 | .find( ">ul" )
150 | // toggle them
151 | .heightToggle( settings.animated, settings.toggle );
152 | if ( settings.unique ) {
153 | $(this).parent()
154 | .siblings()
155 | // swap classes for hitarea
156 | .find(">.hitarea")
157 | .replaceClass( CLASSES.collapsableHitarea, CLASSES.expandableHitarea )
158 | .replaceClass( CLASSES.lastCollapsableHitarea, CLASSES.lastExpandableHitarea )
159 | .end()
160 | .replaceClass( CLASSES.collapsable, CLASSES.expandable )
161 | .replaceClass( CLASSES.lastCollapsable, CLASSES.lastExpandable )
162 | .find( ">ul" )
163 | .heightHide( settings.animated, settings.toggle );
164 | }
165 | }
166 | this.data("toggler", toggler);
167 |
168 | function serialize() {
169 | function binary(arg) {
170 | return arg ? 1 : 0;
171 | }
172 | var data = [];
173 | branches.each(function(i, e) {
174 | data[i] = $(e).is(":has(>ul:visible)") ? 1 : 0;
175 | });
176 | $.cookie(settings.cookieId, data.join(""), settings.cookieOptions );
177 | }
178 |
179 | function deserialize() {
180 | var stored = $.cookie(settings.cookieId);
181 | if ( stored ) {
182 | var data = stored.split("");
183 | branches.each(function(i, e) {
184 | $(e).find(">ul")[ parseInt(data[i]) ? "show" : "hide" ]();
185 | });
186 | }
187 | }
188 |
189 | // add treeview class to activate styles
190 | this.addClass("treeview");
191 |
192 | // prepare branches and find all tree items with child lists
193 | var branches = this.find("li").prepareBranches(settings);
194 |
195 | switch(settings.persist) {
196 | case "cookie":
197 | var toggleCallback = settings.toggle;
198 | settings.toggle = function() {
199 | serialize();
200 | if (toggleCallback) {
201 | toggleCallback.apply(this, arguments);
202 | }
203 | };
204 | deserialize();
205 | break;
206 | case "location":
207 | var current = this.find("a").filter(function() {
208 | return this.href.toLowerCase() == location.href.toLowerCase();
209 | });
210 | if ( current.length ) {
211 | // TODO update the open/closed classes
212 | var items = current.addClass("selected").parents("ul, li").add( current.next() ).show();
213 | if (settings.prerendered) {
214 | // if prerendered is on, replicate the basic class swapping
215 | items.filter("li")
216 | .swapClass( CLASSES.collapsable, CLASSES.expandable )
217 | .swapClass( CLASSES.lastCollapsable, CLASSES.lastExpandable )
218 | .find(">.hitarea")
219 | .swapClass( CLASSES.collapsableHitarea, CLASSES.expandableHitarea )
220 | .swapClass( CLASSES.lastCollapsableHitarea, CLASSES.lastExpandableHitarea );
221 | }
222 | }
223 | break;
224 | }
225 |
226 | branches.applyClasses(settings, toggler);
227 |
228 | // if control option is set, create the treecontroller and show it
229 | if ( settings.control ) {
230 | treeController(this, settings.control);
231 | $(settings.control).show();
232 | }
233 |
234 | return this;
235 | }
236 | });
237 |
238 | // classes used by the plugin
239 | // need to be styled via external stylesheet, see first example
240 | $.treeview = {};
241 | var CLASSES = ($.treeview.classes = {
242 | open: "open",
243 | closed: "closed",
244 | expandable: "expandable",
245 | expandableHitarea: "expandable-hitarea",
246 | lastExpandableHitarea: "lastExpandable-hitarea",
247 | collapsable: "collapsable",
248 | collapsableHitarea: "collapsable-hitarea",
249 | lastCollapsableHitarea: "lastCollapsable-hitarea",
250 | lastCollapsable: "lastCollapsable",
251 | lastExpandable: "lastExpandable",
252 | last: "last",
253 | hitarea: "hitarea"
254 | });
255 |
256 | })(jQuery);
--------------------------------------------------------------------------------
/docs/phpdoc-cache-51/phpdoc-cache-file_28dd9fff2c7f32cbf7c5a9d47b836f35.dat:
--------------------------------------------------------------------------------
1 | O:39:"phpDocumentor\Descriptor\FileDescriptor":20:{s:7:" * hash";s:32:"c7ec93a76641ed658c940d5fe3f7bc90";s:9:" * source";s:1123:"getCenterOffset($original, $targetWidth, $targetHeight);
28 | }
29 |
30 | /**
31 | * Get the cropping offset for the image based on the center of the image
32 | *
33 | * @param \Imagick $image
34 | * @param int $targetWidth
35 | * @param int $targetHeight
36 | * @return array
37 | */
38 | protected function getCenterOffset(\Imagick $image, $targetWidth, $targetHeight)
39 | {
40 | $size = $image->getImageGeometry();
41 | $originalWidth = $size['width'];
42 | $originalHeight = $size['height'];
43 | $goalX = (int)(($originalWidth-$targetWidth)/2);
44 | $goalY = (int)(($originalHeight-$targetHeight)/2);
45 | return array('x' => $goalX, 'y' => $goalY);
46 | }
47 | }
48 | ";s:20:" * namespace_aliases";O:35:"phpDocumentor\Descriptor\Collection":1:{s:8:" * items";a:0:{}}s:11:" * includes";O:35:"phpDocumentor\Descriptor\Collection":1:{s:8:" * items";a:0:{}}s:12:" * constants";O:35:"phpDocumentor\Descriptor\Collection":1:{s:8:" * items";a:0:{}}s:12:" * functions";O:35:"phpDocumentor\Descriptor\Collection":1:{s:8:" * items";a:0:{}}s:10:" * classes";O:35:"phpDocumentor\Descriptor\Collection":1:{s:8:" * items";a:1:{s:10:"CropCenter";O:40:"phpDocumentor\Descriptor\ClassDescriptor":17:{s:9:" * parent";s:16:"\stojg\crop\Crop";s:13:" * implements";O:35:"phpDocumentor\Descriptor\Collection":1:{s:8:" * items";a:0:{}}s:11:" * abstract";b:0;s:8:" * final";b:0;s:12:" * constants";O:35:"phpDocumentor\Descriptor\Collection":1:{s:8:" * items";a:0:{}}s:13:" * properties";O:35:"phpDocumentor\Descriptor\Collection":1:{s:8:" * items";a:0:{}}s:10:" * methods";O:35:"phpDocumentor\Descriptor\Collection":1:{s:8:" * items";a:2:{s:16:"getSpecialOffset";O:41:"phpDocumentor\Descriptor\MethodDescriptor":16:{s:9:" * parent";r:14;s:11:" * abstract";b:0;s:8:" * final";b:0;s:9:" * static";b:0;s:13:" * visibility";s:9:"protected";s:12:" * arguments";O:35:"phpDocumentor\Descriptor\Collection":1:{s:8:" * items";a:3:{s:9:"$original";O:43:"phpDocumentor\Descriptor\ArgumentDescriptor":13:{s:8:" * types";a:1:{i:0;s:8:"\Imagick";}s:10:" * default";N;s:14:" * byReference";b:0;s:8:" * fqsen";s:0:"";s:7:" * name";s:9:"$original";s:12:" * namespace";N;s:10:" * package";N;s:10:" * summary";s:0:"";s:14:" * description";s:0:"";s:7:" * path";s:0:"";s:7:" * line";i:0;s:7:" * tags";O:35:"phpDocumentor\Descriptor\Collection":1:{s:8:" * items";a:0:{}}s:9:" * errors";O:35:"phpDocumentor\Descriptor\Collection":1:{s:8:" * items";a:0:{}}}s:12:"$targetWidth";O:43:"phpDocumentor\Descriptor\ArgumentDescriptor":13:{s:8:" * types";a:1:{i:0;s:3:"int";}s:10:" * default";N;s:14:" * byReference";b:0;s:8:" * fqsen";s:0:"";s:7:" * name";s:12:"$targetWidth";s:12:" * namespace";N;s:10:" * package";N;s:10:" * summary";s:0:"";s:14:" * description";s:0:"";s:7:" * path";s:0:"";s:7:" * line";i:0;s:7:" * tags";O:35:"phpDocumentor\Descriptor\Collection":1:{s:8:" * items";a:0:{}}s:9:" * errors";O:35:"phpDocumentor\Descriptor\Collection":1:{s:8:" * items";a:0:{}}}s:13:"$targetHeight";O:43:"phpDocumentor\Descriptor\ArgumentDescriptor":13:{s:8:" * types";a:1:{i:0;s:3:"int";}s:10:" * default";N;s:14:" * byReference";b:0;s:8:" * fqsen";s:0:"";s:7:" * name";s:13:"$targetHeight";s:12:" * namespace";N;s:10:" * package";N;s:10:" * summary";s:0:"";s:14:" * description";s:0:"";s:7:" * path";s:0:"";s:7:" * line";i:0;s:7:" * tags";O:35:"phpDocumentor\Descriptor\Collection":1:{s:8:" * items";a:0:{}}s:9:" * errors";O:35:"phpDocumentor\Descriptor\Collection":1:{s:8:" * items";a:0:{}}}}}s:8:" * fqsen";s:42:"\stojg\crop\CropCenter::getSpecialOffset()";s:7:" * name";s:16:"getSpecialOffset";s:12:" * namespace";N;s:10:" * package";s:0:"";s:10:" * summary";s:28:"get special offset for class";s:14:" * description";s:0:"";s:7:" * path";s:0:"";s:7:" * line";i:25;s:7:" * tags";O:35:"phpDocumentor\Descriptor\Collection":1:{s:8:" * items";a:3:{s:5:"param";O:35:"phpDocumentor\Descriptor\Collection":1:{s:8:" * items";a:3:{i:0;O:44:"phpDocumentor\Descriptor\Tag\ParamDescriptor":4:{s:15:" * variableName";s:9:"$original";s:8:" * types";a:1:{i:0;s:8:"\Imagick";}s:7:" * name";s:5:"param";s:14:" * description";s:0:"";}i:1;O:44:"phpDocumentor\Descriptor\Tag\ParamDescriptor":4:{s:15:" * variableName";s:12:"$targetWidth";s:8:" * types";a:1:{i:0;s:3:"int";}s:7:" * name";s:5:"param";s:14:" * description";s:0:"";}i:2;O:44:"phpDocumentor\Descriptor\Tag\ParamDescriptor":4:{s:15:" * variableName";s:13:"$targetHeight";s:8:" * types";a:1:{i:0;s:3:"int";}s:7:" * name";s:5:"param";s:14:" * description";s:0:"";}}}s:6:"return";O:35:"phpDocumentor\Descriptor\Collection":1:{s:8:" * items";a:1:{i:0;O:45:"phpDocumentor\Descriptor\Tag\ReturnDescriptor":3:{s:8:" * types";a:1:{i:0;s:5:"array";}s:7:" * name";s:6:"return";s:14:" * description";s:0:"";}}}s:8:"internal";N;}}s:9:" * errors";O:35:"phpDocumentor\Descriptor\Collection":1:{s:8:" * items";a:0:{}}}s:15:"getCenterOffset";O:41:"phpDocumentor\Descriptor\MethodDescriptor":16:{s:9:" * parent";r:14;s:11:" * abstract";b:0;s:8:" * final";b:0;s:9:" * static";b:0;s:13:" * visibility";s:9:"protected";s:12:" * arguments";O:35:"phpDocumentor\Descriptor\Collection":1:{s:8:" * items";a:3:{s:6:"$image";O:43:"phpDocumentor\Descriptor\ArgumentDescriptor":13:{s:8:" * types";a:1:{i:0;s:8:"\Imagick";}s:10:" * default";N;s:14:" * byReference";b:0;s:8:" * fqsen";s:0:"";s:7:" * name";s:6:"$image";s:12:" * namespace";N;s:10:" * package";N;s:10:" * summary";s:0:"";s:14:" * description";s:0:"";s:7:" * path";s:0:"";s:7:" * line";i:0;s:7:" * tags";O:35:"phpDocumentor\Descriptor\Collection":1:{s:8:" * items";a:0:{}}s:9:" * errors";O:35:"phpDocumentor\Descriptor\Collection":1:{s:8:" * items";a:0:{}}}s:12:"$targetWidth";O:43:"phpDocumentor\Descriptor\ArgumentDescriptor":13:{s:8:" * types";a:1:{i:0;s:3:"int";}s:10:" * default";N;s:14:" * byReference";b:0;s:8:" * fqsen";s:0:"";s:7:" * name";s:12:"$targetWidth";s:12:" * namespace";N;s:10:" * package";N;s:10:" * summary";s:0:"";s:14:" * description";s:0:"";s:7:" * path";s:0:"";s:7:" * line";i:0;s:7:" * tags";O:35:"phpDocumentor\Descriptor\Collection":1:{s:8:" * items";a:0:{}}s:9:" * errors";O:35:"phpDocumentor\Descriptor\Collection":1:{s:8:" * items";a:0:{}}}s:13:"$targetHeight";O:43:"phpDocumentor\Descriptor\ArgumentDescriptor":13:{s:8:" * types";a:1:{i:0;s:3:"int";}s:10:" * default";N;s:14:" * byReference";b:0;s:8:" * fqsen";s:0:"";s:7:" * name";s:13:"$targetHeight";s:12:" * namespace";N;s:10:" * package";N;s:10:" * summary";s:0:"";s:14:" * description";s:0:"";s:7:" * path";s:0:"";s:7:" * line";i:0;s:7:" * tags";O:35:"phpDocumentor\Descriptor\Collection":1:{s:8:" * items";a:0:{}}s:9:" * errors";O:35:"phpDocumentor\Descriptor\Collection":1:{s:8:" * items";a:0:{}}}}}s:8:" * fqsen";s:41:"\stojg\crop\CropCenter::getCenterOffset()";s:7:" * name";s:15:"getCenterOffset";s:12:" * namespace";N;s:10:" * package";s:0:"";s:10:" * summary";s:70:"Get the cropping offset for the image based on the center of the image";s:14:" * description";s:0:"";s:7:" * path";s:0:"";s:7:" * line";i:38;s:7:" * tags";O:35:"phpDocumentor\Descriptor\Collection":1:{s:8:" * items";a:3:{s:5:"param";O:35:"phpDocumentor\Descriptor\Collection":1:{s:8:" * items";a:3:{i:0;O:44:"phpDocumentor\Descriptor\Tag\ParamDescriptor":4:{s:15:" * variableName";s:6:"$image";s:8:" * types";a:1:{i:0;s:8:"\Imagick";}s:7:" * name";s:5:"param";s:14:" * description";s:0:"";}i:1;O:44:"phpDocumentor\Descriptor\Tag\ParamDescriptor":4:{s:15:" * variableName";s:12:"$targetWidth";s:8:" * types";a:1:{i:0;s:3:"int";}s:7:" * name";s:5:"param";s:14:" * description";s:0:"";}i:2;O:44:"phpDocumentor\Descriptor\Tag\ParamDescriptor":4:{s:15:" * variableName";s:13:"$targetHeight";s:8:" * types";a:1:{i:0;s:3:"int";}s:7:" * name";s:5:"param";s:14:" * description";s:0:"";}}}s:6:"return";O:35:"phpDocumentor\Descriptor\Collection":1:{s:8:" * items";a:1:{i:0;O:45:"phpDocumentor\Descriptor\Tag\ReturnDescriptor":3:{s:8:" * types";a:1:{i:0;s:5:"array";}s:7:" * name";s:6:"return";s:14:" * description";s:0:"";}}}s:8:"internal";N;}}s:9:" * errors";O:35:"phpDocumentor\Descriptor\Collection":1:{s:8:" * items";a:0:{}}}}}s:8:" * fqsen";s:22:"\stojg\crop\CropCenter";s:7:" * name";s:10:"CropCenter";s:12:" * namespace";s:11:"\stojg\crop";s:10:" * package";s:0:"";s:10:" * summary";s:10:"CropCenter";s:14:" * description";s:148:"The most basic of cropping techniques:
49 |
50 | 1. Find the exact center of the image
51 | 2. Trim any edges that is bigger than the targetWidth and targetHeight";s:7:" * path";r:1;s:7:" * line";i:14;s:7:" * tags";O:35:"phpDocumentor\Descriptor\Collection":1:{s:8:" * items";a:0:{}}s:9:" * errors";O:35:"phpDocumentor\Descriptor\Collection":1:{s:8:" * items";a:0:{}}}}}s:13:" * interfaces";O:35:"phpDocumentor\Descriptor\Collection":1:{s:8:" * items";a:0:{}}s:9:" * traits";O:35:"phpDocumentor\Descriptor\Collection":1:{s:8:" * items";a:0:{}}s:10:" * markers";O:35:"phpDocumentor\Descriptor\Collection":1:{s:8:" * items";a:0:{}}s:8:" * fqsen";s:0:"";s:7:" * name";s:14:"CropCenter.php";s:12:" * namespace";N;s:10:" * package";s:0:"";s:10:" * summary";s:0:"";s:14:" * description";s:0:"";s:7:" * path";s:29:"src/stojg/crop/CropCenter.php";s:7:" * line";i:0;s:7:" * tags";O:35:"phpDocumentor\Descriptor\Collection":1:{s:8:" * items";a:0:{}}s:9:" * errors";O:35:"phpDocumentor\Descriptor\Collection":1:{s:8:" * items";a:0:{}}}
--------------------------------------------------------------------------------
/src/stojg/crop/CropEntropy.php:
--------------------------------------------------------------------------------
1 | getEntropyOffsets($original, $targetWidth, $targetHeight);
33 | }
34 |
35 |
36 | /**
37 | * Get the topleftX and topleftY that will can be passed to a cropping method.
38 | *
39 | * @param \Imagick $original
40 | * @param int $targetWidth
41 | * @param int $targetHeight
42 | * @return array
43 | */
44 | protected function getEntropyOffsets(\Imagick $original, $targetWidth, $targetHeight)
45 | {
46 | $measureImage = clone($original);
47 | // Enhance edges
48 | $measureImage->edgeimage(1);
49 | // Turn image into a grayscale
50 | $measureImage->modulateImage(100, 0, 100);
51 | // Turn everything darker than this to pitch black
52 | $measureImage->blackThresholdImage("#070707");
53 | // Get the calculated offset for cropping
54 | return $this->getOffsetFromEntropy($measureImage, $targetWidth, $targetHeight);
55 | }
56 |
57 | /**
58 | * Get the offset of where the crop should start
59 | *
60 | * @param \Imagick $image
61 | * @param int $targetHeight
62 | * @param int $targetHeight
63 | * @param int $sliceSize
64 | * @return array
65 | */
66 | protected function getOffsetFromEntropy(\Imagick $originalImage, $targetWidth, $targetHeight)
67 | {
68 | // The entropy works better on a blured image
69 | $image = clone $originalImage;
70 | $image->blurImage(3, 2);
71 |
72 | $size = $image->getImageGeometry();
73 |
74 | $originalWidth = $size['width'];
75 | $originalHeight = $size['height'];
76 |
77 | $leftX = $this->slice($image, $originalWidth, $targetWidth, 'h');
78 | $topY = $this->slice($image, $originalHeight, $targetHeight, 'v');
79 |
80 | return array('x' => $leftX, 'y' => $topY);
81 | }
82 |
83 |
84 | /**
85 | * slice
86 | *
87 | * @param mixed $image
88 | * @param mixed $originalSize
89 | * @param mixed $targetSize
90 | * @param mixed $axis h=horizontal, v = vertical
91 | * @access protected
92 | * @return void
93 | */
94 | protected function slice($image, $originalSize, $targetSize, $axis)
95 | {
96 | $aSlice = null;
97 | $bSlice = null;
98 |
99 | // Just an arbitrary size of slice size
100 | $sliceSize = ceil(($originalSize - $targetSize) / 25);
101 |
102 | $aBottom = $originalSize;
103 | $aTop = 0;
104 |
105 | // while there still are uninvestigated slices of the image
106 | while ($aBottom - $aTop > $targetSize) {
107 | // Make sure that we don't try to slice outside the picture
108 | $sliceSize = min($aBottom - $aTop - $targetSize, $sliceSize);
109 |
110 | // Make a top slice image
111 | if (!$aSlice) {
112 | $aSlice = clone $image;
113 | if ($axis === 'h') {
114 | $aSlice->cropImage($originalSize, $sliceSize, $aTop, 0);
115 | } else {
116 | $aSlice->cropImage($originalSize, $sliceSize, 0, $aTop);
117 | }
118 | }
119 |
120 | // Make a bottom slice image
121 | if (!$bSlice) {
122 | $bSlice = clone $image;
123 | if ($axis === 'h') {
124 | $bSlice->cropImage($originalSize, $sliceSize, $aBottom - $sliceSize, 0);
125 | } else {
126 | $bSlice->cropImage($originalSize, $sliceSize, 0, $aBottom - $sliceSize);
127 | }
128 | }
129 |
130 | // calculate slices potential
131 | $aPosition = ($axis === 'h' ? 'left' : 'top');
132 | $bPosition = ($axis === 'h' ? 'right' : 'bottom');
133 |
134 | $aPot = $this->getPotential($aPosition, $aTop, $sliceSize);
135 | $bPot = $this->getPotential($bPosition, $aBottom, $sliceSize);
136 |
137 | $canCutA = ($aPot <= 0);
138 | $canCutB = ($bPot <= 0);
139 |
140 | // if no slices are "cutable", we force if a slice has a lot of potential
141 | if (!$canCutA && !$canCutB) {
142 | if ($aPot * self::POTENTIAL_RATIO < $bPot) {
143 | $canCutA = true;
144 | } elseif ($aPot > $bPot * self::POTENTIAL_RATIO) {
145 | $canCutB = true;
146 | }
147 | }
148 |
149 | // if we can only cut on one side
150 | if ($canCutA xor $canCutB) {
151 | if ($canCutA) {
152 | $aTop += $sliceSize;
153 | $aSlice = null;
154 | } else {
155 | $aBottom -= $sliceSize;
156 | $bSlice = null;
157 | }
158 | } elseif ($this->grayscaleEntropy($aSlice) < $this->grayscaleEntropy($bSlice)) {
159 | // bSlice has more entropy, so remove aSlice and bump aTop down
160 | $aTop += $sliceSize;
161 | $aSlice = null;
162 | } else {
163 | $aBottom -= $sliceSize;
164 | $bSlice = null;
165 | }
166 | }
167 |
168 | return $aTop;
169 | }
170 |
171 | /**
172 | * getSafeZoneList
173 | *
174 | * @access protected
175 | * @return array
176 | */
177 | protected function getSafeZoneList()
178 | {
179 | return array();
180 | }
181 |
182 | /**
183 | * getPotential
184 | *
185 | * @param mixed $position
186 | * @param mixed $top
187 | * @param mixed $sliceSize
188 | * @access protected
189 | * @return void
190 | */
191 | protected function getPotential($position, $top, $sliceSize)
192 | {
193 | $safeZoneList = $this->getSafeZoneList();
194 |
195 | $safeRatio = 0;
196 |
197 | if ($position == 'top' || $position == 'left') {
198 | $start = $top;
199 | $end = $top + $sliceSize;
200 | } else {
201 | $start = $top - $sliceSize;
202 | $end = $top;
203 | }
204 |
205 | for ($i = $start; $i < $end; $i++) {
206 | foreach ($safeZoneList as $safeZone) {
207 | if ($position == 'top' || $position == 'bottom') {
208 | if ($safeZone['top'] <= $i && $safeZone['bottom'] >= $i) {
209 | $safeRatio = max($safeRatio, ($safeZone['right'] - $safeZone['left']));
210 | }
211 | } else {
212 | if ($safeZone['left'] <= $i && $safeZone['right'] >= $i) {
213 | $safeRatio = max($safeRatio, ($safeZone['bottom'] - $safeZone['top']));
214 | }
215 | }
216 | }
217 | }
218 |
219 | return $safeRatio;
220 | }
221 |
222 | /**
223 | * Calculate the entropy for this image.
224 | *
225 | * A higher value of entropy means more noise / liveliness / color / business
226 | *
227 | * @param \Imagick $image
228 | * @return float
229 | *
230 | * @see http://brainacle.com/calculating-image-entropy-with-python-how-and-why.html
231 | * @see http://www.mathworks.com/help/toolbox/images/ref/entropy.html
232 | */
233 | protected function grayscaleEntropy(\Imagick $image)
234 | {
235 | // The histogram consists of a list of 0-254 and the number of pixels that has that value
236 | $histogram = $image->getImageHistogram();
237 |
238 | return $this->getEntropy($histogram, $this->area($image));
239 | }
240 |
241 | /**
242 | * Find out the entropy for a color image
243 | *
244 | * If the source image is in color we need to transform RGB into a grayscale image
245 | * so we can calculate the entropy more performant.
246 | *
247 | * @param \Imagick $image
248 | * @return float
249 | */
250 | protected function colorEntropy(\Imagick $image)
251 | {
252 | $histogram = $image->getImageHistogram();
253 | $newHistogram = array();
254 |
255 | // Translates a color histogram into a bw histogram
256 | $colors = count($histogram);
257 | for ($idx = 0; $idx < $colors; $idx++) {
258 | $colors = $histogram[$idx]->getColor();
259 | $grey = $this->rgb2bw($colors['r'], $colors['g'], $colors['b']);
260 | if (!isset($newHistogram[$grey])) {
261 | $newHistogram[$grey] = $histogram[$idx]->getColorCount();
262 | } else {
263 | $newHistogram[$grey] += $histogram[$idx]->getColorCount();
264 | }
265 | }
266 |
267 | return $this->getEntropy($newHistogram, $this->area($image));
268 | }
269 | }
270 |
--------------------------------------------------------------------------------
/docs/namespaces/stojg.crop.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | » \stojg\crop
7 |
8 |
9 |
10 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
90 |
91 |
92 |
93 |
94 | Javascript is disabled; several features are only available if Javascript is enabled.
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 | \ stojg
128 |
129 |
130 |
131 |
132 | \ crop
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
Classes, interfaces and traits
141 |
142 |
143 |
144 |
CropFace¶
145 |
CropFace
146 |
This class will try to find the most interesting point in the image by trying to find a face and
147 | center the crop on that
148 |
« More »
149 |
150 |
151 |
CropCenter¶
152 |
CropCenter
153 |
The most basic of cropping techniques:
154 |
155 | 1. Find the exact center of the image
156 | 2. Trim any edges that is bigger than the targetWidth and targetHeight
157 |
« More »
158 |
159 |
160 |
Crop¶
161 |
Base class for all Croppers
162 |
163 |
« More »
164 |
165 |
166 |
CropBalanced¶
167 |
CropBalanced
168 |
This class calculates the most interesting point in the image by:
169 |
170 | 1. Dividing the image into four equally squares
171 | 2. Find the most energetic point per square
172 | 3. Finding the images weighted mean interest point
173 |
« More »
174 |
175 |
176 |
CropEntropy¶
177 |
CropEntropy
178 |
This class finds the a position in the picture with the most energy in it.
179 |
180 | Energy is in this case calculated by this
181 |
182 | 1. Take the image and turn it into black and white
183 | 2. Run a edge filter so that we're left with only edges.
184 | 3. Find a piece in the picture that has the highest entropy (i.e. most edges)
185 | 4. Return coordinates that makes sure that this piece of the picture is not cropped 'away'
186 |
« More »
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
201 |
202 |
203 |
--------------------------------------------------------------------------------
/docs/packages/default.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | »
7 |
8 |
9 |
10 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
90 |
91 |
92 |
93 |
94 | Javascript is disabled; several features are only available if Javascript is enabled.
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 | \
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
Classes, interfaces and traits
135 |
136 |
137 |
138 |
CropFace¶
139 |
CropFace
140 |
This class will try to find the most interesting point in the image by trying to find a face and
141 | center the crop on that
142 |
« More »
143 |
144 |
145 |
146 |
CropCenter¶
147 |
CropCenter
148 |
The most basic of cropping techniques:
149 |
150 | 1. Find the exact center of the image
151 | 2. Trim any edges that is bigger than the targetWidth and targetHeight
152 |
« More »
153 |
154 |
155 |
156 |
Crop¶
157 |
Base class for all Croppers
158 |
159 |
« More »
160 |
161 |
162 |
163 |
CropBalanced¶
164 |
CropBalanced
165 |
This class calculates the most interesting point in the image by:
166 |
167 | 1. Dividing the image into four equally squares
168 | 2. Find the most energetic point per square
169 | 3. Finding the images weighted mean interest point
170 |
« More »
171 |
172 |
173 |
174 |
CropEntropy¶
175 |
CropEntropy
176 |
This class finds the a position in the picture with the most energy in it.
177 |
178 | Energy is in this case calculated by this
179 |
180 | 1. Take the image and turn it into black and white
181 | 2. Run a edge filter so that we're left with only edges.
182 | 3. Find a piece in the picture that has the highest entropy (i.e. most edges)
183 | 4. Return coordinates that makes sure that this piece of the picture is not cropped 'away'
184 |
« More »
185 |
186 |
187 |
188 |
ClassEntropyTest¶
189 |
190 |
191 |
« More »
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
207 |
208 |
209 |
--------------------------------------------------------------------------------
/docs/namespaces/stojg.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | » \stojg
7 |
8 |
9 |
10 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
90 |
91 |
92 |
93 |
94 | Javascript is disabled; several features are only available if Javascript is enabled.
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 | \ stojg
139 |
140 |
141 |
142 |
143 | \ crop
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
Classes, interfaces and traits
152 |
153 |
154 |
155 |
CropFace¶
156 |
CropFace
157 |
This class will try to find the most interesting point in the image by trying to find a face and
158 | center the crop on that
159 |
« More »
160 |
161 |
162 |
CropCenter¶
163 |
CropCenter
164 |
The most basic of cropping techniques:
165 |
166 | 1. Find the exact center of the image
167 | 2. Trim any edges that is bigger than the targetWidth and targetHeight
168 |
« More »
169 |
170 |
171 |
Crop¶
172 |
Base class for all Croppers
173 |
174 |
« More »
175 |
176 |
177 |
CropBalanced¶
178 |
CropBalanced
179 |
This class calculates the most interesting point in the image by:
180 |
181 | 1. Dividing the image into four equally squares
182 | 2. Find the most energetic point per square
183 | 3. Finding the images weighted mean interest point
184 |
« More »
185 |
186 |
187 |
CropEntropy¶
188 |
CropEntropy
189 |
This class finds the a position in the picture with the most energy in it.
190 |
191 | Energy is in this case calculated by this
192 |
193 | 1. Take the image and turn it into black and white
194 | 2. Run a edge filter so that we're left with only edges.
195 | 3. Find a piece in the picture that has the highest entropy (i.e. most edges)
196 | 4. Return coordinates that makes sure that this piece of the picture is not cropped 'away'
197 |
« More »
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
213 |
214 |
215 |
--------------------------------------------------------------------------------
/docs/namespaces/default.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | » \
7 |
8 |
9 |
10 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
90 |
91 |
92 |
93 |
94 | Javascript is disabled; several features are only available if Javascript is enabled.
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 | \ \
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
Classes, interfaces and traits
153 |
154 |
155 |
156 |
ClassEntropyTest¶
157 |
158 |
159 |
« More »
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 | \ stojg
174 |
175 |
176 |
177 |
178 | \ crop
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
Classes, interfaces and traits
187 |
188 |
189 |
190 |
CropFace¶
191 |
CropFace
192 |
This class will try to find the most interesting point in the image by trying to find a face and
193 | center the crop on that
194 |
« More »
195 |
196 |
197 |
CropCenter¶
198 |
CropCenter
199 |
The most basic of cropping techniques:
200 |
201 | 1. Find the exact center of the image
202 | 2. Trim any edges that is bigger than the targetWidth and targetHeight
203 |
« More »
204 |
205 |
206 |
Crop¶
207 |
Base class for all Croppers
208 |
209 |
« More »
210 |
211 |
212 |
CropBalanced¶
213 |
CropBalanced
214 |
This class calculates the most interesting point in the image by:
215 |
216 | 1. Dividing the image into four equally squares
217 | 2. Find the most energetic point per square
218 | 3. Finding the images weighted mean interest point
219 |
« More »
220 |
221 |
222 |
CropEntropy¶
223 |
CropEntropy
224 |
This class finds the a position in the picture with the most energy in it.
225 |
226 | Energy is in this case calculated by this
227 |
228 | 1. Take the image and turn it into black and white
229 | 2. Run a edge filter so that we're left with only edges.
230 | 3. Find a piece in the picture that has the highest entropy (i.e. most edges)
231 | 4. Return coordinates that makes sure that this piece of the picture is not cropped 'away'
232 |
« More »
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
249 |
250 |
251 |
--------------------------------------------------------------------------------
/docs/css/template.css:
--------------------------------------------------------------------------------
1 | @import url(bootstrap.min.css);
2 | @import url(bootstrap-responsive.css);
3 | @import url(prettify.css);
4 | @import url(jquery.iviewer.css);
5 | @import url(http://fonts.googleapis.com/css?family=Crimson+Text|Philosopher|Forum);
6 |
7 | body
8 | {
9 | padding-top: 60px; /* 60px to make the container go all the way to the bottom of the topbar */
10 | background: #f9f9f9;
11 | color: #444;
12 | }
13 |
14 | a
15 | {
16 | color: #55A72F;
17 | }
18 |
19 | td p:last-of-type {
20 | margin: 0;
21 | }
22 |
23 | li.l0, li.l1, li.l2, li.l3, li.l5, li.l6, li.l7, li.l8
24 | {
25 | list-style-type: decimal;
26 | }
27 |
28 | a.brand, h2, .hero-unit h1
29 | {
30 | font-family: 'Forum', "Helvetica Neue", Helvetica, Arial, sans-serif;
31 | }
32 |
33 | .element .span4
34 | {
35 | width: 275px;
36 | }
37 |
38 | .namespace-contents hr, .package-contents hr
39 | {
40 | border-top: 3px dotted silver;
41 | }
42 |
43 | .namespace-indent, .package-indent
44 | {
45 | padding-left: 10px; border-left: 1px dashed #f0f0f0;
46 | }
47 |
48 | .element h3 i, .namespace-contents h3 i, .package-contents h3 i
49 | {
50 | margin-top: 2px;
51 | margin-right: 5px;
52 | }
53 |
54 | .element h3, .namespace-contents h3, .package-contents h3
55 | {
56 | margin-top: 25px;
57 | margin-bottom: 20px;
58 | border-bottom: 1px solid silver;
59 | }
60 |
61 | .element h3:first-of-type, .namespace-contents h3:first-of-type,
62 | .package-contents h3:first-of-type
63 | {
64 | margin-top: 30px;
65 | }
66 |
67 | .element h2
68 | {
69 | font-family: inherit;
70 | font-size: 1.2em;
71 | color: black;
72 | }
73 |
74 | .element .type
75 | {
76 | font-weight: bold;
77 | }
78 |
79 | #search-query
80 | {
81 | height: auto;
82 | }
83 |
84 | .hero-unit, div.element, .well
85 | {
86 | border: 1px solid #e0e0e0;
87 | background: white;
88 | }
89 |
90 | .dropdown-menu a{
91 | overflow: hidden;
92 | text-overflow: ellipsis;
93 | }
94 | h2
95 | {
96 | border-bottom: 1px dashed #55A72F;
97 | margin-bottom: 10px;
98 | padding-bottom: 0;
99 | padding-left: 5px;
100 | color: #e9e9e9;
101 | font-weight: normal;
102 | margin-top: 40px;
103 | }
104 |
105 | h2:first-of-type
106 | {
107 | margin-top: 0;
108 | }
109 |
110 | .hero-unit
111 | {
112 | background: #75a70d; /* Old browsers */
113 | background: -moz-radial-gradient(center, ellipse cover, #bfd255 0%, #8eb92a 72%, #72aa00 96%, #9ecb2d 100%); /* FF3.6+ */
114 | background: -webkit-gradient(radial, center center, 0px, center center, 100%, color-stop(0%,#bfd255), color-stop(72%,#8eb92a), color-stop(96%,#72aa00), color-stop(100%,#9ecb2d)); /* Chrome,Safari4+ */
115 | background: -webkit-radial-gradient(center, ellipse cover, #bfd255 0%,#8eb92a 72%,#72aa00 96%,#9ecb2d 100%); /* Chrome10+,Safari5.1+ */
116 | background: -o-radial-gradient(center, ellipse cover, #bfd255 0%,#8eb92a 72%,#72aa00 96%,#9ecb2d 100%); /* Opera 12+ */
117 | background: -ms-radial-gradient(center, ellipse cover, #bfd255 0%,#8eb92a 72%,#72aa00 96%,#9ecb2d 100%); /* IE10+ */
118 | background: radial-gradient(center, ellipse cover, #bfd255 0%,#8eb92a 72%,#72aa00 96%,#9ecb2d 100%); /* W3C */
119 | filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#bfd255', endColorstr='#9ecb2d',GradientType=1 ); /* IE6-9 fallback on horizontal gradient */
120 |
121 | padding: 40px 0 15px 0;
122 | box-shadow: inset 0 0 10px gray;
123 | }
124 |
125 | .hero-unit h1
126 | {
127 | text-align: center;
128 | font-weight: normal;
129 | text-align: center;
130 | color: white;
131 | text-shadow: black 0px 0px 15px;
132 | }
133 |
134 | .hero-unit h2
135 | {
136 | border: none;
137 | color: white;
138 | background: rgba(48, 48, 48, 0.5);
139 | padding: 0;
140 | margin: 0;
141 | margin-top: 15px;
142 | text-align: center;
143 | }
144 |
145 | .namespace-contents h2, .package-contents h2
146 | {
147 | padding-left: 44px;
148 | background: transparent url('../img/icons/icon-th-big.png') no-repeat 3px center;
149 | }
150 |
151 | .package-contents h2
152 | {
153 | background-image: url('../img/icons/icon-folder-open-big.png');
154 | }
155 |
156 | .namespace-contents .element h2, .package-contents .element h2
157 | {
158 | padding-left: 0;
159 | background: none;
160 | }
161 |
162 | div.element
163 | {
164 | border-left: 10px solid #55A72F;
165 | border-radius: 5px;
166 | padding: 7px 7px 2px 7px;
167 | margin-bottom: 15px;
168 | margin-left: 0;
169 | }
170 |
171 | div.element.protected
172 | {
173 | border-left-color: orange;
174 | }
175 |
176 | div.element.private
177 | {
178 | border-left-color: red;
179 | }
180 |
181 | div.element.class, div.element.interface
182 | {
183 | border-left-color: #e0e0e0;
184 | }
185 |
186 | div.element.class.abstract h1, div.element.interface.abstract h1
187 | {
188 | font-style: italic;
189 | }
190 |
191 | div.element h1
192 | {
193 | font-size: 1.2em;
194 | line-height: 1.5em;
195 | margin-bottom: 10px;
196 | padding-left: 22px;
197 | background: transparent no-repeat left 2px;
198 | word-wrap: break-word;
199 | }
200 |
201 | div.element h1 a
202 | {
203 | color: transparent;
204 | margin-left: 10px;
205 | }
206 |
207 | div.element h1:hover a
208 | {
209 | color: silver;
210 | }
211 |
212 | div.element h1 a:hover
213 | {
214 | color: navy;
215 | }
216 |
217 | div.element a.more:hover
218 | {
219 | background: #f0f0f0;
220 | color: #444;
221 | text-decoration: none;
222 | }
223 |
224 | div.element a.more
225 | {
226 | font-weight: bold;
227 | text-align: center;
228 | color: gray;
229 | border-top: 1px dashed silver;
230 | display: block;
231 | margin-top: 5px;
232 | padding: 5px 0;
233 | border-bottom-left-radius: 5px;
234 | border-bottom-right-radius: 5px;
235 | }
236 |
237 | div.element p
238 | {
239 | font-size: 0.9em;
240 | }
241 |
242 | div.element .table
243 | {
244 | font-size: 0.9em;
245 | }
246 |
247 | div.element .table th
248 | {
249 | text-transform: capitalize;
250 | }
251 |
252 | div.detail-description
253 | {
254 | padding-left: 30px;
255 | }
256 |
257 | div.detail-description table th {
258 | vertical-align: top;
259 | }
260 |
261 | body.invert
262 | {
263 | background: white;
264 | }
265 |
266 | body.invert div.element
267 | {
268 | background: #f9f9f9;
269 | }
270 |
271 | ul.side-nav
272 | {
273 | clear: both;
274 | }
275 |
276 | ul.side-nav li
277 | {
278 | word-wrap: break-word;
279 | padding-left: 10px;
280 | text-indent: -10px;
281 | }
282 |
283 | ul.side-nav li a
284 | {
285 | background: transparent no-repeat 5px 3px;
286 | padding-bottom: 10px;
287 | font-style: italic;
288 | }
289 |
290 | ul.side-nav li pre
291 | {
292 | font-size: 0.8em;
293 | margin: 5px 15px 0 15px;
294 | padding: 2px 5px;
295 | background-color: #f8f8f8;
296 | color: gray;
297 | font-style: normal;
298 | word-wrap: break-word;
299 | text-indent: 0;
300 | }
301 |
302 | ul.side-nav li.view-simple span.description
303 | {
304 | display: none;
305 | }
306 |
307 | ul.side-nav li.view-simple pre
308 | {
309 | font-size: inherit;
310 | margin: inherit;
311 | padding: inherit;
312 | background-color: inherit;
313 | border: none;
314 | color: inherit;
315 | font-family: inherit;
316 | font-style: inherit;
317 | padding-bottom: 0;
318 | padding-left: 5px;
319 | }
320 |
321 | ul.side-nav li.view-simple a
322 | {
323 | padding-bottom: 0px;
324 | }
325 |
326 | i.icon-custom
327 | {
328 | width: 16px;
329 | height: 16px;
330 | background-position: 0;
331 | }
332 |
333 | .table.markers
334 | {
335 | background: white;
336 | }
337 |
338 | /* JS only functionality; disable by default */
339 | .btn-group.visibility, .btn-group.view, .btn-group.type-filter
340 | {
341 | display: none;
342 | }
343 |
344 | .btn-group.view
345 | {
346 | margin-left: 20px;
347 | margin-bottom: 20px;
348 | }
349 |
350 | .visibility button
351 | {
352 | height: 24px;
353 | }
354 |
355 | div.element.constant h1,
356 | i.icon-constant { background-image: url('../img/icons/constant.png'); }
357 |
358 | div.element.function h1,
359 | i.icon-function { background-image: url('../img/icons/function.png'); }
360 |
361 | div.element.method h1,
362 | i.icon-method { background-image: url('../img/icons/method.png'); }
363 |
364 | div.element.class h1,
365 | i.icon-class { background-image: url('../img/icons/class.png'); }
366 |
367 | div.element.interface h1,
368 | i.icon-interface { background-image: url('../img/icons/interface.png'); }
369 |
370 | div.element.property h1,
371 | i.icon-property { background-image: url('../img/icons/property.png'); }
372 |
373 | i.icon-show-public { background-image: url('../img/icons/visibility_public.png'); }
374 | i.icon-show-protected { background-image: url('../img/icons/visibility_protected.png'); }
375 | i.icon-show-private { background-image: url('../img/icons/visibility_private.png'); }
376 |
377 | span.empty-namespace
378 | {
379 | color: silver;
380 | }
381 |
382 | footer
383 | {
384 | text-align: right;
385 | font-size: 0.8em;
386 | opacity: 0.5;
387 | }
388 |
389 | #mapHolder
390 | {
391 | border: 4px solid #555;
392 | padding: 0 !important;
393 | overflow: hidden
394 | }
395 |
396 | div.element div.subelement
397 | {
398 | margin-left: 10px;
399 | padding-bottom: 5px;
400 | clear: both;
401 | }
402 |
403 | pre code
404 | {
405 | border: none;
406 | }
407 |
408 | div.element div.subelement > code
409 | {
410 | font-size: 0.8em;
411 | float: left;
412 | margin-right: 10px;
413 | padding: 0 5px;
414 | line-height: 16px;
415 | }
416 |
417 | div.element div.subelement > p
418 | {
419 | margin-left: 20px;
420 | margin-right: 50px;
421 | }
422 |
423 | div.element div.subelement h4
424 | {
425 | color: #666;
426 | margin-bottom: 5px;
427 | }
428 |
429 | div.element div.subelement.response
430 | {
431 | padding-bottom: 15px;
432 | margin-right: 50px;
433 | }
434 |
435 | div.labels
436 | {
437 | text-align: right;
438 | }
439 |
440 | .nav-list .nav-header
441 | {
442 | font-size: 13px;
443 | }
444 |
445 | .detail-description code {
446 | white-space: pre;
447 | display: inline-block;
448 | padding: 10px;
449 | }
450 |
451 | .go_to_top
452 | {
453 | float: right;
454 | margin-right: 20px;
455 | background: #2C2C2C;
456 | color: #999;
457 | padding: 3px 10px;
458 | border-bottom-right-radius: 5px;
459 | border-bottom-left-radius: 5px;
460 | text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
461 | line-height: 19px;
462 | }
463 |
464 | .visibility .btn {
465 | text-transform: uppercase;
466 | font-size: 0.7em;
467 | font-weight: bold;
468 | }
469 |
470 | .iviewer_common
471 | {
472 | z-index: 100;
473 | }
474 |
475 | @media (min-width: 980px)
476 | {
477 | a[name]
478 | {
479 | margin-top: -50px;
480 | position: absolute;
481 | }
482 | }
483 |
484 | @media (min-width: 1200px)
485 | {
486 | .method .span4
487 | {
488 | width: 345px;
489 | }
490 | }
491 |
492 | /* redefined because twitter bootstrap assumes that bootstrap-responsive.css */
493 | @media (max-width: 980px)
494 | {
495 | body
496 | {
497 | padding-top: 0;
498 | }
499 |
500 | .go_to_top
501 | {
502 | display: none;
503 | }
504 |
505 | .btn-group.visibility
506 | {
507 | font-size: 0.80em;
508 | margin-bottom: 7px;
509 | display: block;
510 | float: right;
511 | }
512 | }
513 |
514 | @media (max-width: 768px)
515 | {
516 | .hero-unit h1 {
517 | font-size: 30px;
518 | }
519 | .hero-unit h2 {
520 | font-size: 19px;
521 | }
522 |
523 | }
524 | @media (min-width: 768px) and (max-width: 980px)
525 | {
526 | .method .span4
527 | {
528 | width: 203px;
529 | }
530 | }
531 |
--------------------------------------------------------------------------------
/docs/js/jquery.splitter.js:
--------------------------------------------------------------------------------
1 | /*
2 | * jQuery.splitter.js - two-pane splitter window plugin
3 | *
4 | * version 1.51 (2009/01/09)
5 | *
6 | * Dual licensed under the MIT and GPL licenses:
7 | * http://www.opensource.org/licenses/mit-license.php
8 | * http://www.gnu.org/licenses/gpl.html
9 | */
10 |
11 | /**
12 | * The splitter() plugin implements a two-pane resizable splitter window.
13 | * The selected elements in the jQuery object are converted to a splitter;
14 | * each selected element should have two child elements, used for the panes
15 | * of the splitter. The plugin adds a third child element for the splitbar.
16 | *
17 | * For more details see: http://methvin.com/splitter/
18 | *
19 | *
20 | * @example $('#MySplitter').splitter();
21 | * @desc Create a vertical splitter with default settings
22 | *
23 | * @example $('#MySplitter').splitter({type: 'h', accessKey: 'M'});
24 | * @desc Create a horizontal splitter resizable via Alt+Shift+M
25 | *
26 | * @name splitter
27 | * @type jQuery
28 | * @param Object options Options for the splitter (not required)
29 | * @cat Plugins/Splitter
30 | * @return jQuery
31 | * @author Dave Methvin (dave.methvin@gmail.com)
32 | */
33 | ;
34 | (function($) {
35 |
36 | $.fn.splitter = function(args) {
37 | args = args || {};
38 | return this.each(function() {
39 | var zombie; // left-behind splitbar for outline resizes
40 | function startSplitMouse(evt) {
41 | if (opts.outline)
42 | zombie = zombie || bar.clone(false).insertAfter(A);
43 | panes.css("-webkit-user-select", "none"); // Safari selects A/B text on a move
44 | bar.addClass(opts.activeClass);
45 | $('
').insertAfter(bar);
46 | A._posSplit = A[0][opts.pxSplit] - evt[opts.eventPos];
47 | $(document)
48 | .bind("mousemove", doSplitMouse)
49 | .bind("mouseup", endSplitMouse);
50 | }
51 |
52 | function doSplitMouse(evt) {
53 | var newPos = A._posSplit + evt[opts.eventPos];
54 | if (opts.outline) {
55 | newPos = Math.max(0, Math.min(newPos, splitter._DA - bar._DA));
56 | bar.css(opts.origin, newPos);
57 | } else
58 | resplit(newPos);
59 | }
60 |
61 | function endSplitMouse(evt) {
62 | $('div.splitterMask').remove();
63 | bar.removeClass(opts.activeClass);
64 | var newPos = A._posSplit + evt[opts.eventPos];
65 | if (opts.outline) {
66 | zombie.remove();
67 | zombie = null;
68 | resplit(newPos);
69 | }
70 | panes.css("-webkit-user-select", "text"); // let Safari select text again
71 | $(document)
72 | .unbind("mousemove", doSplitMouse)
73 | .unbind("mouseup", endSplitMouse);
74 | }
75 |
76 | function resplit(newPos) {
77 | // Constrain new splitbar position to fit pane size limits
78 | newPos = Math.max(A._min, splitter._DA - B._max,
79 | Math.min(newPos, A._max, splitter._DA - bar._DA - B._min));
80 | // Resize/position the two panes
81 | bar._DA = bar[0][opts.pxSplit]; // bar size may change during dock
82 | bar.css(opts.origin, newPos).css(opts.fixed, splitter._DF);
83 | A.css(opts.origin, 0).css(opts.split, newPos).css(opts.fixed, splitter._DF);
84 | B.css(opts.origin, newPos + bar._DA)
85 | .css(opts.split, splitter._DA - bar._DA - newPos).css(opts.fixed, splitter._DF);
86 | // IE fires resize for us; all others pay cash
87 | if (!$.browser.msie)
88 | panes.trigger("resize");
89 | }
90 |
91 | function dimSum(jq, dims) {
92 | // Opera returns -1 for missing min/max width, turn into 0
93 | var sum = 0;
94 | for (var i = 1; i < arguments.length; i++)
95 | sum += Math.max(parseInt(jq.css(arguments[i])) || 0, 0);
96 | return sum;
97 | }
98 |
99 | // Determine settings based on incoming opts, element classes, and defaults
100 | var vh = (args.splitHorizontal ? 'h' : args.splitVertical ? 'v' : args.type) || 'v';
101 | var opts = $.extend({
102 | activeClass: 'active', // class name for active splitter
103 | pxPerKey: 8, // splitter px moved per keypress
104 | tabIndex: 0, // tab order indicator
105 | accessKey: '' // accessKey for splitbar
106 | }, {
107 | v: { // Vertical splitters:
108 | keyLeft: 39, keyRight: 37, cursor: "e-resize",
109 | splitbarClass: "vsplitbar", outlineClass: "voutline",
110 | type: 'v', eventPos: "pageX", origin: "left",
111 | split: "width", pxSplit: "offsetWidth", side1: "Left", side2: "Right",
112 | fixed: "height", pxFixed: "offsetHeight", side3: "Top", side4: "Bottom"
113 | },
114 | h: { // Horizontal splitters:
115 | keyTop: 40, keyBottom: 38, cursor: "n-resize",
116 | splitbarClass: "hsplitbar", outlineClass: "houtline",
117 | type: 'h', eventPos: "pageY", origin: "top",
118 | split: "height", pxSplit: "offsetHeight", side1: "Top", side2: "Bottom",
119 | fixed: "width", pxFixed: "offsetWidth", side3: "Left", side4: "Right"
120 | }
121 | }[vh], args);
122 |
123 | // Create jQuery object closures for splitter and both panes
124 | var splitter = $(this).css({position: "relative"});
125 | var panes = $(">*", splitter[0]).css({
126 | position: "absolute", // positioned inside splitter container
127 | "z-index": "1", // splitbar is positioned above
128 | "-moz-outline-style": "none" // don't show dotted outline
129 | });
130 | var A = $(panes[0]); // left or top
131 | var B = $(panes[1]); // right or bottom
132 |
133 | // Focuser element, provides keyboard support; title is shown by Opera accessKeys
134 | var focuser = $(' ')
135 | .attr({accessKey: opts.accessKey, tabIndex: opts.tabIndex, title: opts.splitbarClass})
136 | .bind($.browser.opera ? "click" : "focus", function() {
137 | this.focus();
138 | bar.addClass(opts.activeClass)
139 | })
140 | .bind("keydown", function(e) {
141 | var key = e.which || e.keyCode;
142 | var dir = key == opts["key" + opts.side1] ? 1 : key == opts["key" + opts.side2] ? -1 : 0;
143 | if (dir)
144 | resplit(A[0][opts.pxSplit] + dir * opts.pxPerKey, false);
145 | })
146 | .bind("blur", function() {
147 | bar.removeClass(opts.activeClass)
148 | });
149 |
150 | // Splitbar element, can be already in the doc or we create one
151 | var bar = $(panes[2] || '
')
152 | .insertAfter(A).css("z-index", "100").append(focuser)
153 | .attr({"class": opts.splitbarClass, unselectable: "on"})
154 | .css({position: "absolute", "user-select": "none", "-webkit-user-select": "none",
155 | "-khtml-user-select": "none", "-moz-user-select": "none", "top": "0px"})
156 | .bind("mousedown", startSplitMouse);
157 | // Use our cursor unless the style specifies a non-default cursor
158 | if (/^(auto|default|)$/.test(bar.css("cursor")))
159 | bar.css("cursor", opts.cursor);
160 |
161 | // Cache several dimensions for speed, rather than re-querying constantly
162 | bar._DA = bar[0][opts.pxSplit];
163 | splitter._PBF = $.boxModel ? dimSum(splitter, "border" + opts.side3 + "Width", "border" + opts.side4 + "Width") : 0;
164 | splitter._PBA = $.boxModel ? dimSum(splitter, "border" + opts.side1 + "Width", "border" + opts.side2 + "Width") : 0;
165 | A._pane = opts.side1;
166 | B._pane = opts.side2;
167 | $.each([A,B], function() {
168 | this._min = opts["min" + this._pane] || dimSum(this, "min-" + opts.split);
169 | this._max = opts["max" + this._pane] || dimSum(this, "max-" + opts.split) || 9999;
170 | this._init = opts["size" + this._pane] === true ?
171 | parseInt($.curCSS(this[0], opts.split)) : opts["size" + this._pane];
172 | });
173 |
174 | // Determine initial position, get from cookie if specified
175 | var initPos = A._init;
176 | if (!isNaN(B._init)) // recalc initial B size as an offset from the top or left side
177 | initPos = splitter[0][opts.pxSplit] - splitter._PBA - B._init - bar._DA;
178 | if (opts.cookie) {
179 | if (!$.cookie)
180 | alert('jQuery.splitter(): jQuery cookie plugin required');
181 | var ckpos = parseInt($.cookie(opts.cookie));
182 | if (!isNaN(ckpos))
183 | initPos = ckpos;
184 | $(window).bind("unload", function() {
185 | var state = String(bar.css(opts.origin)); // current location of splitbar
186 | $.cookie(opts.cookie, state, {expires: opts.cookieExpires || 365,
187 | path: opts.cookiePath || document.location.pathname});
188 | });
189 | }
190 | if (isNaN(initPos)) // King Solomon's algorithm
191 | initPos = Math.round((splitter[0][opts.pxSplit] - splitter._PBA - bar._DA) / 2);
192 |
193 | // Resize event propagation and splitter sizing
194 | if (opts.anchorToWindow) {
195 | // Account for margin or border on the splitter container and enforce min height
196 | splitter._hadjust = dimSum(splitter, "borderTopWidth", "borderBottomWidth", "marginBottom");
197 | splitter._hmin = Math.max(dimSum(splitter, "minHeight"), 20);
198 | $(window).bind("resize",
199 | function() {
200 | var top = splitter.offset().top;
201 | var wh = $(window).height();
202 | splitter.css("height", Math.max(wh - top - splitter._hadjust, splitter._hmin) + "px");
203 | if (!$.browser.msie) splitter.trigger("resize");
204 | }).trigger("resize");
205 | }
206 | else if (opts.resizeToWidth && !$.browser.msie)
207 | $(window).bind("resize", function() {
208 | splitter.trigger("resize");
209 | });
210 |
211 | // Resize event handler; triggered immediately to set initial position
212 | splitter.bind("resize",
213 | function(e, size) {
214 | // Custom events bubble in jQuery 1.3; don't Yo Dawg
215 | if (e.target != this) return;
216 | // Determine new width/height of splitter container
217 | splitter._DF = splitter[0][opts.pxFixed] - splitter._PBF;
218 | splitter._DA = splitter[0][opts.pxSplit] - splitter._PBA;
219 | // Bail if splitter isn't visible or content isn't there yet
220 | if (splitter._DF <= 0 || splitter._DA <= 0) return;
221 | // Re-divvy the adjustable dimension; maintain size of the preferred pane
222 | resplit(!isNaN(size) ? size : (!(opts.sizeRight || opts.sizeBottom) ? A[0][opts.pxSplit] :
223 | splitter._DA - B[0][opts.pxSplit] - bar._DA));
224 | }).trigger("resize", [initPos]);
225 | });
226 | };
227 |
228 | })(jQuery);
--------------------------------------------------------------------------------