Plugin Directory

Changeset 1351884


Ignore:
Timestamp:
02/16/2016 02:38:33 PM (10 years ago)
Author:
karim79
Message:

Major rewrite: version 2.5.0

Location:
kraken-image-optimizer
Files:
10 edited

Legend:

Unmodified
Added
Removed
  • kraken-image-optimizer/trunk/css/admin.css

    r1144921 r1351884  
    3535    clear: left;
    3636}
     37
    3738.kraken_req, .kraken_req_bulk {
    38     border: 1px solid #efab00;
    39     color: #222;
     39    border: none;
     40    color: #fff;
    4041    padding: 3px 8px 4px;
    4142    font-size: 11px;
    42     text-shadow: 0 1px 0 rgba(255,255,255,0.5);
    43     background: #ffc942;
    44     background-image: -webkit-gradient(linear,left top,left bottom,from(#ffc942),to(#efab00));
    45     background-image: -webkit-linear-gradient(top,#ffc942,#efab00);
    46     background-image: -moz-linear-gradient(top,#ffc942,#efab00);
    47     background-image: -o-linear-gradient(top,#ffc942,#efab00);
    48     background-image: linear-gradient(to bottom,#ffc942,#efab00);
     43    background: #3498DB;
    4944    -webkit-border-radius: 2px;
    5045    -moz-border-radius: 2px;
    5146    border-radius: 2px;
    5247    cursor: pointer;
    53     font-weight: bold;
    54 }
     48    transition: all 0.15s;
     49}
     50
     51.kraken_req:hover,
     52.kraken_req_bulk:hover {
     53    background: #2881BD;
     54}
     55
     56.kraken_req {
     57    margin-right: 2px;
     58}
     59
    5560.kraken_req_bulk {
    5661    font-size: 16px;
     62    margin: 0 auto;
     63    width: 150px;
     64    padding: 10px 0;
     65    display: block;
     66}
     67
     68td.kraken-bulk-filename {
     69    width: 360px;
     70    max-width: 360px;
     71    overflow: hidden;
     72    text-overflow: ellipsis;
     73    white-space: nowrap;
     74}
     75td.kraken-krakedsize {
     76    width: 170px;
    5777}
    5878.buttonWrap {
     
    116136    margin-bottom: 10px;
    117137    font-weight: bold;
    118 }
     138    padding: 15px 20px;
     139    border-bottom: 1px solid #ddd;
     140    margin: 0;
     141    background: #fafafa;
     142    position: relative;
     143}
     144
     145.krakenBulkHeader span {
     146    position: absolute;
     147    top: 5px;
     148    right: 15px;
     149    font-size: 30px;
     150    color: #777;
     151}
     152
     153.krakenBulkHeader span:hover {
     154    color: #222;
     155}
     156
    119157#kraken-bulk-modal {
    120158    position: absolute !important;
    121     top: 100px; 
     159    top: 100px;
    122160    width: 785px;
     161    padding: 0 0 20px 0;
    123162}
    124163#kraken-bulk-modal label {
    125164    margin-bottom: 11px;
    126165}
     166
    127167#kraken-bulk {
    128     width: 780px;   
    129     margin: 0 auto;
    130     margin-bottom: 10px;   
    131     border-spacing: 1px;
    132     border-collapse: separate;
    133 }
     168    width: 770px;
     169    margin: 0 auto 20px;
     170    border-collapse: collapse;
     171}
     172
     173#kraken-bulk td {
     174    border: 1px solid #ddd;
     175    padding: 5px 10px;
     176}
     177
     178#kraken-bulk tr:nth-child(odd) {
     179  background-color: #eee;
     180}
     181
     182#kraken-bulk-modal .the-following {
     183    margin: 0;
     184    text-align: center;
     185    padding: 15px 0;
     186}
     187
    134188.kraken-bulk-header td {
    135189    font-weight: bold;
    136     font-size: 14px;
    137     padding: 4px 0 4px 2px;
    138 }
     190    font-size: 13px;
     191    padding: 7px 10px !important;
     192    background: #ddd;
     193    color: #212121;
     194    border: 1px solid #ccc !important;
     195}
     196
    139197td.kraken-krakedsize {
    140198    text-align: left;
     
    157215    cursor: pointer;
    158216}
     217
    159218.radiosWrap {
    160     display: inline-block;
    161     margin: 0 0 5px 4px;
    162 }
     219    width: 100%;
     220    text-align: center;
     221    padding: 15px 0;
     222    border-bottom: 1px solid #ddd;
     223}
     224
     225.radiosWrap p {
     226    padding: 0;
     227    margin: 0 0 5px 0;
     228    font-weight: bold;
     229}
     230
     231.radiosWrap input[type="radio"] {
     232    margin-top: -1px;
     233    vertical-align: middle;
     234}
     235
    163236.kraken-bulk-small {
    164237    font-size: 12px;
     
    178251    height: 10px;
    179252    background: url('https://kraken-nekkraug.netdna-ssl.com/assets/images/spinner.gif') no-repeat 0 0;
    180     background-size: 10px; 
     253    background-size: 10px;
    181254}
    182255h1.kraken-admin-section-title {
     
    197270}
    198271td.krakenAdvancedSettings {
    199     padding: 0;
     272    padding: 20px 0 0 0 !important;
    200273}
    201274td.krakenAdvancedSettingsDescription {
    202275    padding: 0;
    203     padding-bottom: 10px;
     276    padding: 10px 0 !important;
    204277}
    205278td.krakenAdvancedSettings h3 {
     
    231304}
    232305.kraken-advanced-settings {
    233     display: none;
     306    /*display: none;*/
    234307}
    235308#krakenSettings .form-table {
    236309    margin-bottom: 30px;
    237310}
     311
    238312#krakenSettings .form-table th {
    239     width: 370px;
    240 }
     313    width: 270px;
     314    border-bottom: 1px solid #ddd;
     315    padding: 25px 0;
     316}
     317
     318#krakenSettings .form-table td {
     319    border-bottom: 1px solid #ddd;
     320    padding: 25px 0;
     321}
     322
     323#krakenSettings .form-table .no-border td,
     324#krakenSettings .form-table .no-border th {
     325    border: none;
     326}
     327
     328#krakenSettings .form-table .with-tip td,
     329#krakenSettings .form-table .with-tip th {
     330    border-bottom: none;
     331    padding-bottom: 10px;
     332}
     333
     334.tip td {
     335    padding: 10px 0 20px 0 !important;
     336}
     337
     338.tip td div {
     339    border: 1px solid #BCE8F1;
     340    background: #D9EDF7;
     341    padding: 5px 10px;
     342    line-height: 20px;
     343    font-size: 12px;
     344}
     345
    241346.kraken.error.settings-error, .kraken.updated.settings-error  {
    242347    margin-bottom: 30px;
    243348}
     349.blog-kraker-info-block-wrap {
     350    margin-top: 10px;
     351    margin-left: 10px;
     352    padding-bottom: 110px;
     353    width: 600px;
     354}
     355.blog-kraker-image-summary {
     356    width: 50%;
     357    float: left;
     358}
     359.blog-kraker-kraken-statistics {
     360    width: 50%;
     361    float: right;
     362}
     363input[name=blog_kraker_mode] {
     364    margin-right: 10px;
     365}
     366label[for=blog_kraker_mode_thumbs] {
     367    margin-right: 10px;
     368}
     369.kraken-item-details {
     370    cursor: pointer;
     371    text-decoration: underline;
     372}
  • kraken-image-optimizer/trunk/css/dist/kraken.min.css

    r1144921 r1351884  
    1 .tipsy-ne .tipsy-arrow,.tipsy-nw .tipsy-arrow{border-top:none;top:0;border-bottom-style:solid}.tipsy-inner,td.kraken-krakedsize{text-align:left}.kraken-plus-minus,td.krakenAdvancedSettings h3>.kraken-advanced-settings-label:hover{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-khtml-user-select:none}.kraken-modal{display:none;width:600px;background:#fff;padding:15px 30px;border-radius:8px;-webkit-box-shadow:0 0 10px #000;-moz-box-shadow:0 0 10px #000;-o-box-shadow:0 0 10px #000;-ms-box-shadow:0 0 10px #000;box-shadow:0 0 10px #000}.kraken-modal,.kraken-modal-spinner{-o-border-radius:8px;-ms-border-radius:8px;-webkit-border-radius:8px;-moz-border-radius:8px}.kraken-modal-spinner{display:none;width:64px;height:64px;position:fixed;top:50%;left:50%;margin-right:-32px;margin-top:-32px;background:url(spinner.gif)center center no-repeat #111;border-radius:8px}.tipsy{font-size:10px;position:absolute;padding:5px;z-index:100000}.tipsy-inner{background-color:#000;color:#FFF;max-width:300px;padding:5px 8px 4px;border-radius:3px;-moz-border-radius:3px;-webkit-border-radius:3px;box-shadow:0 0 5px #000;-webkit-box-shadow:0 0 5px #000;-moz-box-shadow:0 0 5px #000}.head_cell,.head_file_name,.item_file_name{height:20px;padding-top:5px}.tipsy-arrow{position:absolute;width:0;height:0;line-height:0;border:5px dashed #000}.tipsy-arrow-n{border-bottom-color:#000}.tipsy-arrow-s{border-top-color:#000}.tipsy-arrow-e{border-left-color:#000}.tipsy-n .tipsy-arrow,.tipsy-ne .tipsy-arrow,.tipsy-nw .tipsy-arrow,.tipsy-s .tipsy-arrow,.tipsy-se .tipsy-arrow,.tipsy-sw .tipsy-arrow{border-left-color:transparent}.tipsy-arrow-w{border-right-color:#000}.tipsy-n .tipsy-arrow,.tipsy-ne .tipsy-arrow,.tipsy-nw .tipsy-arrow,.tipsy-s .tipsy-arrow,.tipsy-se .tipsy-arrow,.tipsy-sw .tipsy-arrow{border-right-color:transparent}.tipsy-n .tipsy-arrow{top:0;left:50%;margin-left:-5px;border-bottom-style:solid;border-top:none}.tipsy-nw .tipsy-arrow{left:10px}.tipsy-ne .tipsy-arrow{right:10px}.tipsy-s .tipsy-arrow,.tipsy-se .tipsy-arrow,.tipsy-sw .tipsy-arrow{border-bottom:none;border-top-style:solid}.tipsy-e .tipsy-arrow,.tipsy-w .tipsy-arrow{border-bottom-color:transparent;top:50%;border-top-color:transparent}.tipsy-s .tipsy-arrow{bottom:0;left:50%;margin-left:-5px}.tipsy-sw .tipsy-arrow{bottom:0;left:10px}.tipsy-se .tipsy-arrow{bottom:0;right:10px}.tipsy-e .tipsy-arrow{right:0;margin-top:-5px;border-left-style:solid;border-right:none}.tipsy-w .tipsy-arrow{left:0;margin-top:-5px;border-right-style:solid;border-left:none}.item_cell,.item_file_name{background-color:#F8F8F8;margin-top:5px}.head_cell{float:left;width:10em;background-color:#F0F0F0}.head_file_name{float:left;width:30em;background-color:#F0F0F0;padding-left:2px}.item_cell{float:left;width:10em;height:20px;padding-top:5px}.item_file_name{float:left;width:30em;padding-left:2px}.apiInvalid,.apiValid{position:absolute;width:16px}#kraken_loading{float:left;clear:left}.kraken_req,.kraken_req_bulk{border:1px solid #efab00;color:#222;padding:3px 8px 4px;font-size:11px;text-shadow:0 1px 0 rgba(255,255,255,.5);background:#ffc942;background-image:-webkit-gradient(linear,left top,left bottom,from(#ffc942),to(#efab00));background-image:-webkit-linear-gradient(top,#ffc942,#efab00);background-image:-moz-linear-gradient(top,#ffc942,#efab00);background-image:-o-linear-gradient(top,#ffc942,#efab00);background-image:linear-gradient(to bottom,#ffc942,#efab00);-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px;cursor:pointer;font-weight:700}.krakenBulkSpinner,.krakenSpinner,.resetSpinner{background:url(https://kraken-nekkraug.netdna-ssl.com/assets/images/spinner.gif)no-repeat}.kraken_req_bulk{font-size:16px}.buttonWrap{position:relative;margin-bottom:5px}.apiValid{left:0;display:inline-block;height:16px;top:3px}.apiInvalid{left:0;display:inline-block;height:16px;top:2px}p.apiStatus{padding-left:26px;position:relative;width:400px}.krakenSpinner{display:none;position:absolute;width:16px;height:16px;top:5px}.krakenBulkSpinner{display:none;padding:0;margin:0;width:16px;height:16px}.krakenError,.noSavings{margin-left:2px}.kraken-bulk-small,.kraken-plus-minus,.radiosWrap,.resetSpinner,.visible{display:inline-block}.krakenErrorWrap{margin-top:10px;margin-bottom:4px}.krakenError{font-weight:700;color:#dd3d36}.krakenBulkHeader{font-size:16px;margin-bottom:10px;font-weight:700}#kraken-bulk-modal{position:absolute!important;top:100px;width:785px}#kraken-bulk-modal label{margin-bottom:11px}#kraken-bulk{width:780px;margin:0 auto 10px;border-spacing:1px;border-collapse:separate}.kraken-bulk-header td{font-weight:700;font-size:14px;padding:4px 0 4px 2px}tr.kraken-item-row td{padding:2px;height:20px}span.kraken-bulk-choose-type{font-weight:700;margin-right:5px}#kraken-bulk-type-lossy{margin-right:5px}.close-kraken-bulk{font-size:12px;margin-left:10px;font-weight:700;cursor:pointer}.radiosWrap{margin:0 0 5px 4px}.kraken-bulk-small{font-size:12px;margin:0 0 4px 4px}small.krakenReset{text-decoration:underline;cursor:pointer}.resetSpinner{margin-right:3px;margin-top:4px;vertical-align:baseline;width:10px;height:10px;background-size:10px}h1.kraken-admin-section-title{margin-bottom:30px}.kraken_show_reset{margin-right:11px}small.krakenWhatsThis{font-size:9px}span.kraken-reset-all{font-size:11px}span.kraken-reset-all.enabled{text-decoration:underline;cursor:pointer}td.krakenAdvancedSettings{padding:0}td.krakenAdvancedSettingsDescription{padding:0 0 10px}td.krakenAdvancedSettings h3{margin-bottom:2px}#krakenSettings .form-table,.kraken.error.settings-error,.kraken.updated.settings-error{margin-bottom:30px}td.krakenAdvancedSettings h3>.kraken-advanced-settings-label:hover{cursor:pointer;color:#45bbe6;user-select:none}td.krakenAdvancedSettings h3 span.kraken-advanced-toggle{color:#45bbe6;font-size:10px}.kraken-plus-minus{cursor:pointer;line-height:24px;width:20px;user-select:none}.kraken-advanced-settings{display:none}#krakenSettings .form-table th{width:370px}
     1.tipsy-ne .tipsy-arrow,.tipsy-nw .tipsy-arrow{border-top:none;top:0;border-bottom-style:solid}.kraken-plus-minus,td.krakenAdvancedSettings h3>.kraken-advanced-settings-label:hover{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-khtml-user-select:none}.kraken-modal{display:none;width:600px;background:#fff;padding:15px 30px;border-radius:8px;-webkit-box-shadow:0 0 10px #000;-moz-box-shadow:0 0 10px #000;-o-box-shadow:0 0 10px #000;-ms-box-shadow:0 0 10px #000;box-shadow:0 0 10px #000}.kraken-modal,.kraken-modal-spinner{-o-border-radius:8px;-ms-border-radius:8px;-webkit-border-radius:8px;-moz-border-radius:8px}.kraken-modal-spinner{display:none;width:64px;height:64px;position:fixed;top:50%;left:50%;margin-right:-32px;margin-top:-32px;background:url(spinner.gif)center center no-repeat #111;border-radius:8px}.tipsy{font-size:10px;position:absolute;padding:5px;z-index:100000}.tipsy-inner{background-color:#000;color:#FFF;max-width:300px;padding:5px 8px 4px;text-align:left;border-radius:3px;-moz-border-radius:3px;-webkit-border-radius:3px;box-shadow:0 0 5px #000;-webkit-box-shadow:0 0 5px #000;-moz-box-shadow:0 0 5px #000}.head_cell,.head_file_name,.item_file_name{height:20px;padding-top:5px}.tipsy-arrow{position:absolute;width:0;height:0;line-height:0;border:5px dashed #000}.tipsy-arrow-n{border-bottom-color:#000}.tipsy-arrow-s{border-top-color:#000}.tipsy-arrow-e{border-left-color:#000}.tipsy-n .tipsy-arrow,.tipsy-ne .tipsy-arrow,.tipsy-nw .tipsy-arrow,.tipsy-s .tipsy-arrow,.tipsy-se .tipsy-arrow,.tipsy-sw .tipsy-arrow{border-left-color:transparent}.tipsy-arrow-w{border-right-color:#000}.tipsy-n .tipsy-arrow,.tipsy-ne .tipsy-arrow,.tipsy-nw .tipsy-arrow,.tipsy-s .tipsy-arrow,.tipsy-se .tipsy-arrow,.tipsy-sw .tipsy-arrow{border-right-color:transparent}.tipsy-n .tipsy-arrow{top:0;left:50%;margin-left:-5px;border-bottom-style:solid;border-top:none}.tipsy-nw .tipsy-arrow{left:10px}.tipsy-ne .tipsy-arrow{right:10px}.tipsy-s .tipsy-arrow,.tipsy-se .tipsy-arrow,.tipsy-sw .tipsy-arrow{border-bottom:none;border-top-style:solid}.tipsy-e .tipsy-arrow,.tipsy-w .tipsy-arrow{border-bottom-color:transparent;top:50%;border-top-color:transparent}.tipsy-s .tipsy-arrow{bottom:0;left:50%;margin-left:-5px}.tipsy-sw .tipsy-arrow{bottom:0;left:10px}.tipsy-se .tipsy-arrow{bottom:0;right:10px}.tipsy-e .tipsy-arrow{right:0;margin-top:-5px;border-left-style:solid;border-right:none}.tipsy-w .tipsy-arrow{left:0;margin-top:-5px;border-right-style:solid;border-left:none}.item_cell,.item_file_name{background-color:#F8F8F8;margin-top:5px}.head_cell{float:left;width:10em;background-color:#F0F0F0}.head_file_name{float:left;width:30em;background-color:#F0F0F0;padding-left:2px}.item_cell{float:left;width:10em;height:20px;padding-top:5px}.item_file_name{float:left;width:30em;padding-left:2px}#kraken_loading{float:left;clear:left}.kraken_req,.kraken_req_bulk{border:none;color:#fff;padding:3px 8px 4px;font-size:11px;background:#3498DB;-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px;cursor:pointer;transition:all .15s}.kraken_req:hover,.kraken_req_bulk:hover{background:#2881BD}.krakenBulkSpinner,.krakenSpinner{background:url(https://kraken-nekkraug.netdna-ssl.com/assets/images/spinner.gif)no-repeat}.kraken_req{margin-right:2px}.kraken_req_bulk{font-size:16px;margin:0 auto;width:150px;padding:10px 0;display:block}td.kraken-bulk-filename{width:360px;max-width:360px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}td.kraken-krakedsize{width:170px}.apiInvalid,.apiValid{position:absolute;width:16px}.buttonWrap{position:relative;margin-bottom:5px}.apiValid{left:0;display:inline-block;height:16px;top:3px}.apiInvalid{left:0;display:inline-block;height:16px;top:2px}p.apiStatus{padding-left:26px;position:relative;width:400px}.krakenSpinner{display:none;position:absolute;width:16px;height:16px;top:5px}.krakenBulkSpinner{display:none;padding:0;margin:0;width:16px;height:16px}.krakenError,.noSavings{margin-left:2px}.kraken-bulk-small,.kraken-plus-minus,.resetSpinner,.visible{display:inline-block}.krakenErrorWrap{margin-top:10px;margin-bottom:4px}.krakenError{font-weight:700;color:#dd3d36}.krakenBulkHeader{font-size:16px;font-weight:700;padding:15px 20px;border-bottom:1px solid #ddd;margin:0;background:#fafafa;position:relative}.krakenBulkHeader span{position:absolute;top:5px;right:15px;font-size:30px;color:#777}.krakenBulkHeader span:hover{color:#222}#kraken-bulk-modal{position:absolute!important;top:100px;width:785px;padding:0 0 20px}#kraken-bulk-modal label{margin-bottom:11px}#kraken-bulk{width:770px;margin:0 auto 20px;border-collapse:collapse}#kraken-bulk td{border:1px solid #ddd;padding:5px 10px}#kraken-bulk tr:nth-child(odd){background-color:#eee}#kraken-bulk-modal .the-following{margin:0;text-align:center;padding:15px 0}.kraken-bulk-header td{font-weight:700;font-size:13px;padding:7px 10px!important;background:#ddd;color:#212121;border:1px solid #ccc!important}td.kraken-krakedsize{text-align:left}tr.kraken-item-row td{padding:2px;height:20px}span.kraken-bulk-choose-type{font-weight:700;margin-right:5px}#kraken-bulk-type-lossy{margin-right:5px}.close-kraken-bulk{font-size:12px;margin-left:10px;font-weight:700;cursor:pointer}.radiosWrap{width:100%;text-align:center;padding:15px 0;border-bottom:1px solid #ddd}.radiosWrap p{padding:0;margin:0 0 5px;font-weight:700}.radiosWrap input[type=radio]{margin-top:-1px;vertical-align:middle}.kraken-bulk-small{font-size:12px;margin:0 0 4px 4px}small.krakenReset{text-decoration:underline;cursor:pointer}.resetSpinner{margin-right:3px;margin-top:4px;vertical-align:baseline;width:10px;height:10px;background:url(https://kraken-nekkraug.netdna-ssl.com/assets/images/spinner.gif)no-repeat;background-size:10px}h1.kraken-admin-section-title{margin-bottom:30px}.kraken_show_reset{margin-right:11px}small.krakenWhatsThis{font-size:9px}span.kraken-reset-all{font-size:11px}span.kraken-reset-all.enabled{text-decoration:underline;cursor:pointer}td.krakenAdvancedSettings{padding:20px 0 0!important}td.krakenAdvancedSettingsDescription{padding:10px 0!important}td.krakenAdvancedSettings h3{margin-bottom:2px}#krakenSettings .form-table,.kraken.error.settings-error,.kraken.updated.settings-error{margin-bottom:30px}td.krakenAdvancedSettings h3>.kraken-advanced-settings-label:hover{cursor:pointer;color:#45bbe6;user-select:none}td.krakenAdvancedSettings h3 span.kraken-advanced-toggle{color:#45bbe6;font-size:10px}.kraken-plus-minus{cursor:pointer;line-height:24px;width:20px;user-select:none}#krakenSettings .form-table th{width:270px;border-bottom:1px solid #ddd;padding:25px 0}#krakenSettings .form-table td{border-bottom:1px solid #ddd;padding:25px 0}#krakenSettings .form-table .no-border td,#krakenSettings .form-table .no-border th{border:none}#krakenSettings .form-table .with-tip td,#krakenSettings .form-table .with-tip th{border-bottom:none;padding-bottom:10px}.tip td{padding:10px 0 20px!important}.tip td div{border:1px solid #BCE8F1;background:#D9EDF7;padding:5px 10px;line-height:20px;font-size:12px}.blog-kraker-info-block-wrap{margin-top:10px;margin-left:10px;padding-bottom:110px;width:600px}.blog-kraker-image-summary{width:50%;float:left}.blog-kraker-kraken-statistics{width:50%;float:right}input[name=blog_kraker_mode],label[for=blog_kraker_mode_thumbs]{margin-right:10px}.kraken-item-details{cursor:pointer;text-decoration:underline}
  • kraken-image-optimizer/trunk/js/ajax.js

    r1144921 r1351884  
    11jQuery(document).ready(function($) {
    22
    3     var errors = [{
    4         code: 401,
    5         msg: 'Unnknown API Key. Please check your API key and try again'
    6     }, {
    7         code: 403,
    8         msg: 'Your account has been temporarily suspended'
    9     }, {
    10         code: 413,
    11         msg: 'File size too large. The maximum file size for your plan is 1048576 bytes'
    12     }, {
    13         code: 415,
    14         msg: 'File type not supported'
    15     }, {
    16         code: 415,
    17         msg: 'WebP compression is non available for SVG images'
    18     }, {
    19         code: 422,
    20         msg: 'You need to specify either callback_url or wait flag'
    21     }, {
    22         code: 422,
    23         msg: 'This image can not be optimized any further'
    24     }, {
    25         code: 500,
    26         msg: 'Kraken has encountered an unexpected error and cannot fulfill your request'
    27     }, {
    28         code: 502,
    29         msg: 'Couldn\'t get this file'
    30     }];
    31 
     3    var tipsySettings =  {
     4        gravity: 'e',
     5        html: true,
     6        trigger: 'manual',
     7        className: function() {
     8            return 'tipsy-' + $(this).data('id');
     9        },
     10        title: function() {
     11            activeId = $(this).data('id');
     12            return $(this).attr('original-title');
     13        }
     14    }
    3215
    3316    $('.krakenWhatsThis').tipsy({
    3417        fade: true,
    3518        gravity: 'w'
     19    });
     20
     21    $('.krakenError').tipsy({
     22        fade: true,
     23        gravity: 'e'
    3624    });
    3725
     
    4937    var requestSuccess = function(data, textStatus, jqXHR) {
    5038        var $button = $(this),
    51             $parent = $(this).parent(),
     39            $parent = $(this).closest('.kraken-wrap, .buttonWrap'),
    5240            $cell = $(this).closest("td");
    5341
    54         if (data.success && typeof data.error === 'undefined') {
    55 
     42        if (data.html) {
    5643            $button.text("Image optimized");
    5744
    5845            var type = data.type,
    59                 krakedSize = data.kraked_size,
    6046                originalSize = data.original_size,
    61                 savingsPercent = data.savings_percent,
    6247                $originalSizeColumn = $(this).parent().prev("td.original_size"),
    6348                krakedData = '';
    6449
    6550            $parent.fadeOut("fast", function() {
    66                 $cell.find(".noSavings, .krakenErrorWrap").remove();
    67                 $(this).replaceWith(data.html);
     51                $cell
     52                    .find(".noSavings, .krakenErrorWrap")
     53                    .remove();
     54                $cell.html(data.html);
     55                $cell.find('.kraken-item-details')
     56                    .tipsy(tipsySettings);
    6857                $originalSizeColumn.html(originalSize);
    6958                $parent.remove();
     
    138127    };
    139128
     129    var bulkModalOptions = {
     130        zIndex: 4,
     131        escapeClose: true,
     132        clickClose: false,
     133        closeText: 'close',
     134        showClose: false
     135    };
     136
    140137    var renderBulkImageSummary = function(bulkImageData) {
    141         var modalOptions = {
    142                 zIndex: 4,
    143                 escapeClose: true,
    144                 clickClose: false,
    145                 closeText: 'close',
    146                 showClose: false
    147             },
    148             setting = kraken_settings.api_lossy,
    149             nImages = bulkImageData.length,
    150             header = '<p class="krakenBulkHeader">Kraken Bulk Image Optimization</p>',
    151             krakEmAll = '<button class="kraken_req_bulk">Krak \'em all</button>',
    152             typeRadios = '<span class="radiosWrap"><span class="kraken-bulk-choose-type">Choose:</span>' + '<input type="radio" id="kraken-bulk-type-lossy" value="Lossy" name="kraken-bulk-type"/>' + '<label for="kraken-bulk-type-lossy">Lossy</label>&nbsp;' + '<input type="radio" id="kraken-bulk-type-lossless" value="Lossless" name="kraken-bulk-type"/>' + '<label for="kraken-bulk-type-lossless">Lossless</label></span>',
    153             $modal = $('<div id="kraken-bulk-modal" class="kraken-modal"></div>')
    154             .html(header)
    155             .append(typeRadios)
    156             .append('<br /><small class="kraken-bulk-small">The following <strong>' + nImages + '</strong> images will be optimized by Kraken.io using the <strong class="bulkSetting">' + setting + '</strong> setting:</small><br />')
    157             .appendTo("body")
    158             .kmodal(modalOptions)
    159             .bind($.kmodal.BEFORE_CLOSE, function(event, modal) {
    160 
    161             })
    162             .bind($.kmodal.OPEN, function(event, modal) {
    163 
    164             })
    165             .bind($.kmodal.CLOSE, function(event, modal) {
    166                 $("#kraken-bulk-modal").remove();
    167             })
    168             .css({
    169                 top: "10px",
    170                 marginTop: "40px"
    171             });
    172 
    173         if (setting === 'lossy') {
     138        var setting = kraken_settings.api_lossy;
     139        var nImages = bulkImageData.length;
     140        var header = '<p class="krakenBulkHeader">Kraken Bulk Image Optimization <span class="close-kraken-bulk">&times;</span></p>';
     141        var krakEmAll = '<button class="kraken_req_bulk">Krak \'em all</button>';
     142        var typeRadios = '<div class="radiosWrap"><p>Choose optimization mode:</p><label><input type="radio" id="kraken-bulk-type-lossy" value="Lossy" name="kraken-bulk-type"/>Intelligent Lossy</label>&nbsp;&nbsp;&nbsp;<label><input type="radio" id="kraken-bulk-type-lossless" value="Lossless" name="kraken-bulk-type"/>Lossless</label></div>';
     143
     144        var $modal = $('<div id="kraken-bulk-modal" class="kraken-modal"></div>')
     145                .html(header)
     146                .append(typeRadios)
     147                .append('<p class="the-following">The following <strong>' + nImages + '</strong> images will be optimized by Kraken.io using the <strong class="bulkSetting">' + setting + '</strong> setting:</p>')
     148                .appendTo("body")
     149                .kmodal(bulkModalOptions)
     150                .bind($.kmodal.BEFORE_CLOSE, function(event, modal) {
     151
     152                })
     153                .bind($.kmodal.OPEN, function(event, modal) {
     154
     155                })
     156                .bind($.kmodal.CLOSE, function(event, modal) {
     157                    $("#kraken-bulk-modal").remove();
     158                })
     159                .css({
     160                    top: "10px",
     161                    marginTop: "40px"
     162                });
     163
     164        if (setting === "lossy") {
    174165            $("#kraken-bulk-type-lossy").attr("checked", true);
    175166        } else {
     
    194185
    195186        var $table = $('<table id="kraken-bulk"></table>'),
    196             $headerRow = $('<tr class="kraken-bulk-header"><td>File</td><td style="width:120px">Original Size</td><td style="width:120px">Kraked Size</td><td style="width:120px">Savings</td><td style="width:120px">% Savings</td></tr>');
     187            $headerRow = $('<tr class="kraken-bulk-header"><td>File Name</td><td style="width:120px">Original Size</td><td style="width:120px">Kraken.io Stats</td></tr>');
    197188
    198189        $table.append($headerRow);
    199190        $.each(bulkImageData, function(index, element) {
    200             $table.append('<tr class="kraken-item-row" data-krakenbulkid="' + element.id + '"><td class="kraken-filename">' + element.filename + '</td><td class="kraken-originalsize">' + element.originalSize + '</td><td class="kraken-krakedsize"><span class="krakenBulkSpinner hidden"></span></td><td class="kraken-savings"></td><td class="kraken-savingsPercent"></td></tr>');
     191            $table.append('<tr class="kraken-item-row" data-krakenbulkid="' + element.id + '"><td class="kraken-bulk-filename">' + element.filename + '</td><td class="kraken-originalsize">' + element.originalSize + '</td><td class="kraken-krakedsize"><span class="krakenBulkSpinner hidden"></span></td></tr>');
    201192        });
    202193
    203194        $modal
    204195            .append($table)
    205             .append(krakEmAll)
    206             .append('<span class="close-kraken-bulk">Close Window</span>');
     196            .append(krakEmAll);
    207197
    208198        $(".close-kraken-bulk").click(function() {
     
    218208        }
    219209    };
     210
    220211
    221212    var bulkAction = function(bulkImageData) {
     
    243234                    'action': 'kraken_request',
    244235                    'id': id,
    245                     'type': $("input[name='kraken-bulk-type']:checked").val().toLowerCase()
     236                    'type': $("input[name='kraken-bulk-type']:checked").val().toLowerCase(),
     237                    'origin': 'bulk_optimizer'
    246238                },
    247239                type: "post",
     
    250242            })
    251243                .done(function(data, textStatus, jqXHR) {
    252                     if (data.success && typeof data.error === 'undefined') {
     244                    if (data.success && typeof data.message === 'undefined') {
    253245                        var type = data.type,
    254246                            originalSize = data.original_size,
    255                             krakedSize = data.kraked_size,
     247                            krakedSize = data.html,
    256248                            savingsPercent = data.savings_percent,
    257249                            savingsBytes = data.saved_bytes;
    258250
    259                         $krakedSizeColumn.text(krakedSize);
     251                        $krakedSizeColumn.html(data.html);
     252
     253                        $krakedSizeColumn
     254                            .find('.kraken-item-details')
     255                            .remove();
     256
    260257                        $savingsPercentColumn.text(savingsPercent);
    261258                        $savingsBytesColumn.text(savingsBytes);
     
    264261                            $parent = $button.parent(),
    265262                            $cell = $button.closest("td"),
    266                             $originalSizeColumn = $button.parent().prev("td.original_size")
     263                            $originalSizeColumn = $button.parent().prev("td.original_size");
    267264
    268265
    269266                        $parent.fadeOut("fast", function() {
    270267                            $cell.find(".noSavings, .krakenErrorWrap").remove();
    271                             krakedData = '<strong>' + krakedSize + '</strong><br /><small>Type:&nbsp;' + type + '</small><br /><small>Savings: ' + savingsPercent + '</small>';
    272                             if (typeof data.thumbs_data !== 'undefined') {
    273                                 krakedData += '<br /><small>' + data.thumbs_data.length + ' thumbs optimized</small>';
    274                             }
    275                             $(this).replaceWith(krakedData);
     268                            $cell
     269                                .empty()
     270                                .html(data.html);
     271                            $cell
     272                                .find('.kraken-item-details')
     273                                .tipsy(tipsySettings);
    276274                            $originalSizeColumn.html(originalSize);
    277275                            $parent.remove();
     
    285283                        }
    286284                    }
    287 
    288285                })
    289286
     
    339336        });
    340337
     338    var activeId = null;
     339    $('.kraken-item-details').tipsy(tipsySettings);
     340
     341    var $activePopup = null;
     342    $('body').on('click', '.kraken-item-details', function(e) {
     343        //$('.tipsy[class="tipsy-' + activeId + '"]').remove();
     344
     345        var id = $(this).data('id');
     346        $('.tipsy').remove();
     347        if (id == activeId) {
     348            activeId = null;
     349            $(this).text('Show details');
     350            return;
     351        }
     352        $('.kraken-item-details').text('Show details');
     353        $(this).tipsy('show');
     354        $(this).text('Hide details');
     355    });
     356
     357    $('body').on('click', function(e) {
     358        var $t = $(e.target);
     359        if (($t.hasClass('tipsy') || $t.closest('.tipsy').length) || $t.hasClass('kraken-item-details')) {
     360            return;
     361        } else {
     362            activeId = null;
     363            $('.kraken-item-details').text('Show details');
     364            $('.tipsy').remove();
     365        }
     366    });
     367
    341368    $('body').on('click', 'small.krakenReset', function(e) {
    342369        e.preventDefault();
     
    345372            action: 'kraken_reset'
    346373        };
     374
    347375        resetData.id = $(this).data("id");
     376        $row = $('#post-' + resetData.id).find('.kraked_size');
    348377
    349378        var $spinner = $('<span class="resetSpinner"></span>');
     
    359388            .done(function(data, textStatus, jqXHR) {
    360389                if (data.success !== 'undefined') {
    361                     $resetButton
    362                         .closest('.kraked_size.column-kraked_size')
     390                    $row
    363391                        .hide()
    364392                        .html(data.html)
     
    366394                        .prev(".original_size.column-original_size")
    367395                        .html(data.original_size);
     396
     397                    $('.tipsy').remove();
    368398                }
    369399            });
     
    418448            $plusMinus
    419449                .removeClass('dashicons-arrow-right')
    420                 .addClass('dashicons-arrow-down');       
     450                .addClass('dashicons-arrow-down');
    421451        }
    422452    });
  • kraken-image-optimizer/trunk/js/dist/kraken.min.js

    r1144921 r1351884  
    1 !function(){function only_once(fn){var called=!1;return function(){if(called)throw new Error("Callback was already called.");called=!0,fn.apply(root,arguments)}}var root,previous_async,async={};root=this,null!=root&&(previous_async=root.async),async.noConflict=function(){return root.async=previous_async,async};var _each=function(arr,iterator){if(arr.forEach)return arr.forEach(iterator);for(var i=0;i<arr.length;i+=1)iterator(arr[i],i,arr)},_map=function(arr,iterator){if(arr.map)return arr.map(iterator);var results=[];return _each(arr,function(x,i,a){results.push(iterator(x,i,a))}),results},_reduce=function(arr,iterator,memo){return arr.reduce?arr.reduce(iterator,memo):(_each(arr,function(x,i,a){memo=iterator(memo,x,i,a)}),memo)},_keys=function(obj){if(Object.keys)return Object.keys(obj);var keys=[];for(var k in obj)obj.hasOwnProperty(k)&&keys.push(k);return keys};"undefined"!=typeof process&&process.nextTick?(async.nextTick=process.nextTick,"undefined"!=typeof setImmediate?async.setImmediate=function(fn){setImmediate(fn)}:async.setImmediate=async.nextTick):"function"==typeof setImmediate?(async.nextTick=function(fn){setImmediate(fn)},async.setImmediate=async.nextTick):(async.nextTick=function(fn){setTimeout(fn,0)},async.setImmediate=async.nextTick),async.each=function(arr,iterator,callback){if(callback=callback||function(){},!arr.length)return callback();var completed=0;_each(arr,function(x){iterator(x,only_once(function(err){err?(callback(err),callback=function(){}):(completed+=1,completed>=arr.length&&callback(null))}))})},async.forEach=async.each,async.eachSeries=function(arr,iterator,callback){if(callback=callback||function(){},!arr.length)return callback();var completed=0,iterate=function(){iterator(arr[completed],function(err){err?(callback(err),callback=function(){}):(completed+=1,completed>=arr.length?callback(null):iterate())})};iterate()},async.forEachSeries=async.eachSeries,async.eachLimit=function(arr,limit,iterator,callback){var fn=_eachLimit(limit);fn.apply(null,[arr,iterator,callback])},async.forEachLimit=async.eachLimit;var _eachLimit=function(limit){return function(arr,iterator,callback){if(callback=callback||function(){},!arr.length||0>=limit)return callback();var completed=0,started=0,running=0;!function replenish(){if(completed>=arr.length)return callback();for(;limit>running&&started<arr.length;)started+=1,running+=1,iterator(arr[started-1],function(err){err?(callback(err),callback=function(){}):(completed+=1,running-=1,completed>=arr.length?callback():replenish())})}()}},doParallel=function(fn){return function(){var args=Array.prototype.slice.call(arguments);return fn.apply(null,[async.each].concat(args))}},doParallelLimit=function(limit,fn){return function(){var args=Array.prototype.slice.call(arguments);return fn.apply(null,[_eachLimit(limit)].concat(args))}},doSeries=function(fn){return function(){var args=Array.prototype.slice.call(arguments);return fn.apply(null,[async.eachSeries].concat(args))}},_asyncMap=function(eachfn,arr,iterator,callback){var results=[];arr=_map(arr,function(x,i){return{index:i,value:x}}),eachfn(arr,function(x,callback){iterator(x.value,function(err,v){results[x.index]=v,callback(err)})},function(err){callback(err,results)})};async.map=doParallel(_asyncMap),async.mapSeries=doSeries(_asyncMap),async.mapLimit=function(arr,limit,iterator,callback){return _mapLimit(limit)(arr,iterator,callback)};var _mapLimit=function(limit){return doParallelLimit(limit,_asyncMap)};async.reduce=function(arr,memo,iterator,callback){async.eachSeries(arr,function(x,callback){iterator(memo,x,function(err,v){memo=v,callback(err)})},function(err){callback(err,memo)})},async.inject=async.reduce,async.foldl=async.reduce,async.reduceRight=function(arr,memo,iterator,callback){var reversed=_map(arr,function(x){return x}).reverse();async.reduce(reversed,memo,iterator,callback)},async.foldr=async.reduceRight;var _filter=function(eachfn,arr,iterator,callback){var results=[];arr=_map(arr,function(x,i){return{index:i,value:x}}),eachfn(arr,function(x,callback){iterator(x.value,function(v){v&&results.push(x),callback()})},function(err){callback(_map(results.sort(function(a,b){return a.index-b.index}),function(x){return x.value}))})};async.filter=doParallel(_filter),async.filterSeries=doSeries(_filter),async.select=async.filter,async.selectSeries=async.filterSeries;var _reject=function(eachfn,arr,iterator,callback){var results=[];arr=_map(arr,function(x,i){return{index:i,value:x}}),eachfn(arr,function(x,callback){iterator(x.value,function(v){v||results.push(x),callback()})},function(err){callback(_map(results.sort(function(a,b){return a.index-b.index}),function(x){return x.value}))})};async.reject=doParallel(_reject),async.rejectSeries=doSeries(_reject);var _detect=function(eachfn,arr,iterator,main_callback){eachfn(arr,function(x,callback){iterator(x,function(result){result?(main_callback(x),main_callback=function(){}):callback()})},function(err){main_callback()})};async.detect=doParallel(_detect),async.detectSeries=doSeries(_detect),async.some=function(arr,iterator,main_callback){async.each(arr,function(x,callback){iterator(x,function(v){v&&(main_callback(!0),main_callback=function(){}),callback()})},function(err){main_callback(!1)})},async.any=async.some,async.every=function(arr,iterator,main_callback){async.each(arr,function(x,callback){iterator(x,function(v){v||(main_callback(!1),main_callback=function(){}),callback()})},function(err){main_callback(!0)})},async.all=async.every,async.sortBy=function(arr,iterator,callback){async.map(arr,function(x,callback){iterator(x,function(err,criteria){err?callback(err):callback(null,{value:x,criteria:criteria})})},function(err,results){if(err)return callback(err);var fn=function(left,right){var a=left.criteria,b=right.criteria;return b>a?-1:a>b?1:0};callback(null,_map(results.sort(fn),function(x){return x.value}))})},async.auto=function(tasks,callback){callback=callback||function(){};var keys=_keys(tasks);if(!keys.length)return callback(null);var results={},listeners=[],addListener=function(fn){listeners.unshift(fn)},removeListener=function(fn){for(var i=0;i<listeners.length;i+=1)if(listeners[i]===fn)return void listeners.splice(i,1)},taskComplete=function(){_each(listeners.slice(0),function(fn){fn()})};addListener(function(){_keys(results).length===keys.length&&(callback(null,results),callback=function(){})}),_each(keys,function(k){var task=tasks[k]instanceof Function?[tasks[k]]:tasks[k],taskCallback=function(err){var args=Array.prototype.slice.call(arguments,1);if(args.length<=1&&(args=args[0]),err){var safeResults={};_each(_keys(results),function(rkey){safeResults[rkey]=results[rkey]}),safeResults[k]=args,callback(err,safeResults),callback=function(){}}else results[k]=args,async.setImmediate(taskComplete)},requires=task.slice(0,Math.abs(task.length-1))||[],ready=function(){return _reduce(requires,function(a,x){return a&&results.hasOwnProperty(x)},!0)&&!results.hasOwnProperty(k)};if(ready())task[task.length-1](taskCallback,results);else{var listener=function(){ready()&&(removeListener(listener),task[task.length-1](taskCallback,results))};addListener(listener)}})},async.waterfall=function(tasks,callback){if(callback=callback||function(){},tasks.constructor!==Array){var err=new Error("First argument to waterfall must be an array of functions");return callback(err)}if(!tasks.length)return callback();var wrapIterator=function(iterator){return function(err){if(err)callback.apply(null,arguments),callback=function(){};else{var args=Array.prototype.slice.call(arguments,1),next=iterator.next();args.push(next?wrapIterator(next):callback),async.setImmediate(function(){iterator.apply(null,args)})}}};wrapIterator(async.iterator(tasks))()};var _parallel=function(eachfn,tasks,callback){if(callback=callback||function(){},tasks.constructor===Array)eachfn.map(tasks,function(fn,callback){fn&&fn(function(err){var args=Array.prototype.slice.call(arguments,1);args.length<=1&&(args=args[0]),callback.call(null,err,args)})},callback);else{var results={};eachfn.each(_keys(tasks),function(k,callback){tasks[k](function(err){var args=Array.prototype.slice.call(arguments,1);args.length<=1&&(args=args[0]),results[k]=args,callback(err)})},function(err){callback(err,results)})}};async.parallel=function(tasks,callback){_parallel({map:async.map,each:async.each},tasks,callback)},async.parallelLimit=function(tasks,limit,callback){_parallel({map:_mapLimit(limit),each:_eachLimit(limit)},tasks,callback)},async.series=function(tasks,callback){if(callback=callback||function(){},tasks.constructor===Array)async.mapSeries(tasks,function(fn,callback){fn&&fn(function(err){var args=Array.prototype.slice.call(arguments,1);args.length<=1&&(args=args[0]),callback.call(null,err,args)})},callback);else{var results={};async.eachSeries(_keys(tasks),function(k,callback){tasks[k](function(err){var args=Array.prototype.slice.call(arguments,1);args.length<=1&&(args=args[0]),results[k]=args,callback(err)})},function(err){callback(err,results)})}},async.iterator=function(tasks){var makeCallback=function(index){var fn=function(){return tasks.length&&tasks[index].apply(null,arguments),fn.next()};return fn.next=function(){return index<tasks.length-1?makeCallback(index+1):null},fn};return makeCallback(0)},async.apply=function(fn){var args=Array.prototype.slice.call(arguments,1);return function(){return fn.apply(null,args.concat(Array.prototype.slice.call(arguments)))}};var _concat=function(eachfn,arr,fn,callback){var r=[];eachfn(arr,function(x,cb){fn(x,function(err,y){r=r.concat(y||[]),cb(err)})},function(err){callback(err,r)})};async.concat=doParallel(_concat),async.concatSeries=doSeries(_concat),async.whilst=function(test,iterator,callback){test()?iterator(function(err){return err?callback(err):void async.whilst(test,iterator,callback)}):callback()},async.doWhilst=function(iterator,test,callback){iterator(function(err){return err?callback(err):void(test()?async.doWhilst(iterator,test,callback):callback())})},async.until=function(test,iterator,callback){test()?callback():iterator(function(err){return err?callback(err):void async.until(test,iterator,callback)})},async.doUntil=function(iterator,test,callback){iterator(function(err){return err?callback(err):void(test()?callback():async.doUntil(iterator,test,callback))})},async.queue=function(worker,concurrency){function _insert(q,data,pos,callback){data.constructor!==Array&&(data=[data]),_each(data,function(task){var item={data:task,callback:"function"==typeof callback?callback:null};pos?q.tasks.unshift(item):q.tasks.push(item),q.saturated&&q.tasks.length===concurrency&&q.saturated(),async.setImmediate(q.process)})}void 0===concurrency&&(concurrency=1);var workers=0,q={tasks:[],concurrency:concurrency,saturated:null,empty:null,drain:null,push:function(data,callback){_insert(q,data,!1,callback)},unshift:function(data,callback){_insert(q,data,!0,callback)},process:function(){if(workers<q.concurrency&&q.tasks.length){var task=q.tasks.shift();q.empty&&0===q.tasks.length&&q.empty(),workers+=1;var next=function(){workers-=1,task.callback&&task.callback.apply(task,arguments),q.drain&&q.tasks.length+workers===0&&q.drain(),q.process()},cb=only_once(next);worker(task.data,cb)}},length:function(){return q.tasks.length},running:function(){return workers}};return q},async.cargo=function(worker,payload){var working=!1,tasks=[],cargo={tasks:tasks,payload:payload,saturated:null,empty:null,drain:null,push:function(data,callback){data.constructor!==Array&&(data=[data]),_each(data,function(task){tasks.push({data:task,callback:"function"==typeof callback?callback:null}),cargo.saturated&&tasks.length===payload&&cargo.saturated()}),async.setImmediate(cargo.process)},process:function process(){if(!working){if(0===tasks.length)return void(cargo.drain&&cargo.drain());var ts="number"==typeof payload?tasks.splice(0,payload):tasks.splice(0),ds=_map(ts,function(task){return task.data});cargo.empty&&cargo.empty(),working=!0,worker(ds,function(){working=!1;var args=arguments;_each(ts,function(data){data.callback&&data.callback.apply(null,args)}),process()})}},length:function(){return tasks.length},running:function(){return working}};return cargo};var _console_fn=function(name){return function(fn){var args=Array.prototype.slice.call(arguments,1);fn.apply(null,args.concat([function(err){var args=Array.prototype.slice.call(arguments,1);"undefined"!=typeof console&&(err?console.error&&console.error(err):console[name]&&_each(args,function(x){console[name](x)}))}]))}};async.log=_console_fn("log"),async.dir=_console_fn("dir"),async.memoize=function(fn,hasher){var memo={},queues={};hasher=hasher||function(x){return x};var memoized=function(){var args=Array.prototype.slice.call(arguments),callback=args.pop(),key=hasher.apply(null,args);key in memo?callback.apply(null,memo[key]):key in queues?queues[key].push(callback):(queues[key]=[callback],fn.apply(null,args.concat([function(){memo[key]=arguments;var q=queues[key];delete queues[key];for(var i=0,l=q.length;l>i;i++)q[i].apply(null,arguments)}])))};return memoized.memo=memo,memoized.unmemoized=fn,memoized},async.unmemoize=function(fn){return function(){return(fn.unmemoized||fn).apply(null,arguments)}},async.times=function(count,iterator,callback){for(var counter=[],i=0;count>i;i++)counter.push(i);return async.map(counter,iterator,callback)},async.timesSeries=function(count,iterator,callback){for(var counter=[],i=0;count>i;i++)counter.push(i);return async.mapSeries(counter,iterator,callback)},async.compose=function(){var fns=Array.prototype.reverse.call(arguments);return function(){var that=this,args=Array.prototype.slice.call(arguments),callback=args.pop();async.reduce(fns,args,function(newargs,fn,cb){fn.apply(that,newargs.concat([function(){var err=arguments[0],nextargs=Array.prototype.slice.call(arguments,1);cb(err,nextargs)}]))},function(err,results){callback.apply(that,[err].concat(results))})}};var _applyEach=function(eachfn,fns){var go=function(){var that=this,args=Array.prototype.slice.call(arguments),callback=args.pop();return eachfn(fns,function(fn,cb){fn.apply(that,args.concat([cb]))},callback)};if(arguments.length>2){var args=Array.prototype.slice.call(arguments,2);return go.apply(this,args)}return go};async.applyEach=doParallel(_applyEach),async.applyEachSeries=doSeries(_applyEach),async.forever=function(fn,callback){function next(err){if(err){if(callback)return callback(err);throw err}fn(next)}next()},"undefined"!=typeof define&&define.amd?define([],function(){return async}):"undefined"!=typeof module&&module.exports?module.exports=async:root.async=async}(),function($){function maybeCall(thing,ctx){return"function"==typeof thing?thing.call(ctx):thing}function isElementInDOM(ele){for(;ele=ele.parentNode;)if(ele==document)return!0;return!1}function Tipsy(element,options){this.$element=$(element),this.options=options,this.enabled=!0,this.fixTitle()}Tipsy.prototype={show:function(){var title=this.getTitle();if(title&&this.enabled){var $tip=this.tip();$tip.find(".tipsy-inner")[this.options.html?"html":"text"](title),$tip[0].className="tipsy",$tip.remove().css({top:0,left:0,visibility:"hidden",display:"block"}).prependTo(document.body);var tp,pos=$.extend({},this.$element.offset(),{width:this.$element[0].offsetWidth,height:this.$element[0].offsetHeight}),actualWidth=$tip[0].offsetWidth,actualHeight=$tip[0].offsetHeight,gravity=maybeCall(this.options.gravity,this.$element[0]);switch(gravity.charAt(0)){case"n":tp={top:pos.top+pos.height+this.options.offset,left:pos.left+pos.width/2-actualWidth/2};break;case"s":tp={top:pos.top-actualHeight-this.options.offset,left:pos.left+pos.width/2-actualWidth/2};break;case"e":tp={top:pos.top+pos.height/2-actualHeight/2,left:pos.left-actualWidth-this.options.offset};break;case"w":tp={top:pos.top+pos.height/2-actualHeight/2,left:pos.left+pos.width+this.options.offset}}2==gravity.length&&("w"==gravity.charAt(1)?tp.left=pos.left+pos.width/2-15:tp.left=pos.left+pos.width/2-actualWidth+15),$tip.css(tp).addClass("tipsy-"+gravity),$tip.find(".tipsy-arrow")[0].className="tipsy-arrow tipsy-arrow-"+gravity.charAt(0),this.options.className&&$tip.addClass(maybeCall(this.options.className,this.$element[0])),this.options.fade?$tip.stop().css({opacity:0,display:"block",visibility:"visible"}).animate({opacity:this.options.opacity}):$tip.css({visibility:"visible",opacity:this.options.opacity})}},hide:function(){this.options.fade?this.tip().stop().fadeOut(function(){$(this).remove()}):this.tip().remove()},fixTitle:function(){var $e=this.$element;($e.attr("title")||"string"!=typeof $e.attr("original-title"))&&$e.attr("original-title",$e.attr("title")||"").removeAttr("title")},getTitle:function(){var title,$e=this.$element,o=this.options;this.fixTitle();var title,o=this.options;return"string"==typeof o.title?title=$e.attr("title"==o.title?"original-title":o.title):"function"==typeof o.title&&(title=o.title.call($e[0])),title=(""+title).replace(/(^\s*|\s*$)/,""),title||o.fallback},tip:function(){return this.$tip||(this.$tip=$('<div class="tipsy"></div>').html('<div class="tipsy-arrow"></div><div class="tipsy-inner"></div>'),this.$tip.data("tipsy-pointee",this.$element[0])),this.$tip},validate:function(){this.$element[0].parentNode||(this.hide(),this.$element=null,this.options=null)},enable:function(){this.enabled=!0},disable:function(){this.enabled=!1},toggleEnabled:function(){this.enabled=!this.enabled}},$.fn.tipsy=function(options){function get(ele){var tipsy=$.data(ele,"tipsy");return tipsy||(tipsy=new Tipsy(ele,$.fn.tipsy.elementOptions(ele,options)),$.data(ele,"tipsy",tipsy)),tipsy}function enter(){var tipsy=get(this);tipsy.hoverState="in",0==options.delayIn?tipsy.show():(tipsy.fixTitle(),setTimeout(function(){"in"==tipsy.hoverState&&tipsy.show()},options.delayIn))}function leave(){var tipsy=get(this);tipsy.hoverState="out",0==options.delayOut?tipsy.hide():setTimeout(function(){"out"==tipsy.hoverState&&tipsy.hide()},options.delayOut)}if(options===!0)return this.data("tipsy");if("string"==typeof options){var tipsy=this.data("tipsy");return tipsy&&tipsy[options](),this}if(options=$.extend({},$.fn.tipsy.defaults,options),options.live||this.each(function(){get(this)}),"manual"!=options.trigger){var binder=options.live?"live":"bind",eventIn="hover"==options.trigger?"mouseenter":"focus",eventOut="hover"==options.trigger?"mouseleave":"blur";this[binder](eventIn,enter)[binder](eventOut,leave)}return this},$.fn.tipsy.defaults={className:null,delayIn:0,delayOut:0,fade:!1,fallback:"",gravity:"n",html:!1,live:!1,offset:0,opacity:.8,title:"title",trigger:"hover"},$.fn.tipsy.revalidate=function(){$(".tipsy").each(function(){var pointee=$.data(this,"tipsy-pointee");pointee&&isElementInDOM(pointee)||$(this).remove()})},$.fn.tipsy.elementOptions=function(ele,options){return $.metadata?$.extend({},options,$(ele).metadata()):options},$.fn.tipsy.autoNS=function(){return $(this).offset().top>$(document).scrollTop()+$(window).height()/2?"s":"n"},$.fn.tipsy.autoWE=function(){return $(this).offset().left>$(document).scrollLeft()+$(window).width()/2?"e":"w"},$.fn.tipsy.autoBounds=function(margin,prefer){return function(){var dir={ns:prefer[0],ew:prefer.length>1?prefer[1]:!1},boundTop=$(document).scrollTop()+margin,boundLeft=$(document).scrollLeft()+margin,$this=$(this);return $this.offset().top<boundTop&&(dir.ns="n"),$this.offset().left<boundLeft&&(dir.ew="w"),$(window).width()+$(document).scrollLeft()-$this.offset().left<margin&&(dir.ew="e"),$(window).height()+$(document).scrollTop()-$this.offset().top<margin&&(dir.ns="s"),dir.ns+(dir.ew?dir.ew:"")}}}(jQuery),function($){var current=null;$.kmodal=function(el,options){$.kmodal.close();var remove,target;if(this.$body=$("body"),this.options=$.extend({},$.kmodal.defaults,options),this.options.doFade=!isNaN(parseInt(this.options.fadeDuration,10)),el.is("a"))if(target=el.attr("href"),/^#/.test(target)){if(this.$elm=$(target),1!==this.$elm.length)return null;this.open()}else this.$elm=$("<div>"),this.$body.append(this.$elm),remove=function(event,modal){modal.elm.remove()},this.showSpinner(),el.trigger($.kmodal.AJAX_SEND),$.get(target).done(function(html){current&&(el.trigger($.kmodal.AJAX_SUCCESS),current.$elm.empty().append(html).on($.kmodal.CLOSE,remove),current.hideSpinner(),current.open(),el.trigger($.kmodal.AJAX_COMPLETE))}).fail(function(){el.trigger($.kmodal.AJAX_FAIL),current.hideSpinner(),el.trigger($.kmodal.AJAX_COMPLETE)});else this.$elm=el,this.$body.append(this.$elm),this.open()},$.kmodal.prototype={constructor:$.kmodal,open:function(){var m=this;this.options.doFade?(this.block(),setTimeout(function(){m.show()},this.options.fadeDuration*this.options.fadeDelay)):(this.block(),this.show()),this.options.escapeClose&&$(document).on("keydown.modal",function(event){27==event.which&&$.kmodal.close()}),this.options.clickClose&&this.blocker.click($.kmodal.close)},close:function(){this.unblock(),this.hide(),$(document).off("keydown.modal")},block:function(){var initialOpacity=this.options.doFade?0:this.options.opacity;this.$elm.trigger($.kmodal.BEFORE_BLOCK,[this._ctx()]),this.blocker=$('<div class="jquery-modal blocker"></div>').css({top:0,right:0,bottom:0,left:0,width:"100%",height:"100%",position:"fixed",zIndex:this.options.zIndex,background:this.options.overlay,opacity:initialOpacity}),this.$body.append(this.blocker),this.options.doFade&&this.blocker.animate({opacity:this.options.opacity},this.options.fadeDuration),this.$elm.trigger($.kmodal.BLOCK,[this._ctx()])},unblock:function(){this.options.doFade?this.blocker.fadeOut(this.options.fadeDuration,function(){$(this).remove()}):this.blocker.remove()},show:function(){this.$elm.trigger($.kmodal.BEFORE_OPEN,[this._ctx()]),this.options.showClose&&(this.closeButton=$('<a href="#close-modal" rel="modal:close" class="close-modal '+this.options.closeClass+'">'+this.options.closeText+"</a>"),this.$elm.append(this.closeButton)),this.$elm.addClass(this.options.modalClass+" current"),this.center(),this.options.doFade?this.$elm.fadeIn(this.options.fadeDuration):this.$elm.show(),this.$elm.trigger($.kmodal.OPEN,[this._ctx()])},hide:function(){this.$elm.trigger($.kmodal.BEFORE_CLOSE,[this._ctx()]),this.closeButton&&this.closeButton.remove(),this.$elm.removeClass("current"),this.options.doFade?this.$elm.fadeOut(this.options.fadeDuration):this.$elm.hide(),this.$elm.trigger($.kmodal.CLOSE,[this._ctx()])},showSpinner:function(){this.options.showSpinner&&(this.spinner=this.spinner||$('<div class="'+this.options.modalClass+'-spinner"></div>').append(this.options.spinnerHtml),this.$body.append(this.spinner),this.spinner.show())},hideSpinner:function(){this.spinner&&this.spinner.remove()},center:function(){this.$elm.css({position:"fixed",top:"50%",left:"50%",marginTop:-(this.$elm.outerHeight()/2),marginLeft:-(this.$elm.outerWidth()/2),zIndex:this.options.zIndex+1})},_ctx:function(){return{elm:this.$elm,blocker:this.blocker,options:this.options}}},$.kmodal.prototype.resize=$.kmodal.prototype.center,$.kmodal.close=function(event){if(current){event&&event.preventDefault(),current.close();var that=current.$elm;return current=null,that}},$.kmodal.resize=function(){current&&current.resize()},$.kmodal.isActive=function(){return current?!0:!1},$.kmodal.defaults={overlay:"#000",opacity:.75,zIndex:1,escapeClose:!0,clickClose:!0,closeText:"Close",closeClass:"",modalClass:"kraken-modal",spinnerHtml:null,showSpinner:!0,showClose:!0,fadeDuration:null,fadeDelay:1},$.kmodal.BEFORE_BLOCK="modal:before-block",$.kmodal.BLOCK="modal:block",$.kmodal.BEFORE_OPEN="modal:before-open",$.kmodal.OPEN="modal:open",$.kmodal.BEFORE_CLOSE="modal:before-close",$.kmodal.CLOSE="modal:close",$.kmodal.AJAX_SEND="modal:ajax:send",$.kmodal.AJAX_SUCCESS="modal:ajax:success",$.kmodal.AJAX_FAIL="modal:ajax:fail",$.kmodal.AJAX_COMPLETE="modal:ajax:complete",$.fn.kmodal=function(options){return 1===this.length&&(current=new $.kmodal(this,options)),this},$(document).on("click.modal",'a[rel="modal:close"]',$.kmodal.close),$(document).on("click.modal",'a[rel="modal:open"]',function(event){event.preventDefault(),$(this).kmodal()})}(jQuery),jQuery(document).ready(function($){$(".krakenWhatsThis").tipsy({fade:!0,gravity:"w"});var data={action:"kraken_request"},errorTpl='<div class="krakenErrorWrap"><a class="krakenError">Failed! Hover here</a></div>',$btnApplyBulkAction=$("#doaction"),$btnApplyBulkAction2=$("#doaction2"),$topBulkActionDropdown=$(".tablenav.top .bulkactions select[name='action']"),$bottomBulkActionDropdown=$(".tablenav.bottom .bulkactions select[name='action2']"),requestSuccess=function(data,textStatus,jqXHR){var $button=$(this),$parent=$(this).parent(),$cell=$(this).closest("td");if(data.success&&"undefined"==typeof data.error){$button.text("Image optimized");var originalSize=(data.type,data.kraked_size,data.original_size),$originalSizeColumn=(data.savings_percent,$(this).parent().prev("td.original_size"));$parent.fadeOut("fast",function(){$cell.find(".noSavings, .krakenErrorWrap").remove(),$(this).replaceWith(data.html),$originalSizeColumn.html(originalSize),$parent.remove()})}else if(data.error){var $error=$(errorTpl).attr("title",data.error);$parent.closest("td").find(".krakenErrorWrap").remove(),$parent.after($error),$error.tipsy({fade:!0,gravity:"e"}),$button.text("Retry request").removeAttr("disabled").css({opacity:1})}},requestFail=function(jqXHR,textStatus,errorThrown){$(this).removeAttr("disabled")},requestComplete=function(jqXHR,textStatus,errorThrown){$(this).removeAttr("disabled"),$(this).parent().find(".krakenSpinner").css("display","none")},opts='<option value="kraken-bulk-lossy">Krak \'em all</option>';$topBulkActionDropdown.find("option:last-child").before(opts),$bottomBulkActionDropdown.find("option:last-child").before(opts);var getBulkImageData=function(){var $rows=$("tr[id^='post-']"),$row=null,postId=0,$krakBtn=null,btnData={},originalSize="",rv=[];return $rows.each(function(){$row=$(this),postId=this.id.replace(/^\D+/g,""),$row.find("input[type='checkbox'][value='"+postId+"']:checked").length&&($krakBtn=$row.find(".kraken_req"),$krakBtn.length&&(btnData=$krakBtn.data(),originalSize=$.trim($row.find("td.original_size").text()),btnData.originalSize=originalSize,rv.push(btnData)))}),rv},renderBulkImageSummary=function(bulkImageData){var modalOptions={zIndex:4,escapeClose:!0,clickClose:!1,closeText:"close",showClose:!1},setting=kraken_settings.api_lossy,nImages=bulkImageData.length,header='<p class="krakenBulkHeader">Kraken Bulk Image Optimization</p>',krakEmAll='<button class="kraken_req_bulk">Krak \'em all</button>',typeRadios='<span class="radiosWrap"><span class="kraken-bulk-choose-type">Choose:</span><input type="radio" id="kraken-bulk-type-lossy" value="Lossy" name="kraken-bulk-type"/><label for="kraken-bulk-type-lossy">Lossy</label>&nbsp;<input type="radio" id="kraken-bulk-type-lossless" value="Lossless" name="kraken-bulk-type"/><label for="kraken-bulk-type-lossless">Lossless</label></span>',$modal=$('<div id="kraken-bulk-modal" class="kraken-modal"></div>').html(header).append(typeRadios).append('<br /><small class="kraken-bulk-small">The following <strong>'+nImages+'</strong> images will be optimized by Kraken.io using the <strong class="bulkSetting">'+setting+"</strong> setting:</small><br />").appendTo("body").kmodal(modalOptions).bind($.kmodal.BEFORE_CLOSE,function(event,modal){}).bind($.kmodal.OPEN,function(event,modal){}).bind($.kmodal.CLOSE,function(event,modal){$("#kraken-bulk-modal").remove()}).css({top:"10px",marginTop:"40px"});"lossy"===setting?$("#kraken-bulk-type-lossy").attr("checked",!0):$("#kraken-bulk-type-lossless").attr("checked",!0),$bulkSettingSpan=$(".bulkSetting"),$("input[name='kraken-bulk-type']").change(function(){var text="kraken-bulk-type-lossy"===this.id?"lossy":"lossless";$bulkSettingSpan.text(text)}),$(".jquery-modal.blocker").click(function(e){return!1}),$("#menu-media ul.wp-submenu").css({"z-index":1});var $table=$('<table id="kraken-bulk"></table>'),$headerRow=$('<tr class="kraken-bulk-header"><td>File</td><td style="width:120px">Original Size</td><td style="width:120px">Kraked Size</td><td style="width:120px">Savings</td><td style="width:120px">% Savings</td></tr>');$table.append($headerRow),$.each(bulkImageData,function(index,element){$table.append('<tr class="kraken-item-row" data-krakenbulkid="'+element.id+'"><td class="kraken-filename">'+element.filename+'</td><td class="kraken-originalsize">'+element.originalSize+'</td><td class="kraken-krakedsize"><span class="krakenBulkSpinner hidden"></span></td><td class="kraken-savings"></td><td class="kraken-savingsPercent"></td></tr>')}),$modal.append($table).append(krakEmAll).append('<span class="close-kraken-bulk">Close Window</span>'),$(".close-kraken-bulk").click(function(){$.kmodal.close()}),nImages||$(".kraken_req_bulk").attr("disabled",!0).css({opacity:.5})},bulkAction=function(bulkImageData){$bulkTable=$("#kraken-bulk");var jqxhr=null,q=async.queue(function(task,callback){var id=task.id,$row=(task.filename,$bulkTable.find("tr[data-krakenbulkid='"+id+"']")),$krakedSizeColumn=$row.find(".kraken-krakedsize"),$spinner=$krakedSizeColumn.find(".krakenBulkSpinner").css({display:"inline-block"}),$savingsPercentColumn=$row.find(".kraken-savingsPercent"),$savingsBytesColumn=$row.find(".kraken-savings");jqxhr=$.ajax({url:ajax_object.ajax_url,data:{action:"kraken_request",id:id,type:$("input[name='kraken-bulk-type']:checked").val().toLowerCase()},type:"post",dataType:"json",timeout:36e4}).done(function(data,textStatus,jqXHR){if(data.success&&"undefined"==typeof data.error){var type=data.type,originalSize=data.original_size,krakedSize=data.kraked_size,savingsPercent=data.savings_percent,savingsBytes=data.saved_bytes;$krakedSizeColumn.text(krakedSize),$savingsPercentColumn.text(savingsPercent),$savingsBytesColumn.text(savingsBytes);var $button=$("button[id='krakenid-"+id+"']"),$parent=$button.parent(),$cell=$button.closest("td"),$originalSizeColumn=$button.parent().prev("td.original_size");$parent.fadeOut("fast",function(){$cell.find(".noSavings, .krakenErrorWrap").remove(),krakedData="<strong>"+krakedSize+"</strong><br /><small>Type:&nbsp;"+type+"</small><br /><small>Savings: "+savingsPercent+"</small>","undefined"!=typeof data.thumbs_data&&(krakedData+="<br /><small>"+data.thumbs_data.length+" thumbs optimized</small>"),$(this).replaceWith(krakedData),$originalSizeColumn.html(originalSize),$parent.remove()})}else data.error&&"This image can not be optimized any further"===data.error&&$krakedSizeColumn.text("No savings found.")}).fail(function(){}).always(function(){$spinner.css({display:"none"}),callback()})},kraken_settings.bulk_async_limit);q.drain=function(){$(".kraken_req_bulk").removeAttr("disabled").css({opacity:1}).text("Done").unbind("click").click(function(){$.kmodal.close()})},q.push(bulkImageData,function(err){})};$btnApplyBulkAction.add($btnApplyBulkAction2).click(function(e){if("kraken-bulk-lossy"===$(this).prev("select").val()){e.preventDefault();var bulkImageData=getBulkImageData();renderBulkImageSummary(bulkImageData),$(".kraken_req_bulk").click(function(e){e.preventDefault(),$(this).attr("disabled",!0).css({opacity:.5}),bulkAction(bulkImageData)})}}),$("body").on("click","small.krakenReset",function(e){e.preventDefault();var $resetButton=$(this),resetData={action:"kraken_reset"};resetData.id=$(this).data("id");var $spinner=$('<span class="resetSpinner"></span>');$resetButton.after($spinner);$.ajax({url:ajax_object.ajax_url,data:resetData,type:"post",dataType:"json",timeout:36e4}).done(function(data,textStatus,jqXHR){"undefined"!==data.success&&$resetButton.closest(".kraked_size.column-kraked_size").hide().html(data.html).fadeIn().prev(".original_size.column-original_size").html(data.original_size)})}),$("body").on("click",".kraken-reset-all",function(e){e.preventDefault();var reset=confirm("This will immediately remove all Kraken metadata associated with your images. \n\nAre you sure you want to do this?");if(reset){var $resetButton=$(this);$resetButton.text("Resetting images, pleaes wait...").attr("disabled",!0);
    2 
    3 var resetData={action:"kraken_reset_all"},$spinner=$('<span class="resetSpinner"></span>');$resetButton.after($spinner);{$.ajax({url:ajax_object.ajax_url,data:resetData,type:"post",dataType:"json",timeout:36e4}).done(function(data,textStatus,jqXHR){$spinner.remove(),$resetButton.text("Your images have been reset.").removeAttr("disabled").removeClass("enabled")})}}}),$(".krakenAdvancedSettings h3").on("click",function(){var $rows=$(".kraken-advanced-settings"),$plusMinus=$(".kraken-plus-minus");$rows.is(":visible")?($rows.hide(),$plusMinus.removeClass("dashicons-arrow-down").addClass("dashicons-arrow-right")):($rows.show(),$plusMinus.removeClass("dashicons-arrow-right").addClass("dashicons-arrow-down"))}),$("body").on("click",".kraken_req",function(e){e.preventDefault();var $button=$(this),$parent=$(this).parent();data.id=$(this).data("id"),$button.text("Optimizing image...").attr("disabled",!0).css({opacity:.5}),$parent.find(".krakenSpinner").css("display","inline");$.ajax({url:ajax_object.ajax_url,data:data,type:"post",dataType:"json",timeout:36e4,context:$button}).done(requestSuccess).fail(requestFail).always(requestComplete)})});
     1!function(){function only_once(fn){var called=!1;return function(){if(called)throw new Error("Callback was already called.");called=!0,fn.apply(root,arguments)}}var root,previous_async,async={};root=this,null!=root&&(previous_async=root.async),async.noConflict=function(){return root.async=previous_async,async};var _each=function(arr,iterator){if(arr.forEach)return arr.forEach(iterator);for(var i=0;i<arr.length;i+=1)iterator(arr[i],i,arr)},_map=function(arr,iterator){if(arr.map)return arr.map(iterator);var results=[];return _each(arr,function(x,i,a){results.push(iterator(x,i,a))}),results},_reduce=function(arr,iterator,memo){return arr.reduce?arr.reduce(iterator,memo):(_each(arr,function(x,i,a){memo=iterator(memo,x,i,a)}),memo)},_keys=function(obj){if(Object.keys)return Object.keys(obj);var keys=[];for(var k in obj)obj.hasOwnProperty(k)&&keys.push(k);return keys};"undefined"!=typeof process&&process.nextTick?(async.nextTick=process.nextTick,"undefined"!=typeof setImmediate?async.setImmediate=function(fn){setImmediate(fn)}:async.setImmediate=async.nextTick):"function"==typeof setImmediate?(async.nextTick=function(fn){setImmediate(fn)},async.setImmediate=async.nextTick):(async.nextTick=function(fn){setTimeout(fn,0)},async.setImmediate=async.nextTick),async.each=function(arr,iterator,callback){if(callback=callback||function(){},!arr.length)return callback();var completed=0;_each(arr,function(x){iterator(x,only_once(function(err){err?(callback(err),callback=function(){}):(completed+=1,completed>=arr.length&&callback(null))}))})},async.forEach=async.each,async.eachSeries=function(arr,iterator,callback){if(callback=callback||function(){},!arr.length)return callback();var completed=0,iterate=function(){iterator(arr[completed],function(err){err?(callback(err),callback=function(){}):(completed+=1,completed>=arr.length?callback(null):iterate())})};iterate()},async.forEachSeries=async.eachSeries,async.eachLimit=function(arr,limit,iterator,callback){var fn=_eachLimit(limit);fn.apply(null,[arr,iterator,callback])},async.forEachLimit=async.eachLimit;var _eachLimit=function(limit){return function(arr,iterator,callback){if(callback=callback||function(){},!arr.length||0>=limit)return callback();var completed=0,started=0,running=0;!function replenish(){if(completed>=arr.length)return callback();for(;limit>running&&started<arr.length;)started+=1,running+=1,iterator(arr[started-1],function(err){err?(callback(err),callback=function(){}):(completed+=1,running-=1,completed>=arr.length?callback():replenish())})}()}},doParallel=function(fn){return function(){var args=Array.prototype.slice.call(arguments);return fn.apply(null,[async.each].concat(args))}},doParallelLimit=function(limit,fn){return function(){var args=Array.prototype.slice.call(arguments);return fn.apply(null,[_eachLimit(limit)].concat(args))}},doSeries=function(fn){return function(){var args=Array.prototype.slice.call(arguments);return fn.apply(null,[async.eachSeries].concat(args))}},_asyncMap=function(eachfn,arr,iterator,callback){var results=[];arr=_map(arr,function(x,i){return{index:i,value:x}}),eachfn(arr,function(x,callback){iterator(x.value,function(err,v){results[x.index]=v,callback(err)})},function(err){callback(err,results)})};async.map=doParallel(_asyncMap),async.mapSeries=doSeries(_asyncMap),async.mapLimit=function(arr,limit,iterator,callback){return _mapLimit(limit)(arr,iterator,callback)};var _mapLimit=function(limit){return doParallelLimit(limit,_asyncMap)};async.reduce=function(arr,memo,iterator,callback){async.eachSeries(arr,function(x,callback){iterator(memo,x,function(err,v){memo=v,callback(err)})},function(err){callback(err,memo)})},async.inject=async.reduce,async.foldl=async.reduce,async.reduceRight=function(arr,memo,iterator,callback){var reversed=_map(arr,function(x){return x}).reverse();async.reduce(reversed,memo,iterator,callback)},async.foldr=async.reduceRight;var _filter=function(eachfn,arr,iterator,callback){var results=[];arr=_map(arr,function(x,i){return{index:i,value:x}}),eachfn(arr,function(x,callback){iterator(x.value,function(v){v&&results.push(x),callback()})},function(err){callback(_map(results.sort(function(a,b){return a.index-b.index}),function(x){return x.value}))})};async.filter=doParallel(_filter),async.filterSeries=doSeries(_filter),async.select=async.filter,async.selectSeries=async.filterSeries;var _reject=function(eachfn,arr,iterator,callback){var results=[];arr=_map(arr,function(x,i){return{index:i,value:x}}),eachfn(arr,function(x,callback){iterator(x.value,function(v){v||results.push(x),callback()})},function(err){callback(_map(results.sort(function(a,b){return a.index-b.index}),function(x){return x.value}))})};async.reject=doParallel(_reject),async.rejectSeries=doSeries(_reject);var _detect=function(eachfn,arr,iterator,main_callback){eachfn(arr,function(x,callback){iterator(x,function(result){result?(main_callback(x),main_callback=function(){}):callback()})},function(err){main_callback()})};async.detect=doParallel(_detect),async.detectSeries=doSeries(_detect),async.some=function(arr,iterator,main_callback){async.each(arr,function(x,callback){iterator(x,function(v){v&&(main_callback(!0),main_callback=function(){}),callback()})},function(err){main_callback(!1)})},async.any=async.some,async.every=function(arr,iterator,main_callback){async.each(arr,function(x,callback){iterator(x,function(v){v||(main_callback(!1),main_callback=function(){}),callback()})},function(err){main_callback(!0)})},async.all=async.every,async.sortBy=function(arr,iterator,callback){async.map(arr,function(x,callback){iterator(x,function(err,criteria){err?callback(err):callback(null,{value:x,criteria:criteria})})},function(err,results){if(err)return callback(err);var fn=function(left,right){var a=left.criteria,b=right.criteria;return b>a?-1:a>b?1:0};callback(null,_map(results.sort(fn),function(x){return x.value}))})},async.auto=function(tasks,callback){callback=callback||function(){};var keys=_keys(tasks);if(!keys.length)return callback(null);var results={},listeners=[],addListener=function(fn){listeners.unshift(fn)},removeListener=function(fn){for(var i=0;i<listeners.length;i+=1)if(listeners[i]===fn)return void listeners.splice(i,1)},taskComplete=function(){_each(listeners.slice(0),function(fn){fn()})};addListener(function(){_keys(results).length===keys.length&&(callback(null,results),callback=function(){})}),_each(keys,function(k){var task=tasks[k]instanceof Function?[tasks[k]]:tasks[k],taskCallback=function(err){var args=Array.prototype.slice.call(arguments,1);if(args.length<=1&&(args=args[0]),err){var safeResults={};_each(_keys(results),function(rkey){safeResults[rkey]=results[rkey]}),safeResults[k]=args,callback(err,safeResults),callback=function(){}}else results[k]=args,async.setImmediate(taskComplete)},requires=task.slice(0,Math.abs(task.length-1))||[],ready=function(){return _reduce(requires,function(a,x){return a&&results.hasOwnProperty(x)},!0)&&!results.hasOwnProperty(k)};if(ready())task[task.length-1](taskCallback,results);else{var listener=function(){ready()&&(removeListener(listener),task[task.length-1](taskCallback,results))};addListener(listener)}})},async.waterfall=function(tasks,callback){if(callback=callback||function(){},tasks.constructor!==Array){var err=new Error("First argument to waterfall must be an array of functions");return callback(err)}if(!tasks.length)return callback();var wrapIterator=function(iterator){return function(err){if(err)callback.apply(null,arguments),callback=function(){};else{var args=Array.prototype.slice.call(arguments,1),next=iterator.next();args.push(next?wrapIterator(next):callback),async.setImmediate(function(){iterator.apply(null,args)})}}};wrapIterator(async.iterator(tasks))()};var _parallel=function(eachfn,tasks,callback){if(callback=callback||function(){},tasks.constructor===Array)eachfn.map(tasks,function(fn,callback){fn&&fn(function(err){var args=Array.prototype.slice.call(arguments,1);args.length<=1&&(args=args[0]),callback.call(null,err,args)})},callback);else{var results={};eachfn.each(_keys(tasks),function(k,callback){tasks[k](function(err){var args=Array.prototype.slice.call(arguments,1);args.length<=1&&(args=args[0]),results[k]=args,callback(err)})},function(err){callback(err,results)})}};async.parallel=function(tasks,callback){_parallel({map:async.map,each:async.each},tasks,callback)},async.parallelLimit=function(tasks,limit,callback){_parallel({map:_mapLimit(limit),each:_eachLimit(limit)},tasks,callback)},async.series=function(tasks,callback){if(callback=callback||function(){},tasks.constructor===Array)async.mapSeries(tasks,function(fn,callback){fn&&fn(function(err){var args=Array.prototype.slice.call(arguments,1);args.length<=1&&(args=args[0]),callback.call(null,err,args)})},callback);else{var results={};async.eachSeries(_keys(tasks),function(k,callback){tasks[k](function(err){var args=Array.prototype.slice.call(arguments,1);args.length<=1&&(args=args[0]),results[k]=args,callback(err)})},function(err){callback(err,results)})}},async.iterator=function(tasks){var makeCallback=function(index){var fn=function(){return tasks.length&&tasks[index].apply(null,arguments),fn.next()};return fn.next=function(){return index<tasks.length-1?makeCallback(index+1):null},fn};return makeCallback(0)},async.apply=function(fn){var args=Array.prototype.slice.call(arguments,1);return function(){return fn.apply(null,args.concat(Array.prototype.slice.call(arguments)))}};var _concat=function(eachfn,arr,fn,callback){var r=[];eachfn(arr,function(x,cb){fn(x,function(err,y){r=r.concat(y||[]),cb(err)})},function(err){callback(err,r)})};async.concat=doParallel(_concat),async.concatSeries=doSeries(_concat),async.whilst=function(test,iterator,callback){test()?iterator(function(err){return err?callback(err):void async.whilst(test,iterator,callback)}):callback()},async.doWhilst=function(iterator,test,callback){iterator(function(err){return err?callback(err):void(test()?async.doWhilst(iterator,test,callback):callback())})},async.until=function(test,iterator,callback){test()?callback():iterator(function(err){return err?callback(err):void async.until(test,iterator,callback)})},async.doUntil=function(iterator,test,callback){iterator(function(err){return err?callback(err):void(test()?callback():async.doUntil(iterator,test,callback))})},async.queue=function(worker,concurrency){function _insert(q,data,pos,callback){data.constructor!==Array&&(data=[data]),_each(data,function(task){var item={data:task,callback:"function"==typeof callback?callback:null};pos?q.tasks.unshift(item):q.tasks.push(item),q.saturated&&q.tasks.length===concurrency&&q.saturated(),async.setImmediate(q.process)})}void 0===concurrency&&(concurrency=1);var workers=0,q={tasks:[],concurrency:concurrency,saturated:null,empty:null,drain:null,push:function(data,callback){_insert(q,data,!1,callback)},unshift:function(data,callback){_insert(q,data,!0,callback)},process:function(){if(workers<q.concurrency&&q.tasks.length){var task=q.tasks.shift();q.empty&&0===q.tasks.length&&q.empty(),workers+=1;var next=function(){workers-=1,task.callback&&task.callback.apply(task,arguments),q.drain&&q.tasks.length+workers===0&&q.drain(),q.process()},cb=only_once(next);worker(task.data,cb)}},length:function(){return q.tasks.length},running:function(){return workers}};return q},async.cargo=function(worker,payload){var working=!1,tasks=[],cargo={tasks:tasks,payload:payload,saturated:null,empty:null,drain:null,push:function(data,callback){data.constructor!==Array&&(data=[data]),_each(data,function(task){tasks.push({data:task,callback:"function"==typeof callback?callback:null}),cargo.saturated&&tasks.length===payload&&cargo.saturated()}),async.setImmediate(cargo.process)},process:function process(){if(!working){if(0===tasks.length)return void(cargo.drain&&cargo.drain());var ts="number"==typeof payload?tasks.splice(0,payload):tasks.splice(0),ds=_map(ts,function(task){return task.data});cargo.empty&&cargo.empty(),working=!0,worker(ds,function(){working=!1;var args=arguments;_each(ts,function(data){data.callback&&data.callback.apply(null,args)}),process()})}},length:function(){return tasks.length},running:function(){return working}};return cargo};var _console_fn=function(name){return function(fn){var args=Array.prototype.slice.call(arguments,1);fn.apply(null,args.concat([function(err){var args=Array.prototype.slice.call(arguments,1);"undefined"!=typeof console&&(err?console.error&&console.error(err):console[name]&&_each(args,function(x){console[name](x)}))}]))}};async.log=_console_fn("log"),async.dir=_console_fn("dir"),async.memoize=function(fn,hasher){var memo={},queues={};hasher=hasher||function(x){return x};var memoized=function(){var args=Array.prototype.slice.call(arguments),callback=args.pop(),key=hasher.apply(null,args);key in memo?callback.apply(null,memo[key]):key in queues?queues[key].push(callback):(queues[key]=[callback],fn.apply(null,args.concat([function(){memo[key]=arguments;var q=queues[key];delete queues[key];for(var i=0,l=q.length;l>i;i++)q[i].apply(null,arguments)}])))};return memoized.memo=memo,memoized.unmemoized=fn,memoized},async.unmemoize=function(fn){return function(){return(fn.unmemoized||fn).apply(null,arguments)}},async.times=function(count,iterator,callback){for(var counter=[],i=0;count>i;i++)counter.push(i);return async.map(counter,iterator,callback)},async.timesSeries=function(count,iterator,callback){for(var counter=[],i=0;count>i;i++)counter.push(i);return async.mapSeries(counter,iterator,callback)},async.compose=function(){var fns=Array.prototype.reverse.call(arguments);return function(){var that=this,args=Array.prototype.slice.call(arguments),callback=args.pop();async.reduce(fns,args,function(newargs,fn,cb){fn.apply(that,newargs.concat([function(){var err=arguments[0],nextargs=Array.prototype.slice.call(arguments,1);cb(err,nextargs)}]))},function(err,results){callback.apply(that,[err].concat(results))})}};var _applyEach=function(eachfn,fns){var go=function(){var that=this,args=Array.prototype.slice.call(arguments),callback=args.pop();return eachfn(fns,function(fn,cb){fn.apply(that,args.concat([cb]))},callback)};if(arguments.length>2){var args=Array.prototype.slice.call(arguments,2);return go.apply(this,args)}return go};async.applyEach=doParallel(_applyEach),async.applyEachSeries=doSeries(_applyEach),async.forever=function(fn,callback){function next(err){if(err){if(callback)return callback(err);throw err}fn(next)}next()},"undefined"!=typeof define&&define.amd?define([],function(){return async}):"undefined"!=typeof module&&module.exports?module.exports=async:root.async=async}(),function($){function maybeCall(thing,ctx){return"function"==typeof thing?thing.call(ctx):thing}function isElementInDOM(ele){for(;ele=ele.parentNode;)if(ele==document)return!0;return!1}function Tipsy(element,options){this.$element=$(element),this.options=options,this.enabled=!0,this.fixTitle()}Tipsy.prototype={show:function(){var title=this.getTitle();if(title&&this.enabled){var $tip=this.tip();$tip.find(".tipsy-inner")[this.options.html?"html":"text"](title),$tip[0].className="tipsy",$tip.remove().css({top:0,left:0,visibility:"hidden",display:"block"}).prependTo(document.body);var tp,pos=$.extend({},this.$element.offset(),{width:this.$element[0].offsetWidth,height:this.$element[0].offsetHeight}),actualWidth=$tip[0].offsetWidth,actualHeight=$tip[0].offsetHeight,gravity=maybeCall(this.options.gravity,this.$element[0]);switch(gravity.charAt(0)){case"n":tp={top:pos.top+pos.height+this.options.offset,left:pos.left+pos.width/2-actualWidth/2};break;case"s":tp={top:pos.top-actualHeight-this.options.offset,left:pos.left+pos.width/2-actualWidth/2};break;case"e":tp={top:pos.top+pos.height/2-actualHeight/2,left:pos.left-actualWidth-this.options.offset};break;case"w":tp={top:pos.top+pos.height/2-actualHeight/2,left:pos.left+pos.width+this.options.offset}}2==gravity.length&&("w"==gravity.charAt(1)?tp.left=pos.left+pos.width/2-15:tp.left=pos.left+pos.width/2-actualWidth+15),$tip.css(tp).addClass("tipsy-"+gravity),$tip.find(".tipsy-arrow")[0].className="tipsy-arrow tipsy-arrow-"+gravity.charAt(0),this.options.className&&$tip.addClass(maybeCall(this.options.className,this.$element[0])),this.options.fade?$tip.stop().css({opacity:0,display:"block",visibility:"visible"}).animate({opacity:this.options.opacity}):$tip.css({visibility:"visible",opacity:this.options.opacity})}},hide:function(){this.options.fade?this.tip().stop().fadeOut(function(){$(this).remove()}):this.tip().remove()},fixTitle:function(){var $e=this.$element;($e.attr("title")||"string"!=typeof $e.attr("original-title"))&&$e.attr("original-title",$e.attr("title")||"").removeAttr("title")},getTitle:function(){var title,$e=this.$element,o=this.options;this.fixTitle();var title,o=this.options;return"string"==typeof o.title?title=$e.attr("title"==o.title?"original-title":o.title):"function"==typeof o.title&&(title=o.title.call($e[0])),title=(""+title).replace(/(^\s*|\s*$)/,""),title||o.fallback},tip:function(){return this.$tip||(this.$tip=$('<div class="tipsy"></div>').html('<div class="tipsy-arrow"></div><div class="tipsy-inner"></div>'),this.$tip.data("tipsy-pointee",this.$element[0])),this.$tip},validate:function(){this.$element[0].parentNode||(this.hide(),this.$element=null,this.options=null)},enable:function(){this.enabled=!0},disable:function(){this.enabled=!1},toggleEnabled:function(){this.enabled=!this.enabled}},$.fn.tipsy=function(options){function get(ele){var tipsy=$.data(ele,"tipsy");return tipsy||(tipsy=new Tipsy(ele,$.fn.tipsy.elementOptions(ele,options)),$.data(ele,"tipsy",tipsy)),tipsy}function enter(){var tipsy=get(this);tipsy.hoverState="in",0==options.delayIn?tipsy.show():(tipsy.fixTitle(),setTimeout(function(){"in"==tipsy.hoverState&&tipsy.show()},options.delayIn))}function leave(){var tipsy=get(this);tipsy.hoverState="out",0==options.delayOut?tipsy.hide():setTimeout(function(){"out"==tipsy.hoverState&&tipsy.hide()},options.delayOut)}if(options===!0)return this.data("tipsy");if("string"==typeof options){var tipsy=this.data("tipsy");return tipsy&&tipsy[options](),this}if(options=$.extend({},$.fn.tipsy.defaults,options),options.live||this.each(function(){get(this)}),"manual"!=options.trigger){var binder=options.live?"live":"bind",eventIn="hover"==options.trigger?"mouseenter":"focus",eventOut="hover"==options.trigger?"mouseleave":"blur";this[binder](eventIn,enter)[binder](eventOut,leave)}return this},$.fn.tipsy.defaults={className:null,delayIn:0,delayOut:0,fade:!1,fallback:"",gravity:"n",html:!1,live:!1,offset:0,opacity:.8,title:"title",trigger:"hover"},$.fn.tipsy.revalidate=function(){$(".tipsy").each(function(){var pointee=$.data(this,"tipsy-pointee");pointee&&isElementInDOM(pointee)||$(this).remove()})},$.fn.tipsy.elementOptions=function(ele,options){return $.metadata?$.extend({},options,$(ele).metadata()):options},$.fn.tipsy.autoNS=function(){return $(this).offset().top>$(document).scrollTop()+$(window).height()/2?"s":"n"},$.fn.tipsy.autoWE=function(){return $(this).offset().left>$(document).scrollLeft()+$(window).width()/2?"e":"w"},$.fn.tipsy.autoBounds=function(margin,prefer){return function(){var dir={ns:prefer[0],ew:prefer.length>1?prefer[1]:!1},boundTop=$(document).scrollTop()+margin,boundLeft=$(document).scrollLeft()+margin,$this=$(this);return $this.offset().top<boundTop&&(dir.ns="n"),$this.offset().left<boundLeft&&(dir.ew="w"),$(window).width()+$(document).scrollLeft()-$this.offset().left<margin&&(dir.ew="e"),$(window).height()+$(document).scrollTop()-$this.offset().top<margin&&(dir.ns="s"),dir.ns+(dir.ew?dir.ew:"")}}}(jQuery),function($){var current=null;$.kmodal=function(el,options){$.kmodal.close();var remove,target;if(this.$body=$("body"),this.options=$.extend({},$.kmodal.defaults,options),this.options.doFade=!isNaN(parseInt(this.options.fadeDuration,10)),el.is("a"))if(target=el.attr("href"),/^#/.test(target)){if(this.$elm=$(target),1!==this.$elm.length)return null;this.open()}else this.$elm=$("<div>"),this.$body.append(this.$elm),remove=function(event,modal){modal.elm.remove()},this.showSpinner(),el.trigger($.kmodal.AJAX_SEND),$.get(target).done(function(html){current&&(el.trigger($.kmodal.AJAX_SUCCESS),current.$elm.empty().append(html).on($.kmodal.CLOSE,remove),current.hideSpinner(),current.open(),el.trigger($.kmodal.AJAX_COMPLETE))}).fail(function(){el.trigger($.kmodal.AJAX_FAIL),current.hideSpinner(),el.trigger($.kmodal.AJAX_COMPLETE)});else this.$elm=el,this.$body.append(this.$elm),this.open()},$.kmodal.prototype={constructor:$.kmodal,open:function(){var m=this;this.options.doFade?(this.block(),setTimeout(function(){m.show()},this.options.fadeDuration*this.options.fadeDelay)):(this.block(),this.show()),this.options.escapeClose&&$(document).on("keydown.modal",function(event){27==event.which&&$.kmodal.close()}),this.options.clickClose&&this.blocker.click($.kmodal.close)},close:function(){this.unblock(),this.hide(),$(document).off("keydown.modal")},block:function(){var initialOpacity=this.options.doFade?0:this.options.opacity;this.$elm.trigger($.kmodal.BEFORE_BLOCK,[this._ctx()]),this.blocker=$('<div class="jquery-modal blocker"></div>').css({top:0,right:0,bottom:0,left:0,width:"100%",height:"100%",position:"fixed",zIndex:this.options.zIndex,background:this.options.overlay,opacity:initialOpacity}),this.$body.append(this.blocker),this.options.doFade&&this.blocker.animate({opacity:this.options.opacity},this.options.fadeDuration),this.$elm.trigger($.kmodal.BLOCK,[this._ctx()])},unblock:function(){this.options.doFade?this.blocker.fadeOut(this.options.fadeDuration,function(){$(this).remove()}):this.blocker.remove()},show:function(){this.$elm.trigger($.kmodal.BEFORE_OPEN,[this._ctx()]),this.options.showClose&&(this.closeButton=$('<a href="#close-modal" rel="modal:close" class="close-modal '+this.options.closeClass+'">'+this.options.closeText+"</a>"),this.$elm.append(this.closeButton)),this.$elm.addClass(this.options.modalClass+" current"),this.center(),this.options.doFade?this.$elm.fadeIn(this.options.fadeDuration):this.$elm.show(),this.$elm.trigger($.kmodal.OPEN,[this._ctx()])},hide:function(){this.$elm.trigger($.kmodal.BEFORE_CLOSE,[this._ctx()]),this.closeButton&&this.closeButton.remove(),this.$elm.removeClass("current"),this.options.doFade?this.$elm.fadeOut(this.options.fadeDuration):this.$elm.hide(),this.$elm.trigger($.kmodal.CLOSE,[this._ctx()])},showSpinner:function(){this.options.showSpinner&&(this.spinner=this.spinner||$('<div class="'+this.options.modalClass+'-spinner"></div>').append(this.options.spinnerHtml),this.$body.append(this.spinner),this.spinner.show())},hideSpinner:function(){this.spinner&&this.spinner.remove()},center:function(){this.$elm.css({position:"fixed",top:"50%",left:"50%",marginTop:-(this.$elm.outerHeight()/2),marginLeft:-(this.$elm.outerWidth()/2),zIndex:this.options.zIndex+1})},_ctx:function(){return{elm:this.$elm,blocker:this.blocker,options:this.options}}},$.kmodal.prototype.resize=$.kmodal.prototype.center,$.kmodal.close=function(event){if(current){event&&event.preventDefault(),current.close();var that=current.$elm;return current=null,that}},$.kmodal.resize=function(){current&&current.resize()},$.kmodal.isActive=function(){return current?!0:!1},$.kmodal.defaults={overlay:"#000",opacity:.75,zIndex:1,escapeClose:!0,clickClose:!0,closeText:"Close",closeClass:"",modalClass:"kraken-modal",spinnerHtml:null,showSpinner:!0,showClose:!0,fadeDuration:null,fadeDelay:1},$.kmodal.BEFORE_BLOCK="modal:before-block",$.kmodal.BLOCK="modal:block",$.kmodal.BEFORE_OPEN="modal:before-open",$.kmodal.OPEN="modal:open",$.kmodal.BEFORE_CLOSE="modal:before-close",$.kmodal.CLOSE="modal:close",$.kmodal.AJAX_SEND="modal:ajax:send",$.kmodal.AJAX_SUCCESS="modal:ajax:success",$.kmodal.AJAX_FAIL="modal:ajax:fail",$.kmodal.AJAX_COMPLETE="modal:ajax:complete",$.fn.kmodal=function(options){return 1===this.length&&(current=new $.kmodal(this,options)),this},$(document).on("click.modal",'a[rel="modal:close"]',$.kmodal.close),$(document).on("click.modal",'a[rel="modal:open"]',function(event){event.preventDefault(),$(this).kmodal()})}(jQuery),jQuery(document).ready(function($){var tipsySettings={gravity:"e",html:!0,trigger:"manual",className:function(){return"tipsy-"+$(this).data("id")},title:function(){return activeId=$(this).data("id"),$(this).attr("original-title")}};$(".krakenWhatsThis").tipsy({fade:!0,gravity:"w"}),$(".krakenError").tipsy({fade:!0,gravity:"e"});var data={action:"kraken_request"},errorTpl='<div class="krakenErrorWrap"><a class="krakenError">Failed! Hover here</a></div>',$btnApplyBulkAction=$("#doaction"),$btnApplyBulkAction2=$("#doaction2"),$topBulkActionDropdown=$(".tablenav.top .bulkactions select[name='action']"),$bottomBulkActionDropdown=$(".tablenav.bottom .bulkactions select[name='action2']"),requestSuccess=function(data,textStatus,jqXHR){var $button=$(this),$parent=$(this).closest(".kraken-wrap, .buttonWrap"),$cell=$(this).closest("td");if(data.html){$button.text("Image optimized");var originalSize=(data.type,data.original_size),$originalSizeColumn=$(this).parent().prev("td.original_size");$parent.fadeOut("fast",function(){$cell.find(".noSavings, .krakenErrorWrap").remove(),$cell.html(data.html),$cell.find(".kraken-item-details").tipsy(tipsySettings),$originalSizeColumn.html(originalSize),$parent.remove()})}else if(data.error){var $error=$(errorTpl).attr("title",data.error);$parent.closest("td").find(".krakenErrorWrap").remove(),$parent.after($error),$error.tipsy({fade:!0,gravity:"e"}),$button.text("Retry request").removeAttr("disabled").css({opacity:1})}},requestFail=function(jqXHR,textStatus,errorThrown){$(this).removeAttr("disabled")},requestComplete=function(jqXHR,textStatus,errorThrown){$(this).removeAttr("disabled"),$(this).parent().find(".krakenSpinner").css("display","none")},opts='<option value="kraken-bulk-lossy">Krak \'em all</option>';$topBulkActionDropdown.find("option:last-child").before(opts),$bottomBulkActionDropdown.find("option:last-child").before(opts);var getBulkImageData=function(){var $rows=$("tr[id^='post-']"),$row=null,postId=0,$krakBtn=null,btnData={},originalSize="",rv=[];return $rows.each(function(){$row=$(this),postId=this.id.replace(/^\D+/g,""),$row.find("input[type='checkbox'][value='"+postId+"']:checked").length&&($krakBtn=$row.find(".kraken_req"),$krakBtn.length&&(btnData=$krakBtn.data(),originalSize=$.trim($row.find("td.original_size").text()),btnData.originalSize=originalSize,rv.push(btnData)))}),rv},bulkModalOptions={zIndex:4,escapeClose:!0,clickClose:!1,closeText:"close",showClose:!1},renderBulkImageSummary=function(bulkImageData){var setting=kraken_settings.api_lossy,nImages=bulkImageData.length,header='<p class="krakenBulkHeader">Kraken Bulk Image Optimization <span class="close-kraken-bulk">&times;</span></p>',krakEmAll='<button class="kraken_req_bulk">Krak \'em all</button>',typeRadios='<div class="radiosWrap"><p>Choose optimization mode:</p><label><input type="radio" id="kraken-bulk-type-lossy" value="Lossy" name="kraken-bulk-type"/>Intelligent Lossy</label>&nbsp;&nbsp;&nbsp;<label><input type="radio" id="kraken-bulk-type-lossless" value="Lossless" name="kraken-bulk-type"/>Lossless</label></div>',$modal=$('<div id="kraken-bulk-modal" class="kraken-modal"></div>').html(header).append(typeRadios).append('<p class="the-following">The following <strong>'+nImages+'</strong> images will be optimized by Kraken.io using the <strong class="bulkSetting">'+setting+"</strong> setting:</p>").appendTo("body").kmodal(bulkModalOptions).bind($.kmodal.BEFORE_CLOSE,function(event,modal){}).bind($.kmodal.OPEN,function(event,modal){}).bind($.kmodal.CLOSE,function(event,modal){$("#kraken-bulk-modal").remove()}).css({top:"10px",marginTop:"40px"});"lossy"===setting?$("#kraken-bulk-type-lossy").attr("checked",!0):$("#kraken-bulk-type-lossless").attr("checked",!0),$bulkSettingSpan=$(".bulkSetting"),$("input[name='kraken-bulk-type']").change(function(){var text="kraken-bulk-type-lossy"===this.id?"lossy":"lossless";$bulkSettingSpan.text(text)}),$(".jquery-modal.blocker").click(function(e){return!1}),$("#menu-media ul.wp-submenu").css({"z-index":1});var $table=$('<table id="kraken-bulk"></table>'),$headerRow=$('<tr class="kraken-bulk-header"><td>File Name</td><td style="width:120px">Original Size</td><td style="width:120px">Kraken.io Stats</td></tr>');$table.append($headerRow),$.each(bulkImageData,function(index,element){$table.append('<tr class="kraken-item-row" data-krakenbulkid="'+element.id+'"><td class="kraken-bulk-filename">'+element.filename+'</td><td class="kraken-originalsize">'+element.originalSize+'</td><td class="kraken-krakedsize"><span class="krakenBulkSpinner hidden"></span></td></tr>')}),$modal.append($table).append(krakEmAll),$(".close-kraken-bulk").click(function(){$.kmodal.close()}),nImages||$(".kraken_req_bulk").attr("disabled",!0).css({opacity:.5})},bulkAction=function(bulkImageData){$bulkTable=$("#kraken-bulk");var jqxhr=null,q=async.queue(function(task,callback){var id=task.id,$row=(task.filename,$bulkTable.find("tr[data-krakenbulkid='"+id+"']")),$krakedSizeColumn=$row.find(".kraken-krakedsize"),$spinner=$krakedSizeColumn.find(".krakenBulkSpinner").css({display:"inline-block"}),$savingsPercentColumn=$row.find(".kraken-savingsPercent"),$savingsBytesColumn=$row.find(".kraken-savings");jqxhr=$.ajax({url:ajax_object.ajax_url,data:{action:"kraken_request",id:id,type:$("input[name='kraken-bulk-type']:checked").val().toLowerCase(),origin:"bulk_optimizer"},type:"post",dataType:"json",timeout:36e4}).done(function(data,textStatus,jqXHR){if(data.success&&"undefined"==typeof data.message){var originalSize=(data.type,data.original_size),savingsPercent=(data.html,data.savings_percent),savingsBytes=data.saved_bytes;$krakedSizeColumn.html(data.html),$krakedSizeColumn.find(".kraken-item-details").remove(),$savingsPercentColumn.text(savingsPercent),$savingsBytesColumn.text(savingsBytes);var $button=$("button[id='krakenid-"+id+"']"),$parent=$button.parent(),$cell=$button.closest("td"),$originalSizeColumn=$button.parent().prev("td.original_size");$parent.fadeOut("fast",function(){$cell.find(".noSavings, .krakenErrorWrap").remove(),$cell.empty().html(data.html),$cell.find(".kraken-item-details").tipsy(tipsySettings),$originalSizeColumn.html(originalSize),$parent.remove()})}else data.error&&"This image can not be optimized any further"===data.error&&$krakedSizeColumn.text("No savings found.")}).fail(function(){}).always(function(){$spinner.css({display:"none"}),callback()})},kraken_settings.bulk_async_limit);q.drain=function(){$(".kraken_req_bulk").removeAttr("disabled").css({opacity:1}).text("Done").unbind("click").click(function(){$.kmodal.close()})},q.push(bulkImageData,function(err){})};$btnApplyBulkAction.add($btnApplyBulkAction2).click(function(e){if("kraken-bulk-lossy"===$(this).prev("select").val()){e.preventDefault();var bulkImageData=getBulkImageData();renderBulkImageSummary(bulkImageData),$(".kraken_req_bulk").click(function(e){e.preventDefault(),$(this).attr("disabled",!0).css({opacity:.5}),bulkAction(bulkImageData)})}});var activeId=null;$(".kraken-item-details").tipsy(tipsySettings);$("body").on("click",".kraken-item-details",function(e){var id=$(this).data("id");return $(".tipsy").remove(),id==activeId?(activeId=null,void $(this).text("Show details")):($(".kraken-item-details").text("Show details"),$(this).tipsy("show"),void $(this).text("Hide details"))}),$("body").on("click",function(e){var $t=$(e.target);$t.hasClass("tipsy")||$t.closest(".tipsy").length||$t.hasClass("kraken-item-details")||(activeId=null,$(".kraken-item-details").text("Show details"),$(".tipsy").remove())}),$("body").on("click","small.krakenReset",function(e){e.preventDefault();var $resetButton=$(this),resetData={action:"kraken_reset"};resetData.id=$(this).data("id"),$row=$("#post-"+resetData.id).find(".kraked_size");var $spinner=$('<span class="resetSpinner"></span>');$resetButton.after($spinner);$.ajax({url:ajax_object.ajax_url,data:resetData,type:"post",dataType:"json",timeout:36e4
     2}).done(function(data,textStatus,jqXHR){"undefined"!==data.success&&($row.hide().html(data.html).fadeIn().prev(".original_size.column-original_size").html(data.original_size),$(".tipsy").remove())})}),$("body").on("click",".kraken-reset-all",function(e){e.preventDefault();var reset=confirm("This will immediately remove all Kraken metadata associated with your images. \n\nAre you sure you want to do this?");if(reset){var $resetButton=$(this);$resetButton.text("Resetting images, pleaes wait...").attr("disabled",!0);var resetData={action:"kraken_reset_all"},$spinner=$('<span class="resetSpinner"></span>');$resetButton.after($spinner);{$.ajax({url:ajax_object.ajax_url,data:resetData,type:"post",dataType:"json",timeout:36e4}).done(function(data,textStatus,jqXHR){$spinner.remove(),$resetButton.text("Your images have been reset.").removeAttr("disabled").removeClass("enabled")})}}}),$(".krakenAdvancedSettings h3").on("click",function(){var $rows=$(".kraken-advanced-settings"),$plusMinus=$(".kraken-plus-minus");$rows.is(":visible")?($rows.hide(),$plusMinus.removeClass("dashicons-arrow-down").addClass("dashicons-arrow-right")):($rows.show(),$plusMinus.removeClass("dashicons-arrow-right").addClass("dashicons-arrow-down"))}),$("body").on("click",".kraken_req",function(e){e.preventDefault();var $button=$(this),$parent=$(this).parent();data.id=$(this).data("id"),$button.text("Optimizing image...").attr("disabled",!0).css({opacity:.5}),$parent.find(".krakenSpinner").css("display","inline");$.ajax({url:ajax_object.ajax_url,data:data,type:"post",dataType:"json",timeout:36e4,context:$button}).done(requestSuccess).fail(requestFail).always(requestComplete)})});
  • kraken-image-optimizer/trunk/kraken.php

    r1144921 r1351884  
    2222 * Description: This plugin allows you to optimize your WordPress images through the Kraken API, the world's most advanced image optimization solution.
    2323 * Author: Karim Salman
    24  * Version: 2.0.0
    25  * Stable Tag: 2.0.0
     24 * Version: 2.5.0
     25 * Stable Tag: 2.5.0
    2626 * Author URI: https://kraken.io
    2727 * License GPL2
     
    4242        private $optimization_type = 'lossy';
    4343
    44         public static $kraken_plugin_version = '2.0.0';
     44        public static $kraken_plugin_version = '2.5.0';
    4545
    4646        function __construct() {
     
    5151            add_action( 'admin_enqueue_scripts', array( &$this, 'my_enqueue' ) );
    5252            add_action( 'wp_ajax_kraken_reset', array( &$this, 'kraken_media_library_reset' ) );
     53            add_action( 'wp_ajax_kraken_optimize', array( &$this, 'kraken_optimize' ) );
    5354            add_action( 'wp_ajax_kraken_request', array( &$this, 'kraken_media_library_ajax_callback' ) );
    5455            add_action( 'wp_ajax_kraken_reset_all', array( &$this, 'kraken_media_library_reset_all' ) );
     
    5657            add_filter( 'manage_media_columns', array( &$this, 'add_media_columns') );
    5758            add_filter( 'plugin_action_links_' . plugin_basename(__FILE__), array( &$this, 'add_settings_link' ) );
     59
    5860            if ( ( !empty( $this->kraken_settings ) && !empty( $this->kraken_settings['auto_optimize'] ) ) || !isset( $this->kraken_settings['auto_optimize'] ) ) {
    59                 add_filter( 'wp_generate_attachment_metadata', array( &$this, 'optimize_thumbnails') );
    60                 add_action( 'add_attachment', array( &$this, 'kraken_media_uploader_callback' ) );             
    61             }
    62             add_action( 'admin_menu', array( &$this, 'kraken_menu' ) );         
     61                add_action( 'add_attachment', array( &$this, 'kraken_media_uploader_callback' ) );         
     62                add_filter( 'wp_generate_attachment_metadata', array( &$this, 'optimize_thumbnails' ) );
     63            }
     64            add_action( 'admin_menu', array( &$this, 'kraken_menu' ) );     
     65        }
     66
     67        function isApiActive() {
     68            $settings = $this->kraken_settings;
     69            $api_key = isset( $settings['api_key'] ) ? $settings['api_key'] : '';
     70            $api_secret = isset( $settings['api_secret'] ) ? $settings['api_secret'] : '';
     71            if ( empty( $api_key ) || empty( $api_secret) ) {
     72                return false;
     73            }
     74            return true;           
    6375        }
    6476
     
    6779        }
    6880
     81        function blog_kraker() {
     82            add_management_page( 'Blog Kraker', 'Blog Kraker', 'manage_options', 'blog-kraker', array( &$this, 'show_blog_kraker' ) );
     83        }
    6984
    7085        function add_settings_link ( $links ) {
     
    7590        }
    7691
    77 
    78         function  kraken_settings_page() {
    79                  
     92        function kraken_settings_page() {
     93
    8094            if ( !empty( $_POST ) ) {
    8195                $options = $_POST['_kraken_options'];
     
    87101            $lossy = isset( $settings['api_lossy'] ) ? $settings['api_lossy'] : 'lossy';
    88102            $auto_optimize = isset( $settings['auto_optimize'] ) ? $settings['auto_optimize'] : 1;
    89            
     103            $optimize_main_image = isset( $settings['optimize_main_image'] ) ? $settings['optimize_main_image'] : 1;
    90104            $api_key = isset( $settings['api_key'] ) ? $settings['api_key'] : '';
    91105            $api_secret = isset( $settings['api_secret'] ) ? $settings['api_secret'] : '';
    92            
    93106            $show_reset = isset( $settings['show_reset'] ) ? $settings['show_reset'] : 0;
    94107            $bulk_async_limit = isset( $settings['bulk_async_limit'] ) ? $settings['bulk_async_limit'] : 4;
     108            $preserve_meta_date = isset( $settings['preserve_meta_date'] ) ? $settings['preserve_meta_date'] : 0;
     109            $preserve_meta_copyright = isset( $settings['preserve_meta_copyright'] ) ? $settings['preserve_meta_copyright'] : 0;
     110            $preserve_meta_geotag = isset( $settings['preserve_meta_geotag'] ) ? $settings['preserve_meta_geotag'] : 0;
     111            $preserve_meta_orientation = isset( $settings['preserve_meta_orientation'] ) ? $settings['preserve_meta_orientation'] : 0;
     112            $preserve_meta_profile = isset( $settings['preserve_meta_profile'] ) ? $settings['preserve_meta_profile'] : 0;
     113            $auto_orient = isset( $settings['auto_orient'] ) ? $settings['auto_orient'] : 1;
     114            $resize_width = isset( $settings['resize_width'] ) ? $settings['resize_width'] : 0;
     115            $resize_height = isset( $settings['resize_height'] ) ? $settings['resize_height'] : 0;
     116            $jpeg_quality = isset( $settings['jpeg_quality'] ) ? $settings['jpeg_quality'] : 0;
    95117
    96118            $status = $this->get_api_status( $api_key, $api_secret );
     
    139161                                </tr>
    140162                                <tr>
    141                                     <th scope="row">Optimization Type:</th>
     163                                    <th scope="row">API status:</th>
     164                                    <td>
     165                                        <?php echo $status_html ?>
     166                                    </td>
     167                                </tr>
     168                                <tr class="with-tip">
     169                                    <th scope="row">Optimization mode:</th>
    142170                                    <td>
    143171                                        <input type="radio" id="kraken_lossy" name="_kraken_options[api_lossy]" value="lossy" <?php checked( 'lossy', $lossy, true ); ?>/>
    144                                         <label for="kraken_lossy">Lossy</label>
     172                                        <label for="kraken_lossy">Intelligent Lossy</label>
    145173                                        <input style="margin-left:10px;" type="radio" id="kraken_lossless" name="_kraken_options[api_lossy]" value="lossless" <?php checked( 'lossless', $lossy, true ) ?>/>
    146174                                        <label for="kraken_lossless">Lossless</label>
    147175                                    </td>
    148176                                </tr>
    149                                 <tr>
     177                                <tr class="tip">
     178                                    <td colspan="2">
     179                                        <div>
     180                                            The <strong>Intelligent Lossy</strong> mode will yield the greatest savings without perceivable reducing the quality of your images, and so we recommend this setting to users.<br />
     181                                            The <strong>Lossless</strong> mode will result in an unchanged image, however, will yield reduced savings as the image will not be recompressed.
     182                                        </div>
     183                                    </td>
     184                                </tr>
     185                                <tr class="with-tip">
    150186                                    <th scope="row">Automatically optimize uploads:</th>
    151187                                    <td>
    152188                                        <input type="checkbox" id="auto_optimize" name="_kraken_options[auto_optimize]" value="1" <?php checked( 1, $auto_optimize, true ); ?>/>
    153189                                    </td>
    154                                 </tr>                           
    155                                 <tr>
    156                                     <th scope="row">API status:</th>
     190                                </tr>
     191                                <tr class="tip">
     192                                    <td colspan="2">
     193                                        <div>
     194                                            Enabled by default. This setting causes images uploaded through the Media Uploader to be optimized on-the-fly.<br />
     195                                            If you do not wish to do this, or wish to optimize images later, disable this setting by unchecking the box.
     196                                        </div>
     197                                    </td>
     198                                </tr>
     199                                <tr class="with-tip">
     200                                    <th scope="row">Optimize main image:</th>
    157201                                    <td>
    158                                         <?php echo $status_html ?>
     202                                        <input type="checkbox" id="optimize_main_image" name="_kraken_options[optimize_main_image]" value="1" <?php checked( 1, $optimize_main_image, true ); ?>/>
    159203                                    </td>
    160204                                </tr>
    161                                 <tr>
    162                                     <td class="krakenAdvancedSettings"><h3><span class="kraken-advanced-settings-label" title="Click to toggle advanced settings">Advanced Settings</span><span class="kraken-plus-minus dashicons dashicons-arrow-right"></span></h3></td>
     205                                <tr class="tip">
     206                                    <td colspan="2">
     207                                        <div>
     208                                            Enabled by default. This option causes the image uploaded by the user to get optimized, as well as all sizes generated by WordPress.<br />
     209                                            Disabling this option results in faster uploading, since the main image is not sent to our system for optimization.<br />
     210                                            Disable this option if you never use the "main" image upload in your posts, or speed of image uploading is an issue.
     211                                        </div>
     212                                    </td>
     213                                </tr>
     214                                <tr class="with-tip">
     215                                    <th scope="row">Resize main image:</th>
     216                                    <td>
     217                                        Max Width (px):&nbsp;&nbsp;<input type="text" id="kraken_maximum_width" name="_kraken_options[resize_width]" value="<?php echo esc_attr( $resize_width ); ?>" style="width:50px;" />&nbsp;&nbsp;&nbsp;Max Height (px):&nbsp;<input type="text" id="kraken_maximum_height" name="_kraken_options[resize_height]" value="<?php echo esc_attr( $resize_height ); ?>" style="width:50px;" />
     218                                    </td>
     219                                </tr>
     220                                <tr class="tip">
     221                                    <td colspan="2">
     222                                        <div>
     223                                            You can restrict the maximum dimensions of image uploads by width and/or height.<br />
     224                                            It is especially useful if you wish to prevent unnecessarily large photos with extremely high resolutions from being uploaded, for example, <br />
     225                                            photos shot with a recent-model iPhone. Note: you can restrict the dimenions by width, height, or both. A value of zero disables.
     226                                        </div>
     227                                    </td>
     228                                </tr>
     229                                <tr class="with-tip">
     230                                    <th scope="row">JPEG quality setting:</th>
     231                                    <td>
     232                                        <select name="_kraken_options[jpeg_quality]">
     233                                            <?php $i = 0 ?>
     234                                            <?php foreach ( range(100, 25) as $number ) { ?>
     235                                                <?php if ( $i === 0 ) { ?>
     236                                                    <?php echo '<option value="0">Intelligent lossy (recommended)</option>'; ?>
     237                                                <?php } ?>
     238                                                <?php if ($i > 0) { ?>
     239                                                    <option value="<?php echo $number ?>" <?php selected( $jpeg_quality, $number, true); ?>>
     240                                                    <?php echo $number; ?>
     241                                                <?php } ?>
     242                                                    </option>
     243                                                <?php $i++ ?>
     244                                            <?php } ?>
     245                                        </select>
     246                                    </td>
     247                                </tr>
     248                                <tr class="tip">
     249                                    <td colspan="2">
     250                                        <div>
     251                                            Advanced users can force the quality of JPEG images to a discrete "q" value between 25 and 100 using this setting <br />
     252                                            For example, forcing the quality to 60 or 70 might yield greater savings, but the resulting quality might be affected, depending on the image. <br />
     253                                            We therefore recommend keeping the <strong>Intelligent Lossy</strong> setting, which will not allow a resulting image of unacceptable quality.<br />
     254                                            This settings will be ignored when using the <strong>lossless</strong> optimization mode.
     255                                        </div>
     256                                    </td>
     257                                </tr>
     258                                <tr class="no-border">
     259                                    <td class="krakenAdvancedSettings"><h3><span class="kraken-advanced-settings-label" title="Click to toggle advanced settings">Advanced Settings</span></h3></td>
    163260                                </tr>
    164261                                <tr class="kraken-advanced-settings">
    165262                                    <td colspan="2" class="krakenAdvancedSettingsDescription"><small>We recommend that you leave these settings at their default values</td>
    166263                                </tr>
    167                                 <tr class="kraken-advanced-settings">
    168                                     <th scope="row">
    169                                         Show metadata reset per image:&nbsp;
    170                                         <small class="krakenWhatsThis" title="Checking this option will add a Reset button in the Kraked Size column for each optimized image. Resetting an image will remove the Kraken.io metadata associated with it, effectively making your blog forget that it had been optimized in the first place, allowing further optimization in some cases. If an image has been optimized using the lossless setting, lossless optimization will not yield any greater savings. If in doubt, please contact [email protected]">What's this?</small>
    171                                     </th>
     264                                <tr class="kraken-advanced-settings">
     265                                    <th scope="row">Preserve EXIF Metadata:</th>
     266                                    <td>
     267                                        <label for="preserve_meta_date"><input type="checkbox" id="preserve_meta_date" name="_kraken_options[preserve_meta_date]" value="1" <?php checked( 1, $preserve_meta_date, true ); ?>/>&nbsp;Date</label>&nbsp;&nbsp;&nbsp;&nbsp;
     268                                        <label for="preserve_meta_copyright"><input type="checkbox" id="preserve_meta_copyright" name="_kraken_options[preserve_meta_copyright]" value="1" <?php checked( 1, $preserve_meta_copyright, true ); ?>/>&nbsp;Copyright</label>&nbsp;&nbsp;&nbsp;&nbsp;
     269                                        <label for="preserve_meta_geotag"><input type="checkbox" id="preserve_meta_geotag" name="_kraken_options[preserve_meta_geotag]" value="1" <?php checked( 1, $preserve_meta_geotag, true ); ?>/>&nbsp;Geotag</label>&nbsp;&nbsp;&nbsp;&nbsp;
     270                                        <label for="preserve_meta_orientation"><input type="checkbox" id="preserve_meta_orientation" name="_kraken_options[preserve_meta_orientation]" value="1" <?php checked( 1, $preserve_meta_orientation, true ); ?>/>&nbsp;Orientation</label>&nbsp;&nbsp;&nbsp;&nbsp;
     271                                        <label for="preserve_meta_profile"><input type="checkbox" id="preserve_meta_profile" name="_kraken_options[preserve_meta_profile]" value="1" <?php checked( 1, $preserve_meta_profile, true ); ?>/>&nbsp;Profile</label>&nbsp;&nbsp;&nbsp;&nbsp;
     272                                    </td>
     273                                </tr>
     274                                <tr class="kraken-advanced-settings with-tip">
     275                                    <th scope="row">Automatically Orient Images:</th>
     276                                    <td>
     277                                        <input type="checkbox" id="auto_orient" name="_kraken_options[auto_orient]" value="1" <?php checked( 1, $auto_orient, true ); ?>/>
     278                                    </td>
     279                                </tr>
     280                                <tr class="tip">
     281                                    <td colspan="2">
     282                                        <div>
     283                                            This setting will rotate the JPEG image according to its <strong>Orientation</strong> EXIF metadata such that it will always be correctly displayed in Web Browsers.<br />
     284                                            Enable this setting if many of your image uploads come from smart phones or digital cameras which set the orientation based on how they are held at the time of shooting.
     285                                        </div>
     286                                    </td>
     287                                </tr>
     288                                <tr class="kraken-advanced-settings with-tip">
     289                                    <th scope="row">Show metadata reset per image:</th>
    172290                                    <td>
    173291                                        <input type="checkbox" id="kraken_show_reset" name="_kraken_options[show_reset]" value="1" <?php checked( 1, $show_reset, true ); ?>/>
     
    175293                                    </td>
    176294                                </tr>
    177                                 <tr class="kraken-advanced-settings">
    178                                     <th scope="row">
    179                                         Bulk Concurrency:
    180                                         <small class="krakenWhatsThis" title="This settings defines how many images can be processed at the same time using the bulk optimizer. The recommended value is 4. For blogs on very small hosting plans, or with reduced connectivity, a lower number might be necessary to avoid hitting request limits.">what's this?</small>
    181                                     </th>
     295                                <tr class="tip">
     296                                    <td colspan="2">
     297                                        <div>
     298                                            Checking this option will add a Reset button in the "Show Details" popup in the Kraken Stats column for each optimized image.<br />
     299                                            Resetting an image will remove the Kraken.io metadata associated with it, effectively making your blog forget that it had been optimized in the first place, allowing further optimization in some cases.<br />
     300                                            If an image has been optimized using the lossless setting, lossless optimization will not yield any greater savings. If in doubt, please contact [email protected]
     301                                        </div>
     302                                    </td>
     303                                </tr>
     304                                <tr class="kraken-advanced-settings with-tip">
     305                                    <th scope="row">Bulk Concurrency:</th>
    182306                                    <td>
    183307                                        <select name="_kraken_options[bulk_async_limit]">
     
    189313                                        </select>
    190314                                    </td>
    191                                 </tr>                               
     315                                </tr>
     316                                <tr class="tip">
     317                                    <td colspan="2">
     318                                        <div>
     319                                            This settings defines how many images can be processed at the same time using the bulk optimizer. The recommended (and default) value is 4. <br />
     320                                            For blogs on very small hosting plans, or with reduced connectivity, a lower number might be necessary to avoid hitting request limits.
     321                                        </div>
     322                                    </td>
     323                                </tr>
    192324                            </tbody>
    193325                        </table>
     
    202334            $valid['api_lossy'] = $input['api_lossy'];
    203335            $valid['auto_optimize'] = isset( $input['auto_optimize'] )? 1 : 0;
     336            $valid['optimize_main_image'] = isset( $input['optimize_main_image'] ) ? 1 : 0;
     337            $valid['preserve_meta_date'] = isset( $input['preserve_meta_date'] ) ? $input['preserve_meta_date'] : 0;
     338            $valid['preserve_meta_copyright'] = isset( $input['preserve_meta_copyright'] ) ? $input['preserve_meta_copyright'] : 0;
     339            $valid['preserve_meta_geotag'] = isset( $input['preserve_meta_geotag'] ) ? $input['preserve_meta_geotag'] : 0;
     340            $valid['preserve_meta_orientation'] = isset( $input['preserve_meta_orientation'] ) ? $input['preserve_meta_orientation'] : 0;
     341            $valid['preserve_meta_profile'] = isset( $input['preserve_meta_profile'] ) ? $input['preserve_meta_profile'] : 0;
     342            $valid['auto_orient'] = isset( $input['auto_orient'] ) ? $input['auto_orient'] : 0;
    204343            $valid['show_reset'] = isset( $input['show_reset'] ) ? 1 : 0;
    205344            $valid['bulk_async_limit'] = isset( $input['bulk_async_limit'] ) ? $input['bulk_async_limit'] : 4;
     345            $valid['resize_width'] = isset( $input['resize_width'] ) ? (int) $input['resize_width'] : 0;
     346            $valid['resize_height'] = isset( $input['resize_height'] ) ? (int) $input['resize_height'] : 0;
     347            $valid['jpeg_quality'] = isset( $input['jpeg_quality'] ) ? (int) $input['jpeg_quality'] : 0;
    206348
    207349            if ( $valid['show_reset'] ) {
     
    241383
    242384        function my_enqueue( $hook ) {
     385
    243386            if ( $hook == 'options-media.php' || $hook == 'upload.php' || $hook == 'settings_page_wp-krakenio' ) {
    244387                wp_enqueue_script( 'jquery' );
     
    273416
    274417        /**
     418         *  Converts an deserialized API result array into an array
     419         *  which this plugin will consume
     420         */
     421        function get_result_arr( $result, $image_id ) {
     422            $rv = array();
     423            $rv['original_size'] = $result['original_size'];
     424            $rv['kraked_size'] = $result['kraked_size'];
     425            $rv['saved_bytes'] = $result['saved_bytes'];
     426            $savings_percentage = $result['saved_bytes'] / $result['original_size'] * 100;
     427            $rv['savings_percent'] = round( $savings_percentage, 2 ) . '%';
     428            $rv['type'] = $result['type'];
     429            if ( !empty( $result['kraked_width'] ) && !empty( $result['kraked_height'] ) ) {
     430                $rv['kraked_width'] = $result['kraked_width'];
     431                $rv['kraked_height'] = $result['kraked_height'];
     432            }
     433            $rv['success'] = $result['success'];
     434            $rv['meta'] = wp_get_attachment_metadata( $image_id );
     435            return $rv;     
     436        }
     437
     438
     439        /**
    275440         *  Handles optimizing already-uploaded images in the  Media Library
    276441         */
    277442        function kraken_media_library_ajax_callback() {
    278 
     443           
    279444            $image_id = (int) $_POST['id'];
    280445            $type = false;
     446
    281447            if ( isset( $_POST['type'] ) ) {
    282448                $type = $_POST['type'];
     449                $this->optimization_type = $type;
    283450            }
    284451
     
    287454            if ( wp_attachment_is_image( $image_id ) ) {
    288455
     456                $settings = $this->kraken_settings;
     457
    289458                $image_path = get_attached_file( $image_id );
    290                 $settings = $this->kraken_settings;
     459                $optimize_main_image = !empty( $settings['optimize_main_image'] );
    291460                $api_key = isset( $settings['api_key'] ) ? $settings['api_key'] : '';
    292461                $api_secret = isset( $settings['api_secret'] ) ? $settings['api_secret'] : '';
    293462
    294                 $status = $this->get_api_status( $api_key, $api_secret );
    295 
    296                 if ( $status === false ) {
    297                     $kv['error'] = 'There is a problem with your credentials. Please check them in the Kraken.io settings section of Media Settings, and try again.';
    298                     update_post_meta( $image_id, '_kraken_size', $kv );
    299                     echo json_encode( array( 'error' => $kv['error'] ) );
     463                $data = array();
     464
     465                if ( empty( $api_key ) && empty( $api_secret ) ) {
     466                    $data['error'] = 'There is a problem with your credentials. Please check them in the Kraken.io settings section of Media Settings, and try again.';
     467                    update_post_meta( $image_id, '_kraken_size', $data );
     468                    echo json_encode( array( 'error' => $data['error'] ) );
    300469                    exit;
    301470                }
    302471
    303                 if ( isset( $status['active'] ) && $status['active'] === true ) {
    304 
     472                if ( $optimize_main_image ) {
     473
     474                    // check if thumbs already optimized
     475                    $thumbs_optimized = false;
     476                    $kraked_thumbs_data = get_post_meta( $image_id, '_kraked_thumbs', true );
     477                   
     478                    if ( !empty ( $kraked_thumbs_data ) ) {
     479                        $thumbs_optimized = true;
     480                    }
     481
     482                    // get metadata for thumbnails
     483                    $image_data = wp_get_attachment_metadata( $image_id );
     484
     485                    if ( !$thumbs_optimized ) {
     486                        $this->optimize_thumbnails( $image_data );
     487                    } else {
     488
     489                        // re-optimize thumbs if mode has changed
     490                        $kraked_thumbs_mode = $kraked_thumbs_data[0]['type'];                       
     491                        if ( strcmp( $kraked_thumbs_mode, $this->optimization_type ) !== 0 ) {
     492                            wp_generate_attachment_metadata( $image_id, $image_path );
     493                            $this->optimize_thumbnails( $image_data );
     494                        }
     495                    }
     496
     497                    $resize = false;
     498                    if ( !empty( $settings['resize_width'] ) || !empty( $settings['resize_height'] ) ) {
     499                        $resize = true;
     500                    }
     501
     502                    $api_result = $this->optimize_image( $image_path, $type, $resize );
     503
     504                    if ( !empty( $api_result ) && !empty( $api_result['success'] ) ) {
     505                        $data = $this->get_result_arr( $api_result, $image_id );
     506                        if ( $this->replace_image( $image_path, $api_result['kraked_url'] ) ) {
     507
     508                            if ( !empty( $data['kraked_width'] ) && !empty( $data['kraked_height'] ) ) {
     509                                $image_data = wp_get_attachment_metadata( $image_id );
     510                                $image_data['width'] = $data['kraked_width'];
     511                                $image_data['height'] = $data['kraked_height'];
     512                                wp_update_attachment_metadata( $image_id, $image_data );
     513                            }
     514
     515                            // store kraked info to DB
     516                            update_post_meta( $image_id, '_kraken_size', $data );
     517
     518                            // krak thumbnails, store that data too. This can be unset when there are no thumbs
     519                            $kraked_thumbs_data = get_post_meta( $image_id, '_kraked_thumbs', true );
     520                            if ( !empty( $kraked_thumbs_data ) ) {
     521                                $data['thumbs_data'] = $kraked_thumbs_data;
     522                                $data['success'] = true;
     523                            }
     524
     525                            $data['html'] = $this->generate_stats_summary( $image_id );
     526                            echo json_encode( $data );
     527                       
     528                        } else {
     529                            echo json_encode( array( 'error' => 'Could not overwrite original file. Please ensure that your files are writable by plugins.' ) );
     530                            exit;
     531                        }   
     532
     533                    } else {
     534                        // error or no optimization
     535                        if ( file_exists( $image_path ) ) {
     536                            update_post_meta( $image_id, '_kraken_size', $data );
     537                        } else {
     538                            // file not found
     539                        }
     540                        echo json_encode( array( 'error' => $api_result['message'], '' ) );
     541                    }
    305542                } else {
    306                     echo json_encode( array( 'error' => 'Your API is inactive. Please visit your account settings' ) );
    307                     die();
    308                 }
    309 
    310                 $result = $this->optimize_image( $image_path, $type );
    311 
    312                 $kv = array();
    313 
    314                 if ( $result['success'] == true && !isset( $result['error'] ) ) {
    315 
    316                     $kraked_url = $result['kraked_url'];
    317                     $savings_percentage = (int) $result['saved_bytes'] / (int) $result['original_size'] * 100;
    318                     $kv['original_size'] = self::pretty_kb( $result['original_size'] );
    319                     $kv['kraked_size'] = self::pretty_kb( $result['kraked_size'] );
    320                     $kv['saved_bytes'] = self::pretty_kb( $result['saved_bytes'] );
    321                     $kv['savings_percent'] = round( $savings_percentage, 2 ) . '%';
    322                     $kv['type'] = $result['type'];
    323                     $kv['success'] = true;
    324                     $kv['meta'] = wp_get_attachment_metadata( $image_id );
    325                     $saved_bytes = (int) $kv['saved_bytes'];
    326 
    327                     if ( $this->replace_image( $image_path, $result['kraked_url'] ) ) {
    328 
    329                         // get metadata for thumbnails
    330                         $image_data = wp_get_attachment_metadata( $image_id );
    331                         $this->optimize_thumbnails( $image_data );
    332 
    333                         // store kraked info to DB
    334                         update_post_meta( $image_id, '_kraken_size', $kv );
    335 
    336                         // krak thumbnails, store that data too. This can be unset when there are no thumbs
    337                         $kraked_thumbs_data = get_post_meta( $image_id, '_kraked_thumbs', true );
    338                         if ( !empty( $kraked_thumbs_data ) ) {
    339                             $kv['thumbs_data'] = $kraked_thumbs_data;
    340                         }
    341                         $kv['html'] = $this->results_html( $image_id );
    342                         echo json_encode( $kv );
    343                     } else {
    344                         echo json_encode( array( 'error' => 'Could not overwrite original file. Please ensure that your files are writable by plugins.' ) );
    345                         exit;
    346                     }   
    347 
    348                 } else {
    349 
    350                     // error or no optimization
    351                     if ( file_exists( $image_path ) ) {
    352 
    353                         $kv['original_size'] = self::pretty_kb( filesize( $image_path ) );
    354                         $kv['error'] = $result['error'];
    355                         $kv['type'] = $result['type'];
    356 
    357                         if ( $kv['error'] == 'This image can not be optimized any further' ) {
    358                             $kv['kraked_size'] = 'No savings found';
    359                             $kv['no_savings'] = true;
    360                         }
    361 
    362                         update_post_meta( $image_id, '_kraken_size', $kv );
    363 
    364                     } else {
    365                         // file not found
     543                    // get metadata for thumbnails
     544                    $image_data = wp_get_attachment_metadata( $image_id );
     545                    $this->optimize_thumbnails( $image_data );
     546
     547                    // krak thumbnails, store that data too. This can be unset when there are no thumbs
     548                    $kraked_thumbs_data = get_post_meta( $image_id, '_kraked_thumbs', true );
     549
     550                    if ( !empty( $kraked_thumbs_data ) ) {
     551                        $data['thumbs_data'] = $kraked_thumbs_data;
     552                        $data['success'] = true;
    366553                    }
    367                     echo json_encode($result);
    368                 }
    369             }
    370             die();
    371         }
     554                    $data['html'] = $this->generate_stats_summary( $image_id );
     555
     556                    echo json_encode( $data );
     557                }
     558            }
     559            wp_die();
     560        }
     561
     562
     563        function is_successful( $response ) {}
    372564
    373565        /**
     
    375567         */
    376568        function kraken_media_uploader_callback( $image_id ) {
     569
    377570            $this->id = $image_id;
    378571
     572            if ( !$this->kraken_settings['optimize_main_image'] ) {
     573                return;
     574            }
     575
     576            $settings = $this->kraken_settings;
     577            $type = $settings['api_lossy'];
     578
     579            if ( !$this->isApiActive() ) {
     580                remove_filter( 'wp_generate_attachment_metadata', array( &$this, 'optimize_thumbnails') );
     581                remove_action( 'add_attachment', array( &$this, 'kraken_media_uploader_callback' ) );
     582                return;
     583            }
     584
    379585            if ( wp_attachment_is_image( $image_id ) ) {
    380586
    381                 $settings = $this->kraken_settings;
    382                 $type = $settings['api_lossy'];
    383587                $image_path = get_attached_file( $image_id );
    384                 $result = $this->optimize_image( $image_path, $type );
    385 
    386                 if ( $result['success'] == true && !isset( $result['error'] ) ) {
    387 
    388                     $kraked_url = $result['kraked_url'];
    389                     $savings_percentage = (int) $result['saved_bytes'] / (int) $result['original_size'] * 100;
    390                     $kv['original_size'] = self::pretty_kb( $result['original_size'] );
    391                     $kv['kraked_size'] = self::pretty_kb( $result['kraked_size'] );
    392                     $kv['saved_bytes'] = self::pretty_kb( $result['saved_bytes'] );
    393                     $kv['savings_percent'] = round( $savings_percentage, 2 ) . '%';
    394                     $kv['type'] = $result['type'];
    395                     $kv['success'] = true;
    396                     $kv['meta'] = wp_get_attachment_metadata( $image_id );
    397                     $saved_bytes = (int) $kv['saved_bytes'];
    398 
    399                     if ( $this->replace_image( $image_path, $kraked_url ) ) {
    400                         update_post_meta( $image_id, '_kraken_size', $kv );
     588                $image_backup_path = $image_path . '_kraken_' . md5( $image_path );
     589                $backup_created = false;
     590
     591                if ( copy( $image_path, $image_backup_path ) ) {
     592                    $backup_created = true;
     593                }
     594
     595                $resize = false;
     596                if ( !empty( $settings['resize_width'] ) || !empty( $settings['resize_height'] ) ) {
     597                    $resize = true;
     598                }
     599
     600                // optimize backup image
     601                if ( $backup_created ) {
     602                    $api_result = $this->optimize_image( $image_backup_path, $type, $resize );
     603                } else {
     604                    $api_result = $this->optimize_image( $image_path, $type, $resize );
     605                }               
     606
     607                $data = array();
     608
     609                if ( !empty( $api_result ) && !empty( $api_result['success'] ) ) {
     610                    $data = $this->get_result_arr( $api_result, $image_id );
     611                   
     612                    if ( $backup_created ) {
     613                        $data['optimized_backup_file'] = $image_backup_path;
     614                        if ( $data['saved_bytes'] > 0 ) {
     615                            if ( $this->replace_image( $image_backup_path, $api_result['kraked_url'] ) ) {
     616                            } else {
     617                                error_log('Kraken.io: Could not replace local image with optimized image.');
     618                            }                       
     619                        }                       
    401620                    } else {
    402                         // writing image failed
     621                        if ( $data['saved_bytes'] > 0 ) {
     622                            if ( $this->replace_image( $image_path, $api_result['kraked_url'] ) ) {
     623                            } else {
     624                                error_log('Kraken.io: Could not replace local image with optimized image.');
     625                            }                       
     626                        }
    403627                    }
     628                    update_post_meta( $image_id, '_kraken_size', $data );
    404629
    405630                } else {
    406 
    407631                    // error or no optimization
    408632                    if ( file_exists( $image_path ) ) {
    409633
    410                         $kv['original_size'] = self::pretty_kb( filesize( $image_path ) );
    411                         $kv['error'] = $result['error'];
    412                         $kv['type'] = $result['type'];
    413 
    414                         if ( $kv['error'] == 'This image can not be optimized any further' ) {
    415                             $kv['kraked_size'] = 'No savings found';
    416                             $kv['no_savings'] = true;
    417                         }
    418 
    419                         update_post_meta( $image_id, '_kraken_size', $kv );
     634                        $data['original_size'] = filesize( $image_path );
     635                        $data['error'] = $api_result['message'];
     636                        $data['type'] = $api_result['type'];
     637                        update_post_meta( $image_id, '_kraken_size', $data );
    420638
    421639                    } else {
     
    429647            $image_id = (int) $_POST['id'];
    430648            $image_meta = get_post_meta( $image_id, '_kraken_size', true );
    431             $original_size = $image_meta['kraked_size'];
     649            $original_size = self::formatBytes( filesize( get_attached_file( $image_id ) ) );
    432650            delete_post_meta( $image_id, '_kraken_size' );
    433651            delete_post_meta( $image_id, '_kraked_thumbs' );           
    434652            echo json_encode( array( 'success' => true, 'original_size' => $original_size, 'html' => $this->optimize_button_html( $image_id ) ) );
    435             die();
     653            wp_die();
    436654        }
    437655
     
    442660            $result = json_encode( array( 'success' => true ) );
    443661            echo $result;
    444             die();
     662            wp_die();
    445663        }
    446664
     
    554772        }
    555773
    556 
    557774        function add_media_columns( $columns ) {
    558775            $columns['original_size'] = 'Original Size';
    559             $columns['kraked_size'] = 'Kraked Size';
     776            $columns['kraked_size'] = 'Kraken.io Stats';
    560777            return $columns;
    561778        }
    562779
    563         function results_html( $id ) {
     780
     781        static function KBStringToBytes( $str ) {
     782            $temp = floatVal( $str );
     783            $rv = false;
     784            if ( 0 == $temp ) {
     785                $rv = '0 bytes';
     786            } else {
     787                $rv = self::formatBytes( ceil( floatval( $str) * 1024 ) );
     788            }
     789            return $rv;
     790        }
     791
     792
     793        static function calculate_savings( $meta ) {
     794
     795            if ( isset( $meta['original_size'] ) ) {
     796
     797                $saved_bytes = isset( $meta['saved_bytes'] ) ? $meta['saved_bytes'] : '';
     798                $savings_percentage = $meta['savings_percent'];
     799
     800                // convert old data format, where applicable
     801                if ( stripos( $saved_bytes, 'kb' ) !== false ) {
     802                    $saved_bytes = self::KBStringToBytes( $saved_bytes );
     803                } else {
     804                    if ( !$saved_bytes ) {
     805                        $saved_bytes = '0 bytes';
     806                    } else {
     807                        $saved_bytes = self::formatBytes( $saved_bytes );
     808                    }
     809                }
     810
     811                return array(
     812                    'saved_bytes' => $saved_bytes,
     813                    'savings_percentage' => $savings_percentage
     814                );
     815           
     816            } else if ( !empty( $meta ) ) {
     817                $thumbs_count = count( $meta );
     818                $total_thumb_byte_savings = 0;
     819                $total_thumb_size = 0;
     820                $thumbs_savings_percentage = '';
     821                $total_thumbs_savings = '';
     822
     823                foreach ( $meta as $k => $v ) {
     824                    $total_thumb_size += $v['original_size'];
     825                    $thumb_byte_savings = $v['original_size'] - $v['kraked_size'];
     826                    $total_thumb_byte_savings += $thumb_byte_savings;
     827                }
     828
     829                $thumbs_savings_percentage = round( ( $total_thumb_byte_savings / $total_thumb_size * 100 ), 2 ) . '%';
     830                if ( $total_thumb_byte_savings ) {
     831                    $total_thumbs_savings = self::formatBytes( $total_thumb_byte_savings );
     832                } else {
     833                    $total_thumbs_savings = '0 bytes';
     834                }
     835                return array(
     836                    'savings_percentage' => $thumbs_savings_percentage,
     837                    'total_savings' => $total_thumbs_savings
     838                );
     839            }
     840        }
     841
     842        function generate_stats_summary( $id ) {
    564843            $image_meta = get_post_meta( $id, '_kraken_size', true );
    565844            $thumbs_meta = get_post_meta( $id, '_kraked_thumbs', true );
    566             $kraked_size = $image_meta['kraked_size'];
    567             $type = $image_meta['type'];
    568             $thumbs_count = count( $thumbs_meta );
    569             $savings_percentage = $image_meta['savings_percent'];
    570 
     845
     846            $total_original_size = 0;
     847            $total_kraked_size = 0;
     848            $total_saved_bytes = 0;
     849           
     850            $total_savings_percentage = 0;
     851
     852            // crap for backward compat
     853            if ( isset( $image_meta['original_size'] ) ) {
     854
     855                $original_size = $image_meta['original_size'];
     856
     857                if ( stripos( $original_size, 'kb' ) !== false ) {
     858                    $total_original_size = ceil( floatval( $original_size ) * 1024 );
     859                } else {
     860                    $total_original_size = (int) $original_size;
     861                }
     862
     863                if ( isset( $image_meta['saved_bytes'] ) ) {
     864                    $saved_bytes = $image_meta['saved_bytes'];
     865                    if ( is_string( $saved_bytes ) ) {
     866                        $total_saved_bytes = (int) ceil( floatval( $saved_bytes ) * 1024 );
     867                    } else {
     868                        $total_saved_bytes = $saved_bytes;
     869                    }
     870                }
     871
     872                $total_kraked_size = $total_original_size - $total_saved_bytes;
     873            }
     874
     875            if ( !empty( $thumbs_meta ) ) {
     876                $thumb_saved_bytes = 0;
     877                $total_thumb_byte_savings = 0;
     878                $total_thumb_size = 0;
     879
     880                foreach ( $thumbs_meta as $k => $v ) {
     881                    $total_original_size += $v['original_size'];
     882                    $thumb_saved_bytes = $v['original_size'] - $v['kraked_size'];
     883                    $total_saved_bytes += $thumb_saved_bytes;
     884                }
     885
     886            }
     887            $total_savings_percentage = round( ( $total_saved_bytes / $total_original_size * 100 ), 2 ) . '%';
     888            $summary_string = '';
     889            if ( !$total_saved_bytes ) {
     890                $summary_string = 'No savings';
     891            } else {
     892                $total_savings = self::formatBytes( $total_saved_bytes );
     893                $detailed_results_html = $this->results_html( $id );
     894                $summary_string = '<div class="kraken-result-wrap">' . "Saved $total_savings_percentage ($total_savings)";
     895                $summary_string .= '<br /><small class="kraken-item-details" data-id="' . $id . '" original-title="' . htmlspecialchars($detailed_results_html) .'">Show details</small></div>';
     896            }
     897            return $summary_string;
     898        }
     899
     900        function results_html( $id ) {
     901
     902            $settings = $this->kraken_settings;
     903            $optimize_main_image = !empty( $settings['optimize_main_image'] );
     904
     905            // get meta data for main post and thumbs
     906            $image_meta = get_post_meta( $id, '_kraken_size', true );
     907            $thumbs_meta = get_post_meta( $id, '_kraked_thumbs', true );
     908            $main_image_optimized = !empty( $image_meta ) && isset( $image_meta['type'] );
     909            $thumbs_optimized = !empty( $thumbs_meta ) && count( $thumbs_meta ) && isset( $thumbs_meta[0]['type'] );
     910
     911            $type = '';
     912            $kraked_size = '';
     913            $savings_percentage = '';
     914
     915            if ( $main_image_optimized ) {
     916                $type = $image_meta['type'];
     917                $kraked_size = isset( $image_meta['kraked_size'] ) ? $image_meta['kraked_size'] : '';
     918                $savings_percentage = $image_meta['savings_percent'];
     919                $main_image_kraked_stats = self::calculate_savings( $image_meta );
     920            }
     921
     922            if ( $thumbs_optimized ) {
     923                $type = $thumbs_meta[0]['type'];
     924                $thumbs_kraked_stats = self::calculate_savings( $thumbs_meta );
     925                $thumbs_count = count( $thumbs_meta );
     926            }
     927           
    571928            ob_start();
    572929            ?>
    573                 <strong><?php echo $kraked_size; ?></strong>
     930                <?php if ( $main_image_optimized ) { ?>
     931                <div class="kraken_detailed_results_wrap">
     932                <span class=""><strong>Main image savings:</strong></span>
    574933                <br />
    575                 <small>Type:&nbsp;<?php echo $type; ?></small>
     934                <span style="display:inline-block;margin-bottom:5px"><?php echo $main_image_kraked_stats['saved_bytes']; ?> (<?php echo $main_image_kraked_stats['savings_percentage']; ?> saved)</span>
     935                <?php } ?>
     936                <?php if ( $main_image_optimized && $thumbs_optimized ) { ?>
    576937                <br />
    577                 <small>Savings:&nbsp;<?php echo $savings_percentage; ?></small>
    578                 <?php if ( !empty( $thumbs_meta ) ) { ?>
     938                <?php } ?>
     939                <?php if ( $thumbs_optimized ) { ?>
     940                    <span><strong>Savings on <?php echo $thumbs_count; ?> thumbnails:</strong></span>
    579941                    <br />
    580                     <small><?php echo $thumbs_count; ?> thumbs optimized</small>
     942                    <span style="display:inline-block;margin-bottom:5px"><?php echo $thumbs_kraked_stats['total_savings']; ?> (<?php echo $thumbs_kraked_stats['savings_percentage']; ?> saved)</span>
    581943                <?php } ?>
     944                <br />
     945                <span><strong>Optimization mode:</strong></span>
     946                <br />
     947                <span><?php echo ucfirst($type); ?></span> 
    582948                <?php if ( !empty( $this->kraken_settings['show_reset'] ) ) { ?>
    583949                    <br />
    584                     <small 
     950                    <small
    585951                        class="krakenReset" data-id="<?php echo $id; ?>"
    586952                        title="Removes Kraken metadata associated with this image">
     
    588954                    </small>
    589955                    <span class="krakenSpinner"></span>
     956                </div>
    590957                <?php } ?>
    591958            <?php   
     
    596963        function fill_media_columns( $column_name, $id ) {
    597964
    598             $original_size = filesize( get_attached_file( $id ) );
    599             $original_size = self::pretty_kb( $original_size );
    600 
    601             $options = get_option( '_kraken_options' );
    602             $type = isset( $options['api_lossy'] ) ? $options['api_lossy'] : 'lossy';
    603 
     965            $settings = $this->kraken_settings;
     966            $optimize_main_image = !empty( $settings['optimize_main_image'] );
     967
     968            $file = get_attached_file( $id );
     969            $original_size = filesize( $file );
     970
     971            // handle the case where file does not exist
     972            if ( $original_size === 0 || $original_size === false ) {
     973                echo '0 bytes';
     974                return;
     975            } else {
     976                $original_size = self::formatBytes( $original_size );               
     977            }
     978           
     979            $type = isset( $settings['api_lossy'] ) ? $settings['api_lossy'] : 'lossy';
    604980
    605981            if ( strcmp( $column_name, 'original_size' ) === 0 ) {
     
    609985
    610986                    if ( isset( $meta['original_size'] ) ) {
    611                         echo $meta['original_size'];
     987
     988                        if ( stripos( $meta['original_size'], 'kb' ) !== false ) {
     989                            echo self::formatBytes( ceil( floatval( $meta['original_size']) * 1024 ) );
     990                        } else {
     991                            echo self::formatBytes( $meta['original_size'] );
     992                        }
     993                       
    612994                    } else {
    613995                        echo $original_size;
     
    617999                }
    6181000            } else if ( strcmp( $column_name, 'kraked_size' ) === 0 ) {
    619 
     1001                echo '<div class="kraken-wrap">';
     1002                $image_url = wp_get_attachment_url( $id );
     1003                $filename = basename( $image_url );
    6201004                if ( wp_attachment_is_image( $id ) ) {
    6211005
    622                     $meta = get_post_meta($id, '_kraken_size', true);
     1006                    $meta = get_post_meta( $id, '_kraken_size', true );
     1007                    $thumbs_meta = get_post_meta( $id, '_kraked_thumbs', true );
    6231008
    6241009                    // Is it optimized? Show some stats
    625                     if ( isset( $meta['kraked_size'] ) && empty( $meta['no_savings'] ) ) {
    626                         echo $this->results_html( $id );
     1010                    if ( ( isset( $meta['kraked_size'] ) && empty( $meta['no_savings'] ) ) || !empty( $thumbs_meta ) ) {
     1011                        if ( !isset( $meta['kraked_size'] ) && $optimize_main_image ) {
     1012                            echo '<div class="buttonWrap"><button data-setting="' . $type . '" type="button" class="kraken_req" data-id="' . $id . '" id="krakenid-' . $id .'" data-filename="' . $filename . '" data-url="' . $image_url . '">Optimize Main Image</button><span class="krakenSpinner"></span></div>';
     1013                        }
     1014                        echo $this->generate_stats_summary( $id );
    6271015
    6281016                    // Were there no savings, or was there an error?
    6291017                    } else {
    630                         $image_url = wp_get_attachment_url( $id );
    631                         $filename = basename( $image_url );
    6321018                        echo '<div class="buttonWrap"><button data-setting="' . $type . '" type="button" class="kraken_req" data-id="' . $id . '" id="krakenid-' . $id .'" data-filename="' . $filename . '" data-url="' . $image_url . '">Optimize This Image</button><span class="krakenSpinner"></span></div>';
    6331019                        if ( !empty( $meta['no_savings'] ) ) {
     
    6411027                    echo 'n/a';
    6421028                }
     1029                echo '</div>';
    6431030            }
    6441031        }
     
    6471034            $rv = false;
    6481035            $ch =  curl_init( $kraked_url );
    649             curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
     1036            curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
    6501037            curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, 0 );
    651             curl_setopt( $ch, CURLOPT_USERAGENT, 'WordPress/' . get_bloginfo('version') . ' KrakenPlugin/' . self::$kraken_plugin_version );     
     1038            curl_setopt( $ch, CURLOPT_SSL_VERIFYHOST, 0 );
     1039            curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT, 0 );
     1040            curl_setopt( $ch, CURLOPT_TIMEOUT, 120 );                     
     1041            curl_setopt( $ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.85 Safari/537.36' );
    6521042            $result = curl_exec( $ch );
    653             $rv = file_put_contents( $image_path, $result );
     1043
     1044            if ( $result ) {
     1045                $rv = file_put_contents( $image_path, $result );
     1046            }
    6541047            return $rv !== false;
    6551048        }
    6561049
    657         function optimize_image( $image_path, $type ) {
     1050        function optimize_image( $image_path, $type, $resize = false ) {
    6581051            $settings = $this->kraken_settings;
    6591052            $kraken = new Kraken( $settings['api_key'], $settings['api_secret'] );
     
    6621055                $lossy = $type === 'lossy';
    6631056            } else {
    664                 $lossy = $settings['api_lossy'] === "lossy";
     1057                $lossy = $settings['api_lossy'] === 'lossy';
    6651058            }
    6661059
    6671060            $params = array(
    668                 "file" => $image_path,
    669                 "wait" => true,
    670                 "lossy" => $lossy,
    671                 "origin" => "wp"
     1061                'file' => $image_path,
     1062                'wait' => true,
     1063                'lossy' => $lossy,
     1064                'origin' => 'wp'
    6721065            );
    6731066
    674             try {
    675                 $data = $kraken->upload( $params );
    676             } catch (Exception $e) {
    677             }
    678 
     1067            $preserve_meta_arr = [];
     1068            if ( $settings['preserve_meta_date'] ) {
     1069                $preserve_meta_arr[] = 'date';
     1070            }
     1071            if ( $settings['preserve_meta_copyright'] ) {
     1072                $preserve_meta_arr[] = 'copyright';
     1073            }
     1074            if ( $settings['preserve_meta_geotag'] ) {
     1075                $preserve_meta_arr[] = 'geotag';
     1076            }
     1077            if ( $settings['preserve_meta_orientation'] ) {
     1078                $preserve_meta_arr[] = 'orientation';
     1079            }
     1080            if ( $settings['preserve_meta_profile'] ) {
     1081                $preserve_meta_arr[] = 'profile';
     1082            }
     1083
     1084            if ( count( $preserve_meta_arr ) ) {
     1085                $params['preserve_meta'] = $preserve_meta_arr;
     1086            }
     1087
     1088            if ( $settings['auto_orient'] ) {
     1089                $params['auto_orient'] = true;
     1090            }
     1091
     1092            if ( $resize ) {
     1093                $width = (int) $settings['resize_width'];
     1094                $height = (int) $settings['resize_height'];
     1095                if ( $width && $height ) {
     1096                    $params['resize'] = array('strategy' => 'auto', 'width' => $width, 'height' => $height, 'enhance' => true );
     1097                } elseif ( $width && !$height ) {
     1098                    $params['resize'] = array('strategy' => 'landscape', 'width' => $width, 'enhance' => true );
     1099                } elseif ( $height && !$width ) {
     1100                    $params['resize'] = array('strategy' => 'portrait', 'height' => $height, 'enhance' => true );
     1101                }
     1102            }
     1103
     1104            if ( isset( $settings['jpeg_quality'] ) && $settings['jpeg_quality'] > 0 ) {
     1105                $params['quality'] = (int) $settings['jpeg_quality'];
     1106            }
     1107           
     1108            set_time_limit(400);
     1109            $data = $kraken->upload( $params );
    6791110            $data['type'] = !empty( $type ) ? $type : $settings['api_lossy'];
    680 
    6811111            return $data;
    6821112        }
    6831113
    6841114        function optimize_thumbnails( $image_data ) {
    685 
     1115           
    6861116            $image_id = $this->id;
    6871117            if ( empty( $image_id ) ) {
     
    7091139
    7101140            if ( !empty( $sizes ) ) {
    711 
    7121141                $thumb_path = '';
    713 
    7141142                $thumbs_optimized_store = array();
    7151143                $this_thumb = array();
     
    7181146
    7191147                    $thumb_path = $upload_full_path . '/' . $size['file'];
    720 
     1148                   
    7211149                    if ( file_exists( $thumb_path ) !== false ) {
    722 
    7231150                        $result = $this->optimize_image( $thumb_path, $this->optimization_type );
    724 
    725                         if ( !empty($result) && isset($result['success']) && isset( $result['kraked_url'] ) ) {
    726                             $kraked_url = $result["kraked_url"];
    727                             if ( $this->replace_image( $thumb_path, $kraked_url ) ) {
    728                                 $this_thumb = array( 'thumb' => $key, 'file' => $size['file'], 'original_size' => $result['original_size'], 'kraked_size' => $result['kraked_size'], 'type' => $this->optimization_type );
    729                                 $thumbs_optimized_store [] = $this_thumb;
     1151                        if ( !empty( $result ) && isset( $result['success'] ) && isset( $result['kraked_url'] ) ) {
     1152                            $kraked_url = $result['kraked_url'];
     1153                            if ( (int) $result['saved_bytes'] !== 0 ) {
     1154                                if ( $this->replace_image( $thumb_path, $kraked_url ) ) {
     1155                                    $this_thumb = array( 'thumb' => $key, 'file' => $size['file'], 'original_size' => $result['original_size'], 'kraked_size' => $result['kraked_size'], 'type' => $this->optimization_type );
     1156                                    $thumbs_optimized_store [] = $this_thumb;
     1157                                }
     1158                            } else {
     1159                                $this_thumb = array( 'thumb' => $key, 'file' => $size['file'], 'original_size' => $result['original_size'], 'kraked_size' => $result['original_size'], 'type' => $this->optimization_type );
     1160                                $thumbs_optimized_store [] = $this_thumb;                               
    7301161                            }
     1162                        } else {
     1163                            return $image_data;
    7311164                        }
    7321165                    }
    7331166                }
    7341167            }
     1168
     1169            $kraken_meta = get_post_meta( $image_id, '_kraken_size', true );
     1170            $image_backup_path = isset( $kraken_meta['optimized_backup_file'] ) ? $kraken_meta['optimized_backup_file'] : '';
     1171           
     1172            if ( $image_backup_path ) {
     1173                $original_image_path = get_attached_file( $image_id );             
     1174                if ( copy( $image_backup_path, $original_image_path ) ) {
     1175                    unlink( $image_backup_path );
     1176                    unset( $kraken_meta['optimized_backup_file'] );
     1177                    update_post_meta( $image_id, '_kraken_size', $kraken_meta );
     1178                }
     1179            }
     1180
     1181            // when resizing has taken place via API, update the post metadata accordingly
     1182            if ( !empty( $kraken_meta['kraked_width'] ) && !empty( $kraken_meta['kraked_height'] ) ) {
     1183                $image_data['width'] = $kraken_meta['kraked_width'];
     1184                $image_data['height'] = $kraken_meta['kraked_height'];
     1185            }
     1186           
    7351187            if ( !empty( $thumbs_optimized_store ) ) {
    7361188                update_post_meta( $image_id, '_kraked_thumbs', $thumbs_optimized_store, false );
     
    7391191        }
    7401192
    741 
    742         static function pretty_kb( $bytes ) {
    743             return round( ( $bytes / 1024 ), 2 ) . ' kB';
     1193        static function formatBytes( $size, $precision = 2 ) {
     1194            $base = log( $size, 1024 );
     1195            $suffixes = array( ' bytes', 'KB', 'MB', 'GB', 'TB' );   
     1196            return round( pow( 1024, $base - floor( $base ) ), $precision ) . $suffixes[floor( $base )];
    7441197        }
    7451198    }
  • kraken-image-optimizer/trunk/lib/Kraken.php

    r1144921 r1351884  
    11<?php
    22
    3 class Kraken
    4 {
     3class Kraken {
     4    protected $auth = array();
     5    public static $kraken_plugin_version = '2.5.0';
    56
    6     protected $auth = array();
    7     public static $kraken_plugin_version = '2.0.0';
    8 
    9     public function __construct($key = '', $secret = '')
    10     {
     7    public function __construct($key = '', $secret = '') {
    118        $this->auth = array(
    129            "auth" => array(
     
    1714    }
    1815
    19     public function url($opts = array())
    20     {
     16    public function url($opts = array()) {
    2117        $data = json_encode(array_merge($this->auth, $opts));
    22         $response = self::request($data, "https://api.kraken.io/v1/url");
     18        $response = self::request($data, 'https://api.kraken.io/v1/url', 'url');
    2319
    2420        return $response;
    2521    }
    2622
    27     public function upload($opts = array())
    28     {
    29         if (!isset($opts['file']))
    30         {
     23    public function upload($opts = array()) {
     24        if (!isset($opts['file'])) {
    3125            return array(
    3226                "success" => false,
     
    3529        }
    3630
    37         if (preg_match("/\/\//i", $opts['file']))
    38         {
     31        if (preg_match("/\/\//i", $opts['file'])) {
    3932            $opts['url'] = $opts['file'];
    4033            unset($opts['file']);
     34
    4135            return $this->url($opts);
    4236        }
    4337
    44         if (!file_exists($opts['file']))
    45         {
     38        if (!file_exists($opts['file'])) {
    4639            return array(
    4740                "success" => false,
    48                 "error" => "File `" . $opts['file'] . "` does not exist"
     41                "error" => 'File `' . $opts['file'] . '` does not exist'
    4942            );
    5043        }
    5144
    52         if (function_exists('curl_file_create')) {
    53             $file = curl_file_create($opts['file'], 'image/jpeg', $opts['file']);
     45        if (class_exists('CURLFile')) {
     46            $file = new CURLFile($opts['file']);
    5447        } else {
    55             $file = sprintf('@%s', $opts['file']);
     48            $file = '@' . $opts['file'];
    5649        }
    5750
     
    6053        $data = array_merge(array(
    6154            "file" => $file,
    62             "data" => json_encode(array_merge(
    63                             $this->auth, $opts
    64             ))
     55            "data" => json_encode(array_merge($this->auth, $opts))
    6556        ));
    66 
    67         $response = self::request($data, "https://api.kraken.io/v1/upload");
     57        $response = self::request($data, 'https://api.kraken.io/v1/upload', 'upload');
    6858
    6959        return $response;
    7060    }
    7161
    72     public function status()
    73     {
     62    public function status() {
    7463        $data = array('auth' => array(
    7564            'api_key' => $this->auth['auth']['api_key'],
    7665            'api_secret' => $this->auth['auth']['api_secret']
    7766        ));
    78         $response = self::request(json_encode($data), "https://api.kraken.io/user_status");
    79 
     67        $response = self::request(json_encode($data), 'https://api.kraken.io/user_status', 'url');
    8068        return $response;
    8169    }
    8270
    83     private function request($data, $url)
    84     {
    85         $curl = curl_init();
    86        
    87         curl_setopt($curl, CURLOPT_URL, $url);
     71    private function request($data, $url, $type) {
     72        $curl = curl_init($url);
     73
     74        if ($type === 'url') {
     75            curl_setopt($curl, CURLOPT_HTTPHEADER, array(
     76                'Content-Type: application/json'
     77            ));
     78        }
     79
     80        // Force continue-100 from server
     81        curl_setopt($curl, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.85 Safari/537.36');
     82        curl_setopt($curl, CURLOPT_HTTPHEADER, array('Expect:'));
    8883        curl_setopt($curl, CURLOPT_POST, 1);
    8984        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
     
    9287        curl_setopt($curl, CURLOPT_TIMEOUT, 400);       
    9388        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
    94         curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0);     
    95         curl_setopt($curl, CURLOPT_USERAGENT, 'WordPress/' . get_bloginfo('version') . ' KrakenPlugin/' . self::$kraken_plugin_version);     
    96         curl_setopt($curl, CURLOPT_FAILONERROR, 1);
     89        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0);
    9790
    9891        $response = json_decode(curl_exec($curl), true);
    9992        $error = curl_errno($curl);
    10093
     94        if ($response === null) {
     95            $error_code = (int) curl_error($curl);
     96            $response = array (
     97                "success" => false,
     98                "error" => 'cURL Error: ' . $error_code,
     99                "code" => $error_code
     100            );
     101        }
    101102        curl_close($curl);
    102 
    103         if ($error > 0) {
    104             throw new RuntimeException(sprintf('cURL returned with the following error code: "%s"', $error));
    105         }
    106 
    107103        return $response;
    108104    }
    109 
    110105}
  • kraken-image-optimizer/trunk/readme.txt

    r1144921 r1351884  
    11=== Kraken Image Optimizer ===
    22Contributors: karim79
    3 Tags: Image Optimizer, Image Optimiser, Optimize, Optimise, Images, Media, Performance, SEO, faster loading times, smushit, smush.it, compress, kraken-image-optimizer, tinypng, tinyjpeg, pngquant, jpegmini, ewww, pagespeed, pagespeed insights, sitespeed, optimize gif, optimize jpeg, optimize png, optimize animated gif, svg, improve pagerank, gtmetrix speed test
     3Tags: Image Optimizer, Image Optimiser, Optimize, Optimise, Images, Media, Performance, SEO, faster loading times, smushit, smush.it, compress, kraken-image-optimizer, tinypng, tinyjpeg, pngquant, jpegmini, ewww, pagespeed, pagespeed insights, sitespeed, optimize gif, optimize jpeg, optimize png, optimize animated gif, svg, improve pagerank, gtmetrix speed test, EXIF, image resize
    44Requires at least: 3.0.1
    5 Tested up to: 4.2
     5Tested up to: 4.4.2
    66Donate link: https://kraken.io
    7 Stable tag: 2.0.0
     7Stable tag: 2.5.0
    88License: GPLv2 or later
    99License URI: http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
    1010
    1111
    12 This plugin allows you to optimize your WordPress images through the Kraken API, the world's most advanced image optimization solution.
     12This plugin allows you to optimize your WordPress images through the Kraken API, the world's most advanced image optimization and resizing API.
    1313
    1414== Description ==
    1515
    16 This plugin allows you to optimize new and existing Wordpress image uploads through [Kraken Image Optimizer's](https://kraken.io "Kraken Image Optimizer") API. Both lossless and intelligent lossy optimization modes are supported. Supported filetypes are JPEG, PNG and GIF. Maximum filesize limit is 16 MB. For more details, including detailed documentation and plans and pricing, please visit [Kraken.io](https://kraken.io "Kraken Image Optimizer").
     16This plugin allows you to optimize and resize new and existing Wordpress image uploads through [Kraken Image Optimizer's](https://kraken.io "Kraken Image Optimizer") API. Both lossless and intelligent lossy optimization modes are supported. Supported filetypes are JPEG, PNG and GIF (including animated). Maximum filesize limit is 32 MB. For more details, including detailed documentation and plans and pricing, please visit [Kraken.io](https://kraken.io "Kraken Image Optimizer"). Even when using Kraken.io's lossy optimization, our system goes the extra mile to ensure that the results are of high quality, every time. You can just install the plugin and stop worrying.
    1717
    1818> **Get your FREE account with us**
     
    3131* You can use your Kraken API key and secret on as many sites/blogs as you like. We have no per-site license.
    3232* All images uploaded throught the media uploader are optimized on-the-fly. All generated thumbnails are optimized too.
     33* The main image upload can be optionally resized - this is useful for preventing user uploads with unnecessarily large dimensions. You can specify the maximum width and/or height in Kraken.io->Settings.
     34* When restricting the maximum dimensions of the main image using the resizing feature, the resulting image is **enhanced** using various advanced techniques, to help prevent downsample artifacts and "haloing" and produce a sharper result.
     35* You can optionally preserve one or more of the Date, Copyright, Geotag, Orientation, Profile EXIF metadata tags.
     36* Images can be automatically oriented according to their EXIF Orientation value - no need to manually rotate images.
    3337* All images already present in the media library can be optimized individually, or using the Bulk Action menu "Krak 'em all" feature.
    3438* This plugin does not require any root or command-line access. No compilation and installation of any binaries is necessary.
     
    5357For advanced users, there is a third party WordPress Command Line Interface (CLI) tool to allow image optimization from the command line, or by using cron. For details, visit: https://github.com/tillkruss/wp-cli-kraken
    5458
    55 = Features on the way =
    56 * Optimize images directly to Amazon S3.
    57 * Optimize entire media library in one click.
    58 * Optimize your currently active theme.
    59 * WordPress Multisite support.
    60 
    6159Please send bug reports, problems, feature requests and so on to support (at) Kraken dot io, or directly to the author of this plugin.
    6260
     
    6664* [Google+](https://plus.google.com/107209047753760492207/ "Google+")
    6765* [Facebook](https://www.facebook.com/krakenio "Kraken Image Optimizer")
     66* [Github](https://github.com/kraken-io "Kraken.io on Github")
    6867
    6968== Installation ==
     
    103102You will need to switch the Media Library from the Thumbnail view to the List view. In the "Kraked Size" column, you will then see the "Optimize This Image" button for unoptimized images, or the results of the optimization where the image has already been optimized by our plugin.
    104103
    105 
    106104== Changelog ==
     105
     106= 2.5.0 =
     107* Ability to disable optimization of main image, allowing faster uploads from Media Library. You can optimize the main image later from within your Media Library.
     108* Ability to restrict the maximum dimensions of image uploads (resizing), by width and/or height.
     109* When using resize feature, resized images are enhanced for sharper results using various advanced techniques.
     110* Ability to force JPEG quality to a discrete "quality" value, for greater savings if you know what you're doing.
     111* Ability to preserve certain EXIF metadata tags, including Date, Copyright, Orientation, Geotag and Profile.
     112* Ability to automatically orient images according to their Orientation EXIF metadata.
     113* Improvements and simplifications to interface elements and Kraken.io Settings page.
    107114
    108115= 2.0.0 =
Note: See TracChangeset for help on using the changeset viewer.