From 6c94a20220ca3972599e5dfd3dd75d70bd3daf0c Mon Sep 17 00:00:00 2001 From: ultimatemember Date: Mon, 29 Dec 2014 15:51:55 +0200 Subject: [PATCH] Mobile work and profile settings --- admin/core/um-admin-actions-fields.php | 3 +- admin/core/um-admin-builder.php | 2 +- admin/core/um-admin-metabox.php | 6 +- admin/templates/form/profile_settings.php | 60 + assets/css/um-crop.css | 282 +++- assets/css/um-fileupload.css | 53 +- assets/css/um-members.css | 6 + assets/css/um-modal.css | 3 +- assets/css/um-profile.css | 128 +- assets/css/um-responsive.css | 335 ++++- assets/css/um-styles.css | 73 +- assets/dynamic_css/dynamic_profile.php | 24 +- assets/img/Dollarphotoclub_57189843.jpg | Bin 30265 -> 0 bytes assets/img/best-hd-wallpapers-2.jpg | Bin 86357 -> 0 bytes assets/js/um-crop.js | 1570 ++++++++++++++++++++- assets/js/um-functions.js | 120 +- assets/js/um-modal.js | 232 ++- assets/js/um-profile.js | 52 + assets/js/um-responsive.js | 84 +- assets/js/um-scripts.js | 138 +- core/lib/mobiledetect/Mobile_Detect.php | 1341 ++++++++++++++++++ core/um-actions-ajax.php | 65 + core/um-actions-members.php | 8 +- core/um-actions-profile.php | 117 +- core/um-builtin.php | 25 + core/um-fields.php | 166 ++- core/um-files.php | 211 ++- core/um-filters-arguments.php | 17 +- core/um-filters-fields.php | 4 + core/um-filters-members.php | 19 - core/um-menu.php | 33 + core/um-profile.php | 29 + core/um-setup.php | 1 + core/um-short-functions.php | 104 +- core/um-uninstall.php | 14 +- templates/members.php | 2 - templates/modal/um_upload_image.php | 3 - templates/modal/um_upload_single.php | 3 + um-config.php | 39 +- um-init.php | 7 + 40 files changed, 4993 insertions(+), 386 deletions(-) create mode 100644 admin/templates/form/profile_settings.php delete mode 100644 assets/img/Dollarphotoclub_57189843.jpg delete mode 100644 assets/img/best-hd-wallpapers-2.jpg create mode 100644 core/lib/mobiledetect/Mobile_Detect.php create mode 100644 core/um-menu.php create mode 100644 core/um-profile.php delete mode 100644 templates/modal/um_upload_image.php create mode 100644 templates/modal/um_upload_single.php diff --git a/admin/core/um-admin-actions-fields.php b/admin/core/um-admin-actions-fields.php index 9bdec78d..e6dcaedd 100644 --- a/admin/core/um-admin-actions-fields.php +++ b/admin/core/um-admin-actions-fields.php @@ -27,9 +27,10 @@ $save = ''; $save[ $_metakey ]['roles'] = ''; + $save[ $_metakey ]['icon'] = ''; foreach( $array['post'] as $key => $val){ - if ( substr( $key, 0, 1) === '_' && $val != '' ) { // field attribute + if ( substr( $key, 0, 1) === '_' && $val != '' ) { // field attribute $new_key = ltrim ($key,'_'); if ( $new_key == 'options' ) { diff --git a/admin/core/um-admin-builder.php b/admin/core/um-admin-builder.php index 0b77497f..17fb3928 100644 --- a/admin/core/um-admin-builder.php +++ b/admin/core/um-admin-builder.php @@ -225,7 +225,7 @@ class UM_Admin_Builder {
- +
diff --git a/admin/core/um-admin-metabox.php b/admin/core/um-admin-metabox.php index e88abf58..61b24f94 100644 --- a/admin/core/um-admin-metabox.php +++ b/admin/core/um-admin-metabox.php @@ -245,7 +245,8 @@ class UM_Admin_Metabox { add_meta_box('um-admin-form-profile_customize', __('Customize this form'), array(&$this, 'load_metabox_form'), 'um_form', 'side', 'default'); add_meta_box('um-admin-form-login_customize', __('Customize this form'), array(&$this, 'load_metabox_form'), 'um_form', 'side', 'default'); - add_meta_box('um-admin-form-login_settings', __('Settings'), array(&$this, 'load_metabox_form'), 'um_form', 'side', 'default'); + add_meta_box('um-admin-form-profile_settings', __('Options'), array(&$this, 'load_metabox_form'), 'um_form', 'side', 'default'); + add_meta_box('um-admin-form-login_settings', __('Options'), array(&$this, 'load_metabox_form'), 'um_form', 'side', 'default'); } @@ -333,6 +334,7 @@ class UM_Admin_Metabox { $wpdb->update( $wpdb->posts, array( 'post_title' => $_POST['post_title'] ), $where ); // save + delete_post_meta( $post_id, '_um_profile_metafields' ); foreach( $_POST as $k => $v ) { if (strstr($k, '_um_')){ update_post_meta( $post_id, $k, $v); @@ -968,7 +970,7 @@ class UM_Admin_Metabox {

diff --git a/admin/templates/form/profile_settings.php b/admin/templates/form/profile_settings.php new file mode 100644 index 00000000..dfcf8d32 --- /dev/null +++ b/admin/templates/form/profile_settings.php @@ -0,0 +1,60 @@ +
+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ +

+ + + + ui_on_off('_um_profile_show_bio', 1 ); ?> + + +

+ +
\ No newline at end of file diff --git a/assets/css/um-crop.css b/assets/css/um-crop.css index c5b4fce1..0440afcf 100644 --- a/assets/css/um-crop.css +++ b/assets/css/um-crop.css @@ -1,41 +1,271 @@ -/* - * imgAreaSelect default style - */ +.cropper-container { + position: relative; + overflow: hidden; -.imgareaselect-border1 { - background: url(../img/border-v.gif) repeat-y left top; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + + -webkit-tap-highlight-color: transparent; + -webkit-touch-callout: none; } -.imgareaselect-border2 { - background: url(../img/border-h.gif) repeat-x left top; +.cropper-container img { + width: 100%; + height: 100%; + min-width: 0 !important; + min-height: 0 !important; + max-width: none !important; + max-height: none !important; } -.imgareaselect-border3 { - background: url(../img/border-v.gif) repeat-y right top; +.cropper-modal, +.cropper-canvas { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; } -.imgareaselect-border4 { - background: url(../img/border-h.gif) repeat-x left bottom; +.cropper-canvas { + background-color: #fff; + opacity: 0; + filter: alpha(opacity=0); } -.imgareaselect-border1, .imgareaselect-border2, -.imgareaselect-border3, .imgareaselect-border4 { - filter: alpha(opacity=50); - opacity: 0.5; +.cropper-modal { + background-color: #000; + opacity: .5; + filter: alpha(opacity=50); } -.imgareaselect-handle { - background-color: #fff; - border: solid 1px #000; - filter: alpha(opacity=50); - opacity: 0.5; +.cropper-dragger { + position: absolute; + top: 10%; + left: 10%; + width: 80%; + height: 80%; } -.imgareaselect-outer { - background-color: #000; - filter: alpha(opacity=50); - opacity: 0.5; +.cropper-viewer { + display: block; + width: 100%; + height: 100%; + overflow: hidden; + outline-width: 1px; + outline-style: solid; + outline-color: #69f; + outline-color: rgba(51, 102, 255, .75); } -.imgareaselect-selection { -} \ No newline at end of file +.cropper-dashed { + position: absolute; + display: block; + border: 0 dashed #fff; + opacity: .5; + filter: alpha(opacity=50); +} + +.cropper-dashed.dashed-h { + top: 33.3%; + left: 0; + width: 100%; + height: 33.3%; + border-top-width: 1px; + border-bottom-width: 1px; +} + +.cropper-dashed.dashed-v { + top: 0; + left: 33.3%; + width: 33.3%; + height: 100%; + border-right-width: 1px; + border-left-width: 1px; +} + +.cropper-face, +.cropper-line, +.cropper-point { + position: absolute; + display: block; + width: 100%; + height: 100%; + opacity: .1; + filter: alpha(opacity=10); +} + +.cropper-face { + top: 0; + left: 0; + cursor: move; + background-color: #fff; +} + +.cropper-line { + background-color: #69f; +} + +.cropper-line.line-e { + top: 0; + right: -3px; + width: 5px; + cursor: e-resize; +} + +.cropper-line.line-n { + top: -3px; + left: 0; + height: 5px; + cursor: n-resize; +} + +.cropper-line.line-w { + top: 0; + left: -3px; + width: 5px; + cursor: w-resize; +} + +.cropper-line.line-s { + bottom: -3px; + left: 0; + height: 5px; + cursor: s-resize; +} + +.cropper-point { + width: 5px; + height: 5px; + background-color: #69f; + opacity: .75; + filter: alpha(opacity=75); +} + +.cropper-point.point-e { + top: 50%; + right: -3px; + margin-top: -3px; + cursor: e-resize; +} + +.cropper-point.point-n { + top: -3px; + left: 50%; + margin-left: -3px; + cursor: n-resize; +} + +.cropper-point.point-w { + top: 50%; + left: -3px; + margin-top: -3px; + cursor: w-resize; +} + +.cropper-point.point-s { + bottom: -3px; + left: 50%; + margin-left: -3px; + cursor: s-resize; +} + +.cropper-point.point-ne { + top: -3px; + right: -3px; + cursor: ne-resize; +} + +.cropper-point.point-nw { + top: -3px; + left: -3px; + cursor: nw-resize; +} + +.cropper-point.point-sw { + bottom: -3px; + left: -3px; + cursor: sw-resize; +} + +.cropper-point.point-se { + right: -3px; + bottom: -3px; + width: 20px; + height: 20px; + cursor: se-resize; + opacity: 1; + filter: alpha(opacity=100); +} + +.cropper-point.point-se:before { + position: absolute; + right: -50%; + bottom: -50%; + display: block; + width: 200%; + height: 200%; + content: " "; + background-color: #69f; + opacity: 0; + filter: alpha(opacity=0); +} + +@media (min-width: 768px) { + .cropper-point.point-se { + width: 15px; + height: 15px; + } +} + +@media (min-width: 992px) { + .cropper-point.point-se { + width: 10px; + height: 10px; + } +} + +@media (min-width: 1200px) { + .cropper-point.point-se { + width: 5px; + height: 5px; + opacity: .75; + filter: alpha(opacity=75); + } +} + +/* Helper classes for JavaScript */ + +.cropper-hidden { + display: none !important; +} + +.cropper-invisible { + position: fixed; + top: 0; + left: 0; + z-index: -1; + width: auto !important; + max-width: none !important; + height: auto !important; + max-height: none !important; + opacity: 0; + filter: alpha(opacity=0); +} + +.cropper-move { + cursor: move; +} + +.cropper-crop { + cursor: crosshair; +} + +.cropper-disabled .cropper-canvas, +.cropper-disabled .cropper-face, +.cropper-disabled .cropper-line, +.cropper-disabled .cropper-point { + cursor: not-allowed; +} diff --git a/assets/css/um-fileupload.css b/assets/css/um-fileupload.css index 2d677d3d..1437fb01 100644 --- a/assets/css/um-fileupload.css +++ b/assets/css/um-fileupload.css @@ -7,14 +7,24 @@ margin-bottom: 20px; } +.um .um-single-image-preview > img { + max-height: 200px; + display: block; + overflow: hidden; + margin: auto; + border-radius: 3px; +} + .um-single-image-preview, .um-single-file-preview { display: none; - width: 100%; - box-sizing: border-box; text-align: center; position: relative; - min-height: 40px; +} + +.um-single-image-preview > img { + max-width: 100%; + max-height: inherit; } .um-single-image-preview a.cancel, @@ -27,8 +37,9 @@ border-radius: 999px; background: #ddd; cursor: pointer; - transition: 0.2s; text-decoration: none !important; + z-index: 8888; + opacity: 0.75; } .um-single-image-preview a.cancel i, @@ -39,44 +50,40 @@ top: 4px; } -.um-single-image-preview img { - max-width: 100%; - margin: 0 auto !important; - padding: 0 !important; - display: block !important; -} - /* - - file preview + - file info */ .um-single-fileinfo { - padding: 36px 0 30px 0; + padding: 26px 0 20px 0; display: inline-block; } .um-single-fileinfo span.icon { - font-size: 38px; + font-size: 42px; display: inline-block; width: 100px; height: 70px; - line-height: 70px; - border-radius: 5px; + border-radius: 3px; background-color: #666; color: #fff !important; } -.um-single-fileinfo span.icon i {line-height: 1em !important;vertical-align: middle} +.um-single-fileinfo span.icon i{vertical-align: middle} .um-single-fileinfo span.filename { display: block; - font-size: 18px; - line-height: 24px; - color: #666; + font-size: 16px; + color: #888; padding: 15px 0 0 0; } -.um-single-fileinfo a {text-decoration: none !important; opacity: 0.85 } -.um-single-fileinfo a:hover { opacity: 1 } +.um-single-fileinfo a { + text-decoration: none !important; + opacity: 0.9; +} +.um-single-fileinfo a:hover { + opacity: 1 +} /* - upload progress @@ -122,7 +129,7 @@ .ajax-upload-dragdrop span.str { display: block; - margin: 0 0 12px 0; + margin: 0 0 15px 0; } .ajax-upload-dragdrop span.icon { diff --git a/assets/css/um-members.css b/assets/css/um-members.css index ffaeb7fd..9c20cd3c 100644 --- a/assets/css/um-members.css +++ b/assets/css/um-members.css @@ -111,8 +111,14 @@ .um-gutter-sizer { width: 5% } +/* + - Member cover +*/ + .um-member-cover { background-color: #eee; + box-sizing: border-box; + position: relative; } .um-member-cover-e { diff --git a/assets/css/um-modal.css b/assets/css/um-modal.css index 1bb34bf2..729fb51a 100644 --- a/assets/css/um-modal.css +++ b/assets/css/um-modal.css @@ -64,7 +64,7 @@ background: #3ba1da; padding: 0 20px; box-sizing: border-box; - font-size: 15px; + font-size: 17px; } .um-modal-footer { @@ -93,6 +93,7 @@ display: inline-block; border-radius: 3px; padding: 0 20px; + text-align: center; color: #fff; background: #3ba1da; vertical-align: middle; diff --git a/assets/css/um-profile.css b/assets/css/um-profile.css index fb22a3ee..fd3f40d2 100644 --- a/assets/css/um-profile.css +++ b/assets/css/um-profile.css @@ -4,6 +4,19 @@ .um-profile { box-sizing: border-box; + padding-top: 10px; +} + +.um-profile .um-cover-overlay { + display: none; +} + +.um-profile.um-editing .um-cover.has-cover .um-cover-overlay { + display: block; +} + +.um-profile.um-viewing .um-profile-photo-overlay { + display: none; } .um-profile img {display: block;overflow:hidden;border-radius:0!important;margin: 0!important} @@ -33,21 +46,11 @@ - Profile cover */ -.um-cover{ +.um-cover { background-color: #eee; -} - -.um-cover-add span { - display: table-cell; - vertical-align: middle; -} - -.um-cover-e { - text-align: center; box-sizing: border-box; + position: relative; } -.um-cover-e.empty {border: 2px dashed #ddd} -.um-cover-e img {width: 100% !important} .um-cover-add { color: #aaa; @@ -55,8 +58,61 @@ width: 100%; display: table; } + +.um-cover-add span { + display: table-cell; + vertical-align: middle; +} + .um-cover-add:hover { color: #3ba1da } +.um-cover-e { + text-align: center; + box-sizing: border-box; +} + +.um-cover-e img {width: 100% !important} + +.um-cover-overlay { + position: absolute; + top: 0; + left: 0; + background: rgba(0,0,0,.5); + text-align: center; + box-sizing: border-box; + padding: 0 40px; + color: #fff; + text-shadow: 0 2px #333; + cursor: pointer !important; + line-height: 21px; + font-size: 16px; + height: 100%; + width: 100%; +} + +.um-cover-overlay:hover { + background: rgba(0,0,0,.55); +} + +.um-cover-overlay span.um-cover-overlay-s { + display: table; + height: 100%; + width: 100%; +} + +.um-cover-overlay ins { + display: table-cell; + vertical-align: middle; + height: 100%; + text-decoration: none !important; +} + +.um-cover-overlay i { + font-size: 46px; + display: block; + margin-bottom: 6px; +} + /* - Profile header */ @@ -70,7 +126,7 @@ .um-profile-edit { position: absolute; top: 15px; - right: 15px; + right: 0; vertical-align: middle; z-index: 99; font-size: 30px; @@ -124,7 +180,6 @@ .um-profile-photo { float: left; - width: 250px; position: relative; } @@ -136,11 +191,51 @@ left: 30px; } -.um-profile-photo img { +.um-profile-photo a.um-profile-photo-img img { width: 100%; height: 100%; } +.um-profile-photo-overlay { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0,0,0,.5); + text-align: center; + box-sizing: border-box; + padding: 0 40px; + color: #fff; + line-height: 21px; + font-size: 16px; + text-shadow: 0 1px #333; + cursor: pointer !important; +} + +.um-profile-photo-overlay:hover { + background: rgba(0,0,0,.55); +} + +.um-profile-photo-overlay span.um-profile-photo-overlay-s { + display: table; + height: 100%; + width: 100%; +} + +.um-profile-photo-overlay ins { + display: table-cell; + vertical-align: middle; + height: 100%; + text-decoration: none !important; +} + +.um-profile-photo-overlay i { + font-size: 46px; + display: block; + margin-bottom: 6px; +} + /* - Profile meta */ @@ -164,12 +259,13 @@ font-size: 15px; } +.um-meta span:first-child {margin: 0 5px 0 0} .um-meta span { margin: 0 5px; font-size: 14px; } .um-meta-text { - margin: 12px 0 0 0; + margin: 6px 0 0 0; color: #666; } \ No newline at end of file diff --git a/assets/css/um-responsive.css b/assets/css/um-responsive.css index a9240ae3..6c28150a 100644 --- a/assets/css/um-responsive.css +++ b/assets/css/um-responsive.css @@ -28,22 +28,31 @@ div.uimob960 .uimob960-hide /********************************************/ -.uimob340 .um-half { - float: none; - width: 100%; - margin: 0 0 10px 0; +div.uimob340 .um-cover-overlay span.um-cover-overlay-t, +div.uimob340 .um-profile-photo-overlay span.um-profile-photo-overlay-t +{ + display: none !important; } -.uimob340 .um-button.um-btn-auto-width { - min-width: auto; - max-width: auto; - width: 100%; +div.uimob340 .um-cover-overlay, +div.uimob340 .um-profile-photo-overlay +{ + padding: 0; +} + +div.uimob340 .um-cover-overlay i, +div.uimob340 .um-profile-photo-overlay i +{ + margin-bottom: 0; + font-size: 30px; } .um-modal.uimob340 { width: 100% !important; margin: 0 !important; left: 0 !important; + height: 100%; + background: #fff; } .um-modal.uimob340, @@ -52,16 +61,181 @@ div.uimob960 .uimob960-hide border-radius: 0; } +.um-modal.uimob340 .um-modal-header {text-align: center} +.um-modal.uimob340 .um-modal-footer { + position: absolute; + bottom: 0; + left: 0; + width: 100%; + box-sizing: border-box; + padding: 10px; + border-top: 1px solid #ddd; +} + +.um-modal.uimob340 .um-modal-right { + width: 100%; + float: none; +} + +.um-modal.uimob340 .um-modal-footer a.um-modal-btn { + width: 48%; + margin: 0 1%; + float: left; + display: block; + line-height: 44px !important; + height: 44px; +} + +.um-modal.uimob340 .ajax-upload-dragdrop span.icon { + font-size: 40px; + line-height: 40px; +} + +div.uimob340 .um-profile-body { + padding-left: 0; + padding-right: 0; +} + +div.uimob340 .um-profile-photo { + width: 100% !important; + text-align: center !important; + position: absolute; + top: -20px; + left: 0; +} + +div.uimob340 .um-profile-photo a.um-profile-photo-img { + width: 100px !important; + height: 100px !important; + margin: 0 auto !important; + float: none !important; + display: inline-block !important; + position: relative !important; + top: auto !important; + left: auto !important; +} + +div.uimob340 .um-header .um-profile-meta { + padding-left: 0 !important; + padding-right: 0 !important; + text-align: center !important; + padding-top: 90px !important; +} + +div.uimob340 .um-header .um-name { + float: none !important; + margin-right: 0 !important; + font-size: 18px; +} + +div.uimob340 .um-header .um-meta-text { + padding: 0 20px; +} + +div.uimob340 .um-header .um-meta {padding: 0 20px} +div.uimob340 .um-header .um-meta span:first-child {display: block} +div.uimob340 .um-header .um-meta span {display: none} + +div.uimob340 .um-col-121, +div.uimob340 .um-col-122, +div.uimob340 .um-col-131, +div.uimob340 .um-col-132, +div.uimob340 .um-col-133 { + float: none; + width: 100%; + margin: 0; +} + +div.uimob340 .um-field-half { + float: none; + width: 100%; +} + +div.uimob340 .um-search { + padding: 20px 0 !important; + width: 100% !important; + box-sizing: border-box; +} + +div.uimob340 .um-search .um-search-filter { + width: 100% !important; + float: none !important; + padding: 0 0 15px 0 !important; +} + +div.uimob340 .um-search .um-search-submit .um-button { + display: block !important; + min-width: 100% !important; + margin: 0 0 15px 0 !important; +} + +div.uimob340 .um-member { + width: 100%; +} + +div.uimob340 .um-gutter-sizer { width: 0px } + +div.uimob340 .um-account-main { + float: none; + width: 100%; + padding: 0; + box-sizing: border-box; +} + +div.uimob340 .um-account-tab { + padding-bottom: 20px; + border-bottom: 1px solid #eee; +} + +div.uimob340 .um-account-main div.um-field {padding-top: 15px} + +div.uimob340 .um-account-meta { + margin-bottom: 0; +} + +.uimob340 .um-half { + float: none; + width: 100%; + margin: 0 0 10px 0; +} + +.uimob340 .um-button.um-btn-auto-width { + min-width: auto; + max-width: auto; + width: 100%; +} + /** for 500px and below **/ /********************************************/ +div.uimob500 .um-cover-overlay span.um-cover-overlay-t, +div.uimob500 .um-profile-photo-overlay span.um-profile-photo-overlay-t +{ + display: none !important; +} + +div.uimob500 .um-cover-overlay, +div.uimob500 .um-profile-photo-overlay +{ + padding: 0; +} + +div.uimob500 .um-cover-overlay i, +div.uimob500 .um-profile-photo-overlay i +{ + margin-bottom: 0; + font-size: 30px; +} + .um-modal.uimob500 { width: 100% !important; margin: 0 !important; left: 0 !important; + height: 100%; + background: #fff; } .um-modal.uimob500, @@ -70,15 +244,41 @@ div.uimob960 .uimob960-hide border-radius: 0; } +.um-modal.uimob500 .um-modal-header {text-align: center} +.um-modal.uimob500 .um-modal-footer { + position: absolute; + bottom: 0; + left: 0; + width: 100%; + box-sizing: border-box; + padding: 10px; + border-top: 1px solid #ddd; +} + +.um-modal.uimob500 .um-modal-right { + width: 100%; + float: none; +} + +.um-modal.uimob500 .um-modal-footer a.um-modal-btn { + width: 48%; + margin: 0 1%; + float: left; + display: block; + line-height: 44px !important; + height: 44px; +} + +.um-modal.uimob500 .ajax-upload-dragdrop span.icon { + font-size: 40px; + line-height: 40px; +} + div.uimob500 .um-profile-body { padding-left: 0; padding-right: 0; } -div.uimob500 .um-profile-edit { - right: 0; -} - div.uimob500 .um-profile-photo { width: 100% !important; text-align: center !important; @@ -102,7 +302,7 @@ div.uimob500 .um-header .um-profile-meta { padding-left: 0 !important; padding-right: 0 !important; text-align: center !important; - padding-top: 70px !important; + padding-top: 80px !important; } div.uimob500 .um-header .um-name { @@ -112,13 +312,12 @@ div.uimob500 .um-header .um-name { } div.uimob500 .um-header .um-meta-text { - margin: 0; padding: 0 20px; } div.uimob500 .um-header .um-meta {padding: 0 20px} -div.uimob500 .um-header .um-meta span {display: block} -div.uimob500 .um-header .um-meta span.b {display: none} +div.uimob500 .um-header .um-meta span:first-child {display: block} +div.uimob500 .um-header .um-meta span {display: none} div.uimob500 .um-col-121, div.uimob500 .um-col-122, @@ -157,7 +356,7 @@ div.uimob500 .um-member { width: 100%; } -div.uimob500 .um-gutter-sizer { width: 0px } +div.uimob500 .um-gutter-sizer { width: 0px } div.uimob500 .um-account-main { float: none; @@ -246,6 +445,32 @@ div.uimob800 .um-account-meta img { border-radius: 3px; } +div.uimob800 .um-profile-photo { + width: 200px !important; +} + +div.uimob800 .um-profile-photo a.um-profile-photo-img { + width: 140px !important; + height: 140px !important; + top: -70px !important; +} + +div.uimob800 .um-header .um-profile-meta { + padding-left: 200px; +} + +div.uimob800 .um-cover-overlay span.um-cover-overlay-t, +div.uimob800 .um-profile-photo-overlay span.um-profile-photo-overlay-t +{ + display: none !important; +} + +div.uimob800 .um-cover-overlay, +div.uimob800 .um-profile-photo-overlay +{ + padding: 0; +} + /********************************************/ /** @@ -296,30 +521,62 @@ div.uimob960 .um-gutter-sizer { width: 4% } /************************************************/ -@media screen and ( max-height: 900px ){ - .um-single-image-preview img {max-height: 500px} +@media screen and (max-height: 400px) { + + .ajax-upload-dragdrop span.icon { + display: none; + } + + .um-modal { + width: 100% !important; + margin: 0 !important; + left: 0 !important; + height: 100%; + background: #fff; + } + + .um-modal, + .um-modal .um-modal-body, + .um-modal .um-modal-header { + border-radius: 0; + } + + .um-modal .um-modal-header {text-align: center; font-size: 14px} + .um-modal .um-modal-footer { + position: absolute; + bottom: 0; + left: 0; + width: 100%; + box-sizing: border-box; + padding: 10px; + border-top: 1px solid #ddd; + } + + .um-modal .um-modal-right { + width: 100%; + float: none; + } + + .um-modal .um-modal-footer a.um-modal-btn { + width: 48%; + margin: 0 1%; + float: left; + display: block; + line-height: 44px !important; + height: 44px; + } + } -@media screen and ( max-height: 800px ){ - .um-single-image-preview img {max-height: 500px} -} +@media screen and (max-height: 300px) { -@media screen and ( max-height: 700px ){ - .um-single-image-preview img {max-height: 350px} -} + .um-modal-header { + display: none; + } + + .ajax-upload-dragdrop span.icon, + .ajax-upload-dragdrop span.str { + display: none; + } -@media screen and ( max-height: 600px ){ - .um-single-image-preview img {max-height: 300px} -} - -@media screen and ( max-height: 500px ){ - .um-single-image-preview img {max-height: 250px} -} - -@media screen and ( max-height: 400px ){ - .um-single-image-preview img {max-height: 200px} -} - -@media screen and ( max-height: 300px ){ - .um-single-image-preview img {max-height: 150px} } \ No newline at end of file diff --git a/assets/css/um-styles.css b/assets/css/um-styles.css index 74670e46..de3a7f66 100644 --- a/assets/css/um-styles.css +++ b/assets/css/um-styles.css @@ -25,7 +25,8 @@ box-sizing: content-box; } -.um a:focus { +.um a:focus, +.um-modal a:focus { outline: 0 !important; } @@ -209,8 +210,17 @@ p.um-notice.success { } .um-tip i { - font-size: 18px; + font-size: 20px; vertical-align: middle; + position: relative; + top: 2px; +} + +.um-tip-text { + display: block; + font-size: 13px; + line-height: 15px; + color: #999; } /* @@ -264,7 +274,6 @@ p.um-notice.success { .um-field-icon { position: absolute; - z-index: 888; cursor: default; text-align: center !important; top: 2px; @@ -485,4 +494,60 @@ a.um-link-alt { font-weight: normal; text-align: center; } -a.um-link-alt:hover {text-decoration: underline !important;} \ No newline at end of file +a.um-link-alt:hover {text-decoration: underline !important;} + +/* + - Dropdown menu +*/ + +.um-dropdown { + position: absolute; + height: auto; + background: #fff; + border-radius: 5px; + z-index: 555; + display: none; + border: 1px solid #e5e5e5; + box-sizing: border-box; + -webkit-box-shadow: 0 0 2px rgba(0,0,0,0.1); + box-shadow: 0 0 1px rgba(0,0,0,0.1); +} + +.um-dropdown ul, +.um-dropdown li { + list-style-type: none !important; + padding: 0 !important; + margin: 0 !important; +} + +.um-dropdown li a { + display: block; + padding: 8px 12px; + color: #666; + z-index: 4; + font-size: 14px; + line-height: 18px; + position: relative; +} + +.um-dropdown li:last-child a { + border-top: 1px solid #e5e5e5; + padding: 12px; +} + +.um-dropdown li a:hover { + color: #3ba1da; +} + +.um-dropdown-b { + position: relative; +} + +.um-dropdown-arr { + position: absolute; + color: #fff; + font-size: 24px; + width: 24px; + height: 24px; + z-index: 2; +} \ No newline at end of file diff --git a/assets/dynamic_css/dynamic_profile.php b/assets/dynamic_css/dynamic_profile.php index b45a9d02..7e580e8d 100644 --- a/assets/dynamic_css/dynamic_profile.php +++ b/assets/dynamic_css/dynamic_profile.php @@ -6,17 +6,35 @@ $photosize_up = ( $photosize / 2 ) + 10; $meta_padding = ( $photosize + 60 ) . 'px'; if ( $photocorner == 1 ) { -print ".um-$form_id.um .um-profile-photo a.um-profile-photo-img, .um-profile-photo img { border-radius: 999px !important }"; +print " +.um-$form_id.um .um-profile-photo a.um-profile-photo-img, +.um-$form_id.um .um-profile-photo img, +.um-$form_id.um .um-profile-photo span.um-profile-photo-overlay +{ border-radius: 999px !important }"; } if ( $photocorner == 2 ) { -print ".um-$form_id.um .um-profile-photo a.um-profile-photo-img, .um-profile-photo img { border-radius: 4px !important }"; +print " +.um-$form_id.um .um-profile-photo a.um-profile-photo-img, +.um-$form_id.um .um-profile-photo img, +.um-$form_id.um .um-profile-photo span.um-profile-photo-overlay +{ border-radius: 4px !important }"; } if ( $photocorner == 3 ) { -print ".um-$form_id.um .um-profile-photo a.um-profile-photo-img, .um-profile-photo img { border-radius: 0px !important }"; +print " +.um-$form_id.um .um-profile-photo a.um-profile-photo-img, +.um-$form_id.um .um-profile-photo img, +.um-$form_id.um .um-profile-photo span.um-profile-photo-overlay +{ border-radius: 0px !important }"; } +print " +.um-$form_id.um .um-profile-photo { + width: $meta_padding; +} +"; + print " .um-$form_id.um .um-header.no-cover a.um-profile-photo-img img, .um-$form_id.um .um-profile-photo a.um-profile-photo-img { diff --git a/assets/img/Dollarphotoclub_57189843.jpg b/assets/img/Dollarphotoclub_57189843.jpg deleted file mode 100644 index b31f6dc72872a9179f56c326694f5371352d5e76..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30265 zcmb?@1y~hb*Y+F^9nzfw3esJYA|N0jt$-jNx(>~uRU||@rIii=k#6ZyknS!45s;AP zAJ9ji_x+ylYxd0SeJ|i_1qj`eS3*VxzzwQG1i$HsxBSwax^{{(2cE(O$}{e7bhr&Mow0a=glaOtqft7 z7XX-QBD?*+}w z*wD(+>;i!CJALO@P^{rb#uw%=&P|xx7&|yz_yrm_g2U}EPEc*_jU63-y69qN{wpIe zLwjQrd*h4#QB91k4S)8EX>E=$HlnjP{Fy7{^ZRjb5em1tfTG*N9n2lgoqy`V3>}<) zg=1pqU}pX^X=qk(xaF@5VZz`xCSaJqa*qtEU#Ma}1~YGJ59aB@JhI8J4l%6EO^h8J zU4P{o_HziGdjPs}J|xHiOk)HXk%Pmp-awCj4zG)AhK}G+gZ}!&gQW1%S%fe+*4DLmA@+kKB54Cedn&Z4IBv%0IY2s z?bYON(cOQbLxf@hA;?DWzjKd{ z?oa!FOTvfQ+B*UO8pF&$nG2M$?KRY-L0J?4(6LSbCO`h0>}ZSt{R9AMxUH+bxv7~W z9W(3(9k;Ns5ZxVP7c20|V7~_rMMHZdI%&AIt)Y!80Q`*kTnivL&n+F8WC3nr0ReU% z4lw<{xBv3uZ`6M+oZs7@h;7x2dj=u-_^0iU?w>YzDgX%Wg1HI%)Al$H0LpykBCrN*0(-y_IHN#=;6Ml= zC_7XLDh*YE>Of7P_E0ZqAT$P=3N3(ELEE7H&>84g=m9b+G9fY@GCT53 zkJg8_h_;W8g-(mkgD#7%gKmTV3_S)t7rhR>4}BT^2m=q}8iojl3WgDeD@G_rIz|;n z55^+K5hgw+6Q&rZ2Brn(Q_NSG1(+?E6PVjrSXlH}!dR+U=2%a#USYk%YR8(zI>08t zX2F)m*28wf4#CdC{(wD-y^Vu|a~($#M+e6dCj=)4rwL~YXa5S}6^<+NSB$QBUrD%9 zcBSviH(U(dYq(Ol`nYbmF}TIJJ-A=-Fz~M9N#hyfdEq7CRpJff?cx*SbK|SvTjK}e z=i+zZe<464xK1ELU_|hoAdR4jV4e_4$UrDf2qW|%Oebt1TqHszVj_|!vLJd%luy)6 zv`I`r%tx$6j37=Rt|gu!fs$M&kteYt2_<G zqHCtxyh?TT&Q+(YZ?1MqbY1(p|Ml|gUzo_4;g#j}=B?n};$!7A=1b-q z;V0x*;`iro+GNT^*HMfjGmw{VT{frx;JqsV)a zjhpN@EpO)D{36OEYAl*AIxj{qW+?VXY(|_;{E_%;@fnG$5{43~5_6Ial17r5lFL#z zq%5S~N`1S8&5qBGR7HA8tc$%iRvV-7P~PqbUNKVP`;*Yqg<{6 zsK~2Cs!ZNvx@UKXk{Y*~hgzFDp}K*3uKJ$FEsao(aZP4TC(TAJTrEAV z9IYSPx3wd*XYO;}_q^ZrfZ~Dqg9;rq9W9+qon2iS-DurKJ$^kuy`hIp4-pSL^eObM z^lJ@p4Gay6AE7+bew6#@*igkV)o}N*{Nu#On=oluENsn4(kRMk#aPTZ!g$$4%p}6( zi>a7tr0I&8gjtN)S959ec=IibI~FMxKP*)&vnon^u?wsJf@1o^WjKD|OAU?XX zxV~^*c9V6>a7T8Bxi@-T^?2$r>nY{=#tY*0*sIZ-!TY)Q;uE_m%U_^Sk0_?>Fp!(?2BuIlw%i`vvcdxEH5^MuD9#xn9P+JPv{d zbp~?>zY0DJF$w7o6$niZLk+VD8w!^Q&yK*4aEq9aREjK%qKgWQ+KD!Z?ug-wNr}ab zb&8#Nb@x?89AjKq+;O~F{9wYZgu+Cs#21M_l8lo2k|mQ1Qm9j2rX0RDe?9U>?oCf;l<4RJoyfNO?|qD{uAQe$Kz0 zUs1qb@TQQY@Z~$;ozuJ3qDMtT?-k!S77G;@lw2!GEF~-rEQ6G}mTi`smCsZ>sOYU! zsBEmdSyfuiQJqzDwI-pKxHhy7v+jA_S-o5R?g!fsUmHvt<{J$f$D8gq4K%AZceg0E zw71H(HnmB$)whea*K~+)#O zeGUD$`&$R(2f7B82YZJ!hen3=hG#~MM!t+%jc$!Oj~$IanLwEcoWz@qnWCCXoxU+$ zI3qAqJ9~TfgJ3Ss_*0j)u}FLZIwtwVS& zWFkrxAB?$mPSzb{^nNC_o?VgI7l!m;D@~@WLf?VgT z1mK+UxQO85rxdujcXYBh25Ao-4n6=lCo@F2ghhCG=)fbtun0F7@XwqEXoc}v{9lnn zfoDm-W%L00KbZ<(`YA)g0B|Y=pn-bGTs%D7VbH%a0>ukM{w*_xLw;f)NKhaG0?yek zyoCxyApN#48G-z}{Z0hR@Ai8UXutXe?`s75FBvotf$=vP^Edu}gx^J9U?!lCBEl|w zfN}|e`0&5U5r5>@fBDbN#q}EU%O-g5UL*Z=7lFFh&_8l2=`Rd88q)v5gZ`u={n1Sa zCvIR9`ZjJGkyk|20xU9FYE3jOX~jVm!D1w;0ng|JE<|FZo>g=LTv5 z?ch!Y?kN9s$N#Af0GOjdxC#JJ9)UmK{4{m$HD2YfbI?-&BBd@ZFAoZI|BdY&f4-m1 zUK#nd3bS$GFf@WcHs*lAtvL~fww&A?T%3R?LIh!LYi;akNQbbtvT+bWh|yobL_qnx znUkLG0^(>XMt@)F9=MtT$230&9|sq`I40D;d2V~}7Hqo+Ub==b*1 zPeUir(|-!h7Y{$!Fbg@JVBrI?=#^rf(0?DVC$znJSwffuayrKk(W`%=_THvCf9 zAEx|L=r0!iQrrc@ektlF`+h0xf|cXX{zbX%x0)JK|E`|N6SX>G{uV!2d0xKPtV*aXvTnhYIA-NRxt9o)N?pI~y zQrw@a%caosip-^;Kh>H`q34yHOF_S@JeMMWRe&zVomYo01^uZUT?+lHs&pyvPsQm{ z=wH>SOM&N=s!Kr^RjhwT{cQc$549-gKYrEEzxC%l`LF5SzkabVzV}jA|27Nw^SwWx zT#A5IE>kgj1iR5sZl7NK|GK36uVVUFXD(v;XYbGBx~TU!{Qn5)pI3zcLr4FEJLf(+ znmbw<|Nn6De_OBo9|rO7{)&RrZxLyGV?#&d+u$yKzLH|+5@zS&(fDgUEz0?Cm_M_^ zKiY764LICN{9l*nf7ASR{ohp8zz zSg3ZicNPCzU00OzKVJT4U3+7&&LsYy4WYkT`3d|-Bj@h_%(Brxr5LPWi*RxM-KD=P z{G&y6u=oZu7XRxvJN~Zw>(Vc^p9AD?OTdfK`m+^WV2Wdc$^UDyDvo(J4oCw?=f7$K zIX@zuZ^+Q|BMLGy6d4r-ETWw6Xc*|=j)sZ~mPRlz&o@xQ#=^utKRGw@QxypX3Pr(0 zLq)rQ{ErT29{_xGpbn^mLhu13d0xg1UyLmNr=6gMqI|@OND-92}jTT@bErKF@vq z`~zMDMnpzM$Hcyhi%(5U&&bTm&dGgWTvA$AUQt=q*woz8+ScCD+1EcXI5a#mIyO5u zzp%Kp{AFcz>-+Z3?%t36gTwQo5YXpdof8E`DU+eoriW2*?%vvXaB3neh2m^t}y@` z3IT5(6d#ZPPUcunGoR|+%A$(gdHooFG#KCt%aTX}WEWrUv;e&9{d|BwQ}%1rm}R6e zoU5;PBz%FZ6SY#Gkc_O8MqzhRtHVF{y=#Swg)W&v1gSQgw$q{VjJwC5_>ho1<(8Ns za@2Q@TPU7X_E7qRXCHzdr3^hGP5EwCfz>Xtd~7BuEbT(ub!`4*Jxi!a#+Hubd<^O`3u;*3N5@U>4pV?)RXSOyO;_lC5~|@yOf(-yHTob6>$dE^n3e*<>SDfJR2p_Mw69*HFr3tQ z(mdf0fCi!%qEm%SVq3w4S0wS@QA-`0k&{!+J;8e#4_M-Nn?W+~y6{2BBWYVC@kLir zrM+aI;`5DHsa3SzEKW%f-u=Wy@liV{LPhEh^0u;uZ1 z`J0RBbD!C_5q*oO=(8SaiEI;L!#|kYHIX?39?%Cpa_W)j%E@++ITA1#_b}ggcMwKv zjtS)YSefMDyB40ztVwqm+?!o~n_{@_40!HEg}SD{)tvx?m(Z3>O|=og$XA=4mqN`c z{flx&mPrp;~!MqF?5+@3| z7Ix@^3M@lmW`!q`4=Wi&lb_tneEy{*cx#RlEsAMO1N+Ix`;SbsBx08}G7C&*SxM8P z%30~dL(+)GuQxRB zU8by^)WZOTczx%~r;TMvTbquyWkFNo8Q{ zZrvA`H=cp-<&N&XzUK5&I=t1|td6sa`ir=)aQ|LG>zCcUTap_l-10T1VFtJ(T?0Gma4Znk^_C%dW-UX(9>~hF01<`V=O$iFGQGJo#jhB{Z0KTYHZb?|e z%M&$rNftW8h6X2W^p)~wIQH3dtO5rqpNc;(Hub(=G++!(5Y;`>JyzW^ctJN%gF6?j zf1e{O-00JBf{Et*BV^x4DrBOjLBo|k4YwkGl#8<2h-fla@~a$vUa65OM4SOFZfqn~ zxocWW9-;&f4aWi zVre0P9Jce~)zTyDT#;g`Z*5>%f~Kb2Vm|L>+oQ2+l?6+xdPtCuZE&6Gz!cIM@GN$J zxkaBr1$ERzee(<$FF=b!wl*WyGbKX~BTBTz)p@mizuTywZXUA|<=Pcv(jUp=X&wqE z?|0Xv+%pAY?iJYYF@?B?46dp-n?weGt($MnlZGfe4}1=7p)F^{X=Gd4XVd@)=8bd#3bLd!Fu&v#PX zMzC_II)CaJEMXqEdq=N=mS;d*=;WOSMu%JINrYX0G~#b+b6}xGNe9^XMmpw?u&ItqM_w&PPi2loPS1Z{2eri z)$rY<@=fgyQ{fDADfO)4!O_(-V1^bJZp;@o`QTfL_NaG5(Lv;#dE^{!vu?^)IK`^7 z2=Tko?5Poy2iG2_M}Hu@M`Tp4Uo-4a?vAO~7q}GDerSrOmBkXB8bnLA&p9jcjK89k z8gB~Wf25*Fe>33=`L)oSa7dfzy^qBsUWI0Nn; z%=H8`Ax*QUUmNCA$VPQ7MBvZbgTqiGs7LHpead(f9rJVN-)(x77x`kB6W&ye z`gTfx`f0k+daqo_)dxMD( z+%3N9t7}Nw(Y^iLX8GAOAW$FOm)-CTxc3RR)b6}9&44p{Xs0xDqtNa7#&!{Rn}$jx zK9_k~v%{I)-&Rr7Yap)>rDePp6& zNR&0bOCo@enS80(--CrKf@6-z|1J{uIGsbQZsZzl0_Wvf~?ei>HEo3 zef_P@Jy(4hg0aXt!xG9$I4}+jDqzFH^W=pLgw%6qKq2oy)2GZ7>D{J14e)t;e$2l) z+#rGVrH(;H~w%0I3R zx9C`A!@u&Af7SE$b(*AdhP4Ik%%1^VSHCLaZl-<_mNCFHZ$1OskxyJO`LMXB$0=0T z-gwUsn2|hAQom!Pdz<#h=QJrop~>(>nrZH=*M34lE~1W0VfaP>H+7r=hSpo5#*CW*i zHdM==+l5}gwpJ4&4?bYS&C3r;&4%|JWAdEOfNO`v>)-VMy+zaCXsPb7caL*E4FLFlUN7ujlZqm z%k5pE4Zp+S@}@$ZRqrdJQiVF>Qx=;t4#wHK%;N%Io*2QphYqB$ZPTNP5jpM_fw@W< z8G%jej;~kB3U)aVvCTC2@Al2JLWx*(EJf`6ta+vtp0K?oTHxi#@0<{*nZi_Oqi=b7)TWNuzVxz(OM{hSc5?kUvG?w8i4rnBQ_Lnt3^;89wvNM)}Y z>*OYu2$t6S)zJ>JEBQ`^Vi!dKw*3luH59(*JOSh-! zwYx%1hL*$R+&Yt4>PRx?uRCcp85TuNm=g7Lc!#8592*?b?N9PORC-gnIoZ5Wn1qJ* zrhB&Yedrw8c41+|oMF7`uzW#V3+!EFCpYO-!Z(L^h(h^yTX(iJ3*P-$qi<()fAKu1 zz3Y4Hol}}b{E_6+G&RQqieg#YD41@NxY6)Vs6p?sQ?8|(Q}T=1o148dtYW=`4{Bmn zqxIeiu|Ew;uA!+YyppcLQYr7*R!%P5uNpe%meM5gVwz%SgYZ5=MYXEVlVAgj^ve5rY&UEg_BwdQ4vpcO}_i)!Ss%po|sN<>hryCo~lQvUV}8ur_;kJ3R~Ap z9bAm_Q2YDUDqje$tJWa_n5vZv_UJnq{w*r(Q6?qMb)hPH;(H2hQQkeD{U0;n_rzHxZ@=e$ zSb86FX>pNndW4lw$A|THav{};;o#6#wWhpJhu88<%bzr8Hs6$TD^HbM_Fs28y0b4g zw^vxOpY~x)cjQA69giZJ`864}Oi8KP?GI4V?WuZ+k3|L#TdTrDS;i(+KHGo9YM9kv z;xO?*+p592^{q>#w5YZ`e)WOq$kcqdv(>toq~QP;+EHEfIbL-8#2`_2=&>r zwEVm5PWAZhFYFV_J4(g-7-mza2kv|-**SVWP!w!5!ut<($Vo^-_w&-T)Azzm;axJ-f*I&*xF?$Rz6nKs>XB>S&R}m zZYFYu<|nU@VW`_DW6tNh?iQrG^jfsFOmE&x88vOB5!rX~%(I}lZ%9Nykeg;tAoR%n zah3hoyYKd&_ZRuW{QX#Qm^<{2ZHJB-Pk|wh^_JicZuz9S8J6ezAr4JOVZtjtZYRV- zbLM)-UDJ(PpZ9v6d$Dg4cSl242}_(06Ky#1BecBPKVUm^xUPiBiMkZVwD#sa;MnyCcRz ztcjY&DV@F<+fs2%zJ=V9sfd$ZQ=!7!#hAbF9xniW8Cj5R!gH+gzNV1=Ly0R{nxaZ6 zEN6hfgj)Thjd9)&TgMW8Z%^qEYbt#c23STRr|RTdN3SD;P+;wolfLe8H0H8F46>!S zlXk>)#X2npb65r*O*?!`!qrmJ>E&eqMi(Qf_crjD_O4ry_E@7{`@T-tLUdgE^OOT~ zykq&i$~YDn?bc?>lQ-zmM3B?^@5EXa!I^NEGoWrIg1dcH@eG*OfM5~RHc$T;gfh4Y z%}%ckWzHDxzu&bg=`dkp!|wq9PN`ubxv@BVolr+H-dTdTe&+RbiT`u)s|aN=-q{pC zr(h{+N~PLi@lOfF%(H=1c+E5}rq6($`UaDcPNy-)NUi>awC{3XD#dkmaq`XCom5wA zMmIN_RbJFF4sm`fIRgSjqVip=o?5S4_mIy%p_GJ*I)rdf4Erj?!;{5S-XvDSI(x1T zc$lAL&HI*A1{cI{D|hCULPnx$a)kOT3a*eEs}&r z89q05CzJT(2)gp_GBxVcZ4UdDAgoKqznBJV;PQ|ny7&#hEBP@=rD+N=eZNt)_8QV6%a8W#s|!~E?QEaJd_ozY?|^K zp)K|+&J}-=Zt>%A@~{kBN9LvCGdFuw;oX89)huQQMd>)@bYrtb(-v#X#uU7rwP5k~ zyDvo9C|XYk-$c1Z1fj}R%+=(6UWo4hyz@mRS-?HzrNO3Z4~j$s?u%h@>N4MCocr8G zbU@(S`3<{njKb=6Zf~8&I_o(PT!KfZIWP696=99mHF7yC+U8l~6IBgFMJ03`faW~Q z)I!+Qe!+x{P|uXZ&5>ZM2%7xD-fQ&qCI4onFJ(HO=-`@2y;#BLL;BJ8O>+Fm_BCEBuK;! zDvR$JZ_AC*qIjsx9eAJ~@}GuhnzbIty{Za)kr_|*EjZ1I&T;lBHJ6)@KD!(Ri^gEQ zx0@J-T2!Ki!s;aMkjh9}o8Z*-eWo`rR~C5Mki*Ls$jRXC1#b8)UjE^8*9@R&?7PQ3 zpLoBDn$0hzmHC&ovEuPEc-^Y4{(?{J_!i_)1{}T&1Wi-eGqzOh6O$a%`6U zrlNk>kHiEE$=c4aWu0hec#-5U)sbT*C^|j25m@3$*%a^hW-f1kov&i{hJ$D^N@v9f z@?Bx*RsoyLomQNl+b**?%9`|V@+8Q_nLV7hHvApMEN6!Oc0Y-nPB-bC^qGsi%tRUB z@unG~yP1lY_=uZ)!ytanWKEVYeFMF`Tz~k$Y}&ne*F&6I)@zhiLEh{PplwrM^mNem z>8y62RG#~0P9kr!5Z*dsQ1C%=hMCe|-(lsUqoF0^+bZ&*HUG7dkY9DYcWGvr*)j20fsVN%my-w9k<74GDF97S4dt$T8X z^X?G8B`4bXs(@;wW6?5{YA7HQXDjK0_`m`xgD4ejNp#q8o>WyKWptwZifZb-wz>5g z031p0IqxSNF(H^f@R(2I*9fL32r|=bt0pr)zapurE?V%K2K*H(=4yLWU<4E5_n817`ZnhE97flG62(2o)8;rP?B5sUUub+5_&ngOcm<>8k z3=SR9%x+Ha#qGNvN$q<&*g1&=d; zq;cnl!lX)|=)>81yfspRA~PN7hh*!5ujqrS*;uA|gVPr8q4!jf7v}itSa8OTANR!M zDC8yT5rfN;&-->;XsX4VpQd6l6(XSYs9Kbbo3@oy(2nIyRv+S91i2}1zRYs-_+EEO zCf-^f4x}#`giYVnsnItsLGY9a9}F*cdp*K9)J_OU`?@f9INi5g{QPaMjJ&0#XWLLc z?y>qDwPHgV#a>dr?!*m^`(J7F9_o%SW25hO{2-&F((Y0gcsuuFL0QwHuMJR?KG+q4$EKJp<<@v$U!svenyd8Wf&&DcWeg?cxZ)_7} zYQvIY5+@$;eCKm)N&A-LV}S|}>Hfx}6!%8AxI?=1Ev#Z(lcQ+Ulh->7-5%7rp0^*z zmOp%TlJE_-R$r2tL?n3zG*ah$o!lmPjqN_9$sP8rTDf)LU0>qcxbH6OIY$cn5-lXq z5Akm)Qg0nmEH!@SZSM_hX1PZ29Z6Mq_ZyTOHu@P~HT>t$r9x{q3353A17 zt1Jp-jOy{jqY%|&;hcw-JQI4K?5@9{y1_E>;Je+`M$%hS>NVEFGqiI#oB%^7D!$p?SXVhh9rMuL%5SYVsghQ7^3%0e z_>>V)(cXk&%=l<`!5+~f%9m@%WMnQZO{f_25SOD2V<+rqA_n9Bs zw+apCc{ ztlz(88gvGjZah6KZ_7uMu6|#+ncsv64NY{gXKQ9{**&?t?6%!`kCBU{(h#85kNEG{n^qJDN)$X&*Bh zMhz5%c&`k$?c_c1Bj3F?%jzuldSAEUU;$kJdJdb?3l+$o5^Tb3?;3!dQ23RvesC1T zwSAu35;M0VpJ5eKwuqx`V)Um6e1fO2CF^`~c zYC*|x(V@7yZyptP&YV^1Q2biKwB6RmI&t3QvD)zN%X;*ft;zY7DGEdJ6rLTtdS^JB(e7(*>y|3DNBAUqak7zZRFc=!i(2o67d=D z8zhlIIgnRxvV(5;g`&i&^jiYlD($Qt#8rW!CLjz zETd#1uzlua?nB|-M;r)Ny=Y6B-bh6mxNg#j4$j=`^j)_D-J&DSqZ=}52DCekS)wU|PNR<=#mqH}j zQ;&u|U#`=YbDcZSyiTxxyu};$*kX_3G*Uz*T@L$6ez#BrIiUm(PcmZWiJZdrT6dB~ z@P{2Pjb;BICI!h$u?@6iEN}{QcGJA8K5FkEoL1>J8hgfK1A7v_3e5wm6Wl@~w$XjP zG2++vr*0;RNG$>Pk1NDjeeeU8+!AKZ(tEn6_t{he)VxQ_bZ>l9#c9nkdo%s|x^bbR zzg|Y%^=jV8fCE^xyo=?wsPrj% z9HdbOR}o*HNg8f=PEs(GwkatNp*J=0US0z&tNMLLp<4u-l&Rd9KibQt^ zK6WFvdr9nf*gTlfcWQ3=V`KX}SB6jWm8zzO=~#nEH@9(k_}6+;+0Zw~4|awcE0_DRc9?xl{kHm`KFm;z?LYh)?k6KFK_Zx|4Y)%L3f3LOKy;IRbTePF{VM zFi8RQWJOy_I3PIw(!E8=xS6aw*@oGB5;t-J@+QXo_bfLlPDb)8&H(3Z(beL>7!+NT8?DKj<7FyBJ& z61pK+B#r~}+xv~ma-Hby3>$eN2>N=K7;)7mQBjf*75#kg*d?z*Y-Eea3;`*(OTMHW zoXS13;IZ)HeNiLG{ub9Ww}Uf$&jqsn>h(crYH*t9E`J+&uETe^?D-z~wANDzGYLX* z+Octj$%bx?EkNKNl5(F)!$PIQLoE9nRIqf?qD!atKF>EFAygGOl}g-_ z)pN`a6Azb?a9R@W;FYYniS2JgIyH)Vl+tLqzVHb1f5{CGx&{kxwXw4r+}da^#6}3V z=wl`{RShR6lMTJ@uTez67$0^xYT>OeoURkSv%-%VyYm4mZ1`@F^;VXsbq~q>LR#VO z$XEdn5lg>aYy)G)(&C##mzlzsIx#+*w>+iZ_34aS-^z*&o7LW>ud~2u^vKKEiFUP6 z;&q=nuA}aC%qf|KYXvHfrgFM;uTZDt&>4NVEQl6~NU3^ZQ|kGsgq#1916U5g`P3RW zdy~xceI?`bs)$7e!p~1s>)D>RxSVL?w+{y2=*>}`{3gGdXeM&2rvMR5&S5fv9X7^2 zp`uq4Tp3!2rr_(Fr_31_uhlr0s~c`+uTQrhoq;f&W2~d!EUPARaUR8i3N(n&WUELg zdO3Yq>1LlKR7hHVVha>)M-JpDuIe3@bfT4 zkLX*@KorjQyx76&;7=HLg|ijz^m}GJR5}`^KUvZs*A-@?TnqA|;#-v&6=O__7qsh5 zFDd=zF88&PByYCxE1Y1T_haOJzeC|ZtbnE$0^b%mH=k7^Z$05{xy{l|_N9~ceqaeq zld8bu7cvk-Q(2SXsv4Mhc!M_TtAk| z?kpQu7y;gIk6+|6t{F=YyXIpDT$c{vLb=WpJucwv)6Kh+V}^@$QtITX&^7bz^}C5% z>_#{FnUgfrV+h1o;bgOAMc*Wa3ef_Xm>2uYCf&!|f4oP(_24`*@uTibr1TkP22MXx8J-!;}#3iU`>dF zO5OWDC#YSvs{pABNs6f-GHrFaIi*YNQp%ha;JN>nMOLrM!m(xAVWBXPl;--ot=&nm zAl}ll6DjXxN7;QZf)b+oYIX!r7HXB;Elh9tu7Vw@LnF=^Pv+PAbS@8ly;N6mP3iF$ zbHUpsTl5TMqh4@MS2UDb`T4|WGS;MsiqgW}1ANw;8(D8UPy0!&vvrz`Ei-i=d~rE6 zPkg|P65;W6Vk4}FyN!VA5i^u*IgL%rdV1<*d6#!}`|`&!9rJ76{Ttg7QuQ+_-ee$! zVKzOg!0M@HB$D=JBWtxc?NN&-(oC4hY@omT1Q-8yWh?3Gt5N3ls+}|7Y0(b3KKq>Q zyTtO*!>?U#+5zJH9>^!JMcL;z&^%bm_I=+VrtkSH50v1@?u?81zp75YpNT$V+gqe* zSNhU=B2l|KS`kYtI9KkWm*>50vnBklfz47XVaCd&b+cN|)as!zTKmEf@5j{->E}3u zPLZ~4%-Wz3|e|n8WD}Kdk#UW+Or3F4JGGIvxGB#&`u>rw;*`4$_-{UdlGA%jJ z0s$HJ88vmfWs4c=W}{8lWfgWcug-*++N82C(<;h!#%DmQs!kA%1U05Ym$Ku+I{Sp- z$Bi}1JV$Z1&Bf4GgAf{)fm2}*{e6Q}#Et8TWBZnkb+q*nhb_v;)q?4BzZaG;LdG zYu_Lu(NcKxY7>c$wy%yoM>EfOU}t58T7?%}gsGf%$1k_lkD=*Y|;Ng4|f8EZ$U}`Of zcDBCe3|L<$_^!R`X?!HTKdxFC!Ffft5|@rgJ|qyA@eE)&nDBJ%&IncHOpkI6jj5kW zrv>vlOK}qCVNkel@K8}PExj$(tiRZL+`);yWr?#Nu{@TAIq0GBc-J6xFjj}d7j=5q z6Sp(ee~Ph|hOxH}^8tTtet@Z^?tL~bR>wF}{Im!x!f89EB`AGe3Zarh)E1}qWUa^O z_P#{{f11AzN5=y6QQL#lXzR} z-HioLs9j9?P0nZ5CoktMN&NL<72qtsF~!;XkkXASQyvog9cimC zEx~_Ybv-0PBK8H5k6SVGSU!eyM`!e7G)C*Y8f=E-`<`J=wbe7a#>wkVqy zeBdH(zn|oe)4$4V(LrA6?ra@Fti{p0s4bS5xJ=e9i^IAHaV?vD9acs~*UTxQcI0()F%NZaw7{2{mqiiaJ}+4OQFtlA*fV z9trNKRV{VpEjKsn*ki&Udv)(lqP)bVW_u{6*$e$VWb9+AqGpPvP3^+Ueo(%HNa}Vv z(>As{Jii`e9IMFHSSh8iI_Tm#yjnjK3XN0$@R(R6URKAIV>%>u`@6(rK5N`tTO@Im z<6UG=>c`j?k)v<9vM40Zi!M-wq}FLeAAIi9rFD=dW)rh9s*KdKtU5@_pKj@)+IQ|` z7jJ4)8-LEyaRv}l`S`U%5lqK(b%Ep=Lam+FZ0$SGt_m$8tNGBp zY>W2ALd^JMq~ggqENdoYmU*Vg;e5N{Pp_-WS1sNN(~x+K=wP0#pSlQb5V8lSE9O6x zWhgBn_tgv0rA3@b^e7x88oRv;3{lCkg(qm|Y=@+A6dyUC_L0!OmwQ&Y1QLw52fP?9 zhKkpZ1QKd|OSPz8*gVkT4#IoMibSq0UZ(kE&-U<>%p%cY-RaBYP(j+DP~N7t)S8-D znKgDV`IKBYG`}5Me;SmM%@lvWinnhakNBRA^-Ur%a;N%g-UI(=94NFY-qW!1c$rv4 zmXIRkVZHU0{p|$OyrJjw`9d+YS`RP!;v`v58-nPZwC~lYm`O*HxG!ng!K$@;ZyCSe zEBb-bYF{>z(Wfw^;2K_=hYJKkSSPava$$xKnNMsU+C*$jFPg&lVJ$sHENO@7ZaQk7 z)!r$`!gKYQUu*D<=f*jy5sixV>(_5^L@#82-+N^LRd=OZB!acK#oZvUGf_Y{*@nCi z5zy^k8=5MPgLtX1%Jz`hg zZ4((6=IRR>+-H-0(D)G#RUX0mTOeUgP_;;hV$fxg=dr&CxYa z@2-JY;4k8>TjP!xj5M{7Ofl?>W$2BW0ngzP45 zim66zvWUWR<^6K09^$WQO(xOBc$JK&PLj@QCoCTmSoQ7@W5G&sMsTm3yp=GA1IIL* zi^Kl>U86>W{OmlY&l>xNPc3XT2FEyiaWUn z@tx|GtX&5mTH%Jw=v|(Y2Q^Gv+8?aUG!_YYg03!Sz!TR8e%A@&)1zZyaYJ8g#Bz0YSEgvos^Zo1h{q;oKPW%9$Mh8q z($*lp|Ebe}m8{!wRZ5EWN9AEXxULx4-+lXq7JDZYYyZ@wy5FlkT-u2>#@$&pI7sCh zVrH)`!$Vv!`mwRi^5`t%B-vz&bpzTn{)9VOQrJcBCQ<|(4zvWdL=G0J5;sDQRX67w zoOvxo(glWAkZjBuBQ3J2yWO;@ALwf-s%Ku&E4TlA{QaZ=oROr(=!?f{Z2C9W>QgES zDrp}%7mGIXL*zcT`^Ts@uzBfSIbj`cFeoptcla~X6z z;3Rg#3*)q#Mu@(1$n9))Bsa(If;7}=F-!+yD)ILF)#tQDz^m!Q7HRkPAFOSoy`Vv2%O~KjB zwE60lnyyS3JbmoV!8e=q15FJViFTvCLP8ARWC@ZS>4J^-|1WYOjo#|dp{9X$nBz|1zUK!S|uW!C6`0rG*zP{A;S=&;NQHDT_#Y14C^1vunUP;=x$i;ozsY(ho zAgvgsYkyt!ey7Dooq2O&ChsXXd;b7kv|dQ-G|dabpAoNg{{RrB@AhrJmm;((6u4;% za*-kV*l;?W4%x0MwJK3kr@NQs=62DgPNR)W*+=@aJxusZN6`FBd;OQL+}b?$Uuh8B zOrS{qVx599%7@ExdE@Y}E1s&Nl%sT=zovS8w53(J%cajz_<5vX$Ep7S!WUyOztv{9 zd34)$mm(z!$ID!wyjb$51CA;sPueTByGC-TXt)H=X*h#AL0U!i5xKCOj~JM4aYnpaqyWU573Gu-TC%8!|PSE8(W zQry$FloikQsu9&o;tdxW{?)g}f9J5Ox-!(KdE#9NL{Ij`uOIZ-P}&kcH~#>FhUpid z6D_T7JQv|e=GE-Y#hKK;-56i6$OoJm0o@=tS3m|sDCu7t#89h=vZozaT{T^F-_0Lm z!<8vvrFxRl+fP24{%7U)#vg*-FYy+Y4c61Y>yq(^?eJ%2s}HbPU0bQUg*TGBbsRx5_6D8 z-2i<}dkUROvgUjKIv!uNrAj=#KUR4+iv^PES6^W+B!)mzPd9MRa7WG0^XI*KloXm* zJi7dQJFBnyj;=DyW=+9kBN$AgyKj5N&5IkWnoSF&oWyQ6Lko~?`t_XG+$CeoV z(tWw)SFM&p4z2BWapS5_*!)&U!~XyitnO36I;v^6@`oB@yx4G|BHB+;^EY5m0OL5X zsKZP5(Yn&-&evSj+P$>-FPhZ;n15w)J-5OQ2HN0;3#Hrt0032Q-7imS@UZQ|F6Xt6 zoNCWQp!nn#KLKh_2H52`kK~+mC3vn(e3!FqSQx)*kHT-;=i)5C6+dca_-mx=20MQT zUdN{__{g`tk_4K0aCpdyNYF6pxU8@ZrSMe5w;r&0vm&}c zv_}lRoTnHj?R{D7ej(NT5%9~ynvcUq_;sLZ@kch@Iy-A6GQl60m7ON#yv8J)^f+qs z;X@OOuNkSf-u`}8@;a(kqe6_L;*wi`UT2zo7_k2UgsWG$X|&1ie9N?&E7YBmNIbp2 z#e%F303d<~U%WZ`ni*yyt}>N4+VaxRZ|kY^)iWwI@Rce{XzO*k^#_E!S$FXt#QHw1 zaid%t4-ecsTWU7iIi;w7vP-W4z>1cfT_z2u<%Y(dh>H~q`N5Vf1bz2 z%Z(buJVi#K1*a{!q~CV*vbSx|)DPIJ#r`Dmw}T%408nu~m8nQ=FC-ylm4GGH)Dwa- zIq#nJv%jTVW>N z-92ioA@lyt)O_?uU@`Kk;EKLEQ?BiH6m+kcZzXt)4)}M&KP~-=WJ_&2HX?VrG8b!`Auxc zFpX?Hm%DqnqJ0{=)&2+Uyf2$WFUwsVOj>fbiQTO(p1LRHsq>eO{3mhaUlK`u;(Pe) zujAjkNhT4AMl!MPJT7y9=caw@=JVXLwG0ArSCn0ywe9aO@;-+q%xPE0JG(|o>iT#0 zU-_KB?MJLnr$M3kV&lPyq`kar8|l_lZ!?0qb~0p)bU9wx#e5!3hvvpfxpR4+ahu{T zgH~4lza!0bVQYP;{{Uu0@LflCq^x%Yu|fzT_W{9PFgedo)$L}qV~2v}O-Xj`{cg`A zDz7T_A2UubzklnY$oxg{jrN7(DPh!QB_jdjyYkhdbje|c1J^kOdRJymDplww6{_g} z0IiNJ=~kg#r!5~<{cd#M4!kP2+WaLaWs_ME#eb(TIJif_k--FDs*Hvm2+y^Bz8eh* z;oaX&zfj-P1Q5~x00|i2@s4Zpz9)nw zi=DMc>%2DRuyE#9(ESM1wOdy3(p;^}MkG!h4qnHqGA|*!XW*(0;+MKFFkx zYZ7{9xUpW&Q1_0kJF6O(iTuyqHlcU>SNv4eHM{4yTMvmk)CTfW0!!sz$r8$-AKnip zIM9FBJaf%^wW-FQ0}t%~0Fpn7zsp;{ndRYXPOdh)sI}&Qo}Ri{e1~b5cJS+Z)|KLW zo0D($sBSGTn1pv`KPfmkEr2&;f)A$@qX|?{a9z_kl$9QM&Gu{h{LcGU@LlG=p;_He zekGj7V6Yc%TWm~27{i12KphW3fxzOvyN9y89am?p9U_%dXGwW$%$CY^4e%o#{%ih+ZX)w++ZeS+ays;ib2KYHI2_*!>OJ^fc> z;%9`Gs%x2fHNW1!BkFxO_Fedq z5fA?W3I4C2U$-@WS@k*p05ki~(%vQ=k+*C6Iebb)!T5oz{{Y}?RV*b>=6`uVSchZZ z=D)i+{{Tpkd+$8gqh@e!bH5P2A!+{r546s%jh%BuPrIywu z%Pe{Nl3UdN0>5s_a_Tu&4m2t1_iwv>f6K`HQ#s5lX4tt`siN5~$!x#RyF8lybh^`k zy}B^~9F2+wc?1#df%*H_qf$*tq>f5ak1B2Kd9RN(3;zHaX>w?qagsKU;ZjM^11N~8 zJAr-xB>=`ddsok6GW*UFTiw6cW9x8PouMepXB4*iZg?k+FLf)+uk9;6J_1TV+C}fq zBak*cxd{V)cpxr1bvdsaHOrxli>BUbI7iI#|c_g{i9zyoq2XDgerS9{!ghx$5t|G8kU!5 zCA?+tB}`-<1_=yDewEe3IA>B##c8p{#JGh{3z_SpTp!QMU9 zL5+a^*P3NefqqF}%~65J0l?#ogI|F0Jvm|XI`ky9jGyDL>VE$K!w#M+Dxq2%IIqJ` z(EA5b@g)BM@U4WYBV{m19fyC)zA>+S`l!cUPZ0R!KZkWc3Ekh{>JlTnf+m1A;>@4w z@H*gtc=xXCG^H7&j%xJNi)`|L*dyST=f-c_x8YC2n~NJ;*r)MqI;O2Ge=|dArm4Fn zk94>pbF$+wyEgcZd2DEt$j8@mQ=(iK3{cx=g;v5 zV@{ORZ?E8b?z64`0K#SC8-ECR4^ECtm@R}evK}mhuHQ3vLf9Mw>TC5}zb~5Qg&69! zy|%x9mdEBfhGR}{a;-0SRMqr$?cZJd9;fg|+u^5z^>@>>{ZVeUJA50v*noIrV~obE zbB50+zcu#wTqQa&e1{fIsw_?&*zo(|STZDQwDBHkiHFpfxNU8Ej^A6}L7+(^0d zD);_h$)o6aa?sA`Tf6(5S^AHsd_wU{Z4G|Ej4A#V_?v9~ipbcv@!yFVf8*_Xf6o;E z015)g-kq>I9-P+pFg{BE0D`Xg`tw2fY2jZ3+FTgp*L*_r>IZQfptXgi1CGDFF>&7& z_FNZ-jae^qG&yM42@~4)^^C7sKbIz=1dhY* zrZJDt`Pb^HTSM~Ys?_9sZ{pd@SVeby%LL(|d@=jWk_q$}{5hyvGkaYe&4#_F+IY!s zwafWpvA7#$Faw)~P*g;4IZ_#k1E}xKV^)f(Xq{ARx;K*Sai1Myf;gjF7G_sF;+5f4 zK(6O#RbDt;fyYnIxN(?Nz15|UPXmjbz3FPt9-C2?O-AA$Ao(U{2P{KvJaOsU@~_Xc zyr!jEj&%1wLdkPzQlzQW*wECxC*a$iG4HfljQWbGjbdwoBCC28U>Ik=JuBwvW%=eJ z^qTj7!}{LG(bdP~cz@of?|S~0x#yPJPOT@7?KJ&nad9Q1E6(Sf$VqHx&}0E$W0Bn{jRqT*`z_tOBvLOA31C@R5O^xbiu#!0e|0D?k@B@M zU%rf5^{xK4-D3t zv-l>y`#--=nfn)p*~6h;c~s?f@xl3ZUv{U|tEIiyg&Nl+hL0ozUxrTVXYMdcNcrpH zygKKFd@-u&cej(6uI~#)<*wraVlqkY2**8vz^_LR;a03)V~)K!Q>QC+XVSm$Z)d>k zT~p&HjJ`PB*xR&z7}257H0dJ)_g6P8E^e{)jctAZ06O-nRd&+$f2aI8;nb(gO((nj zx_+nX-QDl+-HTu+2cEU%)~j~Qr%LG_3Hws&&vW42YFL;y-8ymCq1e^qIdfLI>`;d+ zY=1c452qgxwQC;}c-H#ncb80Aw#6uS6J=FnDhDGxW80ip>JY)yj20e*Ztrcs6Z7A$ zLUVQN%F5T@XV~^X23hADeo?vw3z_eHPN{Ug&{94_p8SK}=ouO@9;w$;G&p&_<* z*9RT{09x3<_+S1D7q41Eo&?b&fApZ&_brpahY_X^)yn==`VRrqeE5mJKl1wN0!x+dOj)`B2|BPu9OhdPy_$_$1Ru6XRq$hlicv zl+0}H#oT2^N^k>vo-zFD6FMVy>~XSdP2xt<9>Qp2ST7=&%0XpZ-~=G!C!qfT^;JeU zsJ%xia?_WiHvT;6QCs+O(h#alaIVt5&OQAyc&NrfNwTu}Nh_QjzYmvDv291lXq7^c zr9M)4Qb(yd=DhEy#DZtT&Sf^RBA1x)qb z87Bjs+}Flud3V<^xU3`p0GE+%c{^Y8N803hg2Cc&xW)efF65K+y`BC{`_JK}+i}_CK}OWwebhyicp$>a#R!g6h(2h@VsCMx58E>OW?i z{;#R?n25*Q@}ubOcKD=Bo@vWu2B}!C!;l5$<$2HNOM+QsVym%GqTsthV;Bb^Y|Rxd0&Rfw=kuU)Gss zO;Qy3;RN4H=$EVi0Ea)9Gc4kr`Daqgt+j8_90B>M{9G;tQYXC&(3R+c-));we2n4T?v zUVF>ihRRwlScwig9@yh2rYfg+!6fxEjA2r5vGgDO6EjPD@7fwm+o?SHY&rgI2cbKw;`q*|CQzf^OYpls&x9!{W{!QL{{R-h$o&x0bAWaqz}Mw_pQc>dw#MFa zKMDkPS1*j@)~09XPy7{&#?knL_L7r9k1ziK2+xKs;6)4w-#fIjWPLooYxRx`py=fe zr}N4E(N70zP0R` z^AOTno?YPY6`fN`(~|MhNKLV_y;fofMpA@+ct3?_vu8%@L(P0Et&I;|yYQZ}#2q^S z0AVBV97wx=4ZPD+wMFt9rraN+J+I@|m3Mg#lCa=Jw)@=)&!8N7V?UlNS;N{)#)Q>4 zYF^O%FQ+Yn{{U)8C2MVkVguzy>ykV42k@<79$g}5Qhd)|hJVKS?R+)xi&E7sq%A$m zrMRyV+Uw6AY!b~bGFg>#hf9`FmGyE!B@ZnWyASsfYAwpHlwJb4hdH<+Zs{n{vqGImbEtcp|@! zxRVO-ImLP}?9NN^y8Idak;AyPjmjuiUuqG5hxwb{Jn(#)_wMbMK`faNCzeka(q4zkO}SKWxcz>0 zK4TM_@@u=EjTOp|p8o)7>$wMoH8o~o{)IM493PO^J{d2qLjBj~dJFhcw`chuiJC{n zJ!iz4MZbi+3j?LNw~i;DZg7D>+NyZYdY+a2xeB#v&yt!yi{(n4I#HEPG(Fegx9rVn z1WDo#9kWY2I5DKhAjo*+o=+czdpLXwhrK^}o-Qjea`$1PKA8A*;4cl^=+ZWyr^Jqv z28Z`x77B1Po`XF4U{_q%BS`Z-q^c{XQ`h`$qx?QqlE&pHo?N*IyVaYs0q%L{ujvumt6C5NYOd>PyV3Px}JDKI5!fZjKpryr0g=epmUQdBJ!`7*@o) z>&q3}OSaek1!w4e$EKq3kzb!^eaW$JfV9BuE~9rlFnKuXT3pQhUHz;4czz^)(jO8$ zR}JxKZoE%(Yj50aw_$JJpavpq_gtS2@VPb~n_BXx{{Vq|->LY9X^4(9IKz$?l z0B+hZ@ryiid&^r#v%isxM;+IeNj$J+$MZjhcRSw4B3|j@Q?<{8J|%dI!qWnq=JRcp zk67R4!k=7{KMKlU9gdi!;d9M?Io(U)zlb_Vj$xSjT8zmB@j`a_>c9ej+Qusi^*x;9 zKQxZKJUy%>2ic!sc$>plULuMOYAr%IVm8sL!q9TKOtUKvy}rJc>m;wK;ijCD;RLM!JvJ3=e*lk$o68P;p{O4Kk^^_^z^P>Q^y z{I-v*k@wBag4<3JXCEgx?OgTUm5zEzN>@2OZt47%JF#9(M|0AO=y)&3*M{>(y@?r^ zwX?2v=OlyvHH`^cx_)P+PA$@C{IU2k@K(dYCgR^+HnB{zv}ojH#sq``jyS;Q(!acN z=+TBA6Tifw{`8srIh#7)S*c3j45qE=CXc0jHErSRjW!$Gmy=QZ6d(W)OM6mzWt{<}MCMPPje5 z1M$s0QqUi~&(sgtgW+}G!av!^!+sdnEmj>)>fN<1T@-!WtEMwL{{X;u1^)nm*W!8h zHDmKyl_KvH?7bF<{f{QWJO*1%lU8=S^?JX=_Z>6=gZ(SVJwsLk2>aArM>pc#2gDji zhkdIji*LWtY^9rO+_yVXoBC6wPnx7-ecJrUn^B8?N8?5Ip)KB&b&Ho``i1n9%B1ej z@7}OKR%2h$a!Qq;x+K2s59jJr_TRjJX}9<4{{R+pGtPXg&mj~mv2<1+bB?2)4@#LU z*xnm6$bV=X!KM5}@b`)B;~TH^^AzewQUDzdo1hqJ?jQUKTBKUczD3MYX(`klq{Emu$2?L#7W9u_Uc@}a+AM*k?!F0x#t{bjiA+| zhlC=$+?=_er@F2G00jLR@f`atfduk0YxAr`kF=ny&J)BMiI&~|_c=HmXX{>cozFs? zUC$HvlPS8lnE=5D11CLAXHD9oJxE90J{9m!iEaE#@w>;r0Cnvu8->z5O)6SmG7e#Q zqMRW;ep5C-3jWUE9Ljm04B@3$;*1i%epceY1drp75wgXV@kS@vzjjk{xA>Bi{98Bt z9ji&oY7F@Sxiu~3i_#fe~_)q>lXxh|X-jL}`bS%Q z-H*{-C*lXjLA1W7qhO8{S~dlK3+*vI(fdCo9?a=qiQ+zI(z*WtrcF)NuDYO9=`;V?8vE`o diff --git a/assets/img/best-hd-wallpapers-2.jpg b/assets/img/best-hd-wallpapers-2.jpg deleted file mode 100644 index 3debb2496ef6dd7ea904f1152ff2a05be056d20e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 86357 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 HOppKB_X{UK diff --git a/assets/js/um-crop.js b/assets/js/um-crop.js index bde7a214..3ab83a2c 100644 --- a/assets/js/um-crop.js +++ b/assets/js/um-crop.js @@ -1 +1,1569 @@ -eval(function(p,a,c,k,e,d){e=function(c){return(c35?String.fromCharCode(c+29):c.toString(36))};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('(m($){18 W=2v.4T,D=2v.4S,F=2v.4R,u=2v.4Q;m V(){C $("<4P/>")};$.N=m(T,c){18 O=$(T),1F,A=V(),1k=V(),I=V().r(V()).r(V()).r(V()),B=V().r(V()).r(V()).r(V()),E=$([]),1K,G,l,17={v:0,l:0},Q,M,1l,1g={v:0,l:0},12=0,1J="1H",2k,2j,1t,1s,S,1B,1A,2o,2n,14,1Q,a,b,j,g,f={a:0,b:0,j:0,g:0,H:0,L:0},2u=R.4O,1M=4N.4M,$p,d,i,o,w,h,2p;m 1n(x){C x+17.v-1g.v};m 1m(y){C y+17.l-1g.l};m 1b(x){C x-17.v+1g.v};m 1a(y){C y-17.l+1g.l};m 1z(3J){C 3J.4L-1g.v};m 1y(3I){C 3I.4K-1g.l};m 13(32){18 1i=32||1t,1h=32||1s;C{a:u(f.a*1i),b:u(f.b*1h),j:u(f.j*1i),g:u(f.g*1h),H:u(f.j*1i)-u(f.a*1i),L:u(f.g*1h)-u(f.b*1h)}};m 23(a,b,j,g,31){18 1i=31||1t,1h=31||1s;f={a:u(a/1i||0),b:u(b/1h||0),j:u(j/1i||0),g:u(g/1h||0)};f.H=f.j-f.a;f.L=f.g-f.b};m 1f(){9(!1F||!O.H()){C}17={v:u(O.2t().v),l:u(O.2t().l)};Q=O.2Y();M=O.3H();17.l+=(O.30()-M)>>1;17.v+=(O.2q()-Q)>>1;1B=u(c.4J/1t)||0;1A=u(c.4I/1s)||0;2o=u(F(c.4H/1t||1<<24,Q));2n=u(F(c.4G/1s||1<<24,M));9($().4F=="1.3.2"&&1J=="21"&&!2u["4E"]){17.l+=D(R.1q.2r,2u.2r);17.v+=D(R.1q.2s,2u.2s)}1g=/1H|4D/.1c(1l.q("1p"))?{v:u(1l.2t().v)-1l.2s(),l:u(1l.2t().l)-1l.2r()}:1J=="21"?{v:$(R).2s(),l:$(R).2r()}:{v:0,l:0};G=1n(0);l=1m(0);9(f.j>Q||f.g>M){1U()}};m 1V(3F){9(!1Q){C}A.q({v:1n(f.a),l:1m(f.b)}).r(1k).H(w=f.H).L(h=f.L);1k.r(I).r(E).q({v:0,l:0});I.H(D(w-I.2q()+I.2Y(),0)).L(D(h-I.30()+I.3H(),0));$(B[0]).q({v:G,l:l,H:f.a,L:M});$(B[1]).q({v:G+f.a,l:l,H:w,L:f.b});$(B[2]).q({v:G+f.j,l:l,H:Q-f.j,L:M});$(B[3]).q({v:G+f.a,l:l+f.g,H:w,L:M-f.g});w-=E.2q();h-=E.30();2O(E.3f){15 8:$(E[4]).q({v:w>>1});$(E[5]).q({v:w,l:h>>1});$(E[6]).q({v:w>>1,l:h});$(E[7]).q({l:h>>1});15 4:E.3G(1,3).q({v:w});E.3G(2,4).q({l:h})}9(3F!==Y){9($.N.2Z!=2R){$(R).U($.N.2z,$.N.2Z)}9(c.1T){$(R)[$.N.2z]($.N.2Z=2R)}}9(1j&&I.2q()-I.2Y()==2){I.q("3E",0);3x(m(){I.q("3E","4C")},0)}};m 22(3D){1f();1V(3D);a=1n(f.a);b=1m(f.b);j=1n(f.j);g=1m(f.g)};m 27(2X,2w){c.1P?2X.4B(c.1P,2w):2X.1r()};m 1d(2W){18 x=1b(1z(2W))-f.a,y=1a(1y(2W))-f.b;9(!2p){1f();2p=11;A.1G("4A",m(){2p=Y})}S="";9(c.2D){9(y<=c.1W){S="n"}X{9(y>=f.L-c.1W){S="s"}}9(x<=c.1W){S+="w"}X{9(x>=f.H-c.1W){S+="e"}}}A.q("2V",S?S+"-19":c.26?"4z":"");9(1K){1K.4y()}};m 2S(4x){$("1q").q("2V","");9(c.4w||f.H*f.L==0){27(A.r(B),m(){$(J).1r()})}$(R).U("P",2l);A.P(1d);c.2f(T,13())};m 2C(1X){9(1X.3z!=1){C Y}1f();9(S){$("1q").q("2V",S+"-19");a=1n(f[/w/.1c(S)?"j":"a"]);b=1m(f[/n/.1c(S)?"g":"b"]);$(R).P(2l).1G("1x",2S);A.U("P",1d)}X{9(c.26){2k=G+f.a-1z(1X);2j=l+f.b-1y(1X);A.U("P",1d);$(R).P(2T).1G("1x",m(){c.2f(T,13());$(R).U("P",2T);A.P(1d)})}X{O.1O(1X)}}C Y};m 1w(3C){9(14){9(3C){j=D(G,F(G+Q,a+W(g-b)*14*(j>a||-1)));g=u(D(l,F(l+M,b+W(j-a)/14*(g>b||-1))));j=u(j)}X{g=D(l,F(l+M,b+W(j-a)/14*(g>b||-1)));j=u(D(G,F(G+Q,a+W(g-b)*14*(j>a||-1))));g=u(g)}}};m 1U(){a=F(a,G+Q);b=F(b,l+M);9(W(j-a)<1B){j=a-1B*(jG+Q){a=G+Q-1B}}}9(W(g-b)<1A){g=b-1A*(gl+M){b=l+M-1A}}}j=D(G,F(j,G+Q));g=D(l,F(g,l+M));1w(W(j-a)2o){j=a-2o*(j2n){g=b-2n*(g=0){E.H(5).L(5)}9(o=c.2K){E.q({2K:o,2H:"3m"})}1R(E,{3n:"2J-28",3l:"2I-28",3o:"1e"})}1t=c.4l/Q||1;1s=c.4k/M||1;9(K.a!=3q){23(K.a,K.b,K.j,K.g);K.2F=!K.1r}9(K.1T){c.1T=$.2c({2b:1,2a:"19"},K.1T)}B.29(c.1S+"-4j");1k.29(c.1S+"-4i");3p(i=0;i++<4;){$(I[i-1]).29(c.1S+"-2J"+i)}1R(1k,{4h:"2I-28",4g:"1e"});1R(I,{3o:"1e",2K:"2J-H"});1R(B,{4f:"2I-28",4e:"1e"});9(o=c.3n){$(I[0]).q({2H:"3m",3k:o})}9(o=c.3l){$(I[1]).q({2H:"4d",3k:o})}A.2G(1k.r(I).r(1K)).2G(E);9(1j){9(o=(B.q("3j")||"").3i(/1e=(\\d+)/)){B.q("1e",o[1]/1Z)}9(o=(I.q("3j")||"").3i(/1e=(\\d+)/)){I.q("1e",o[1]/1Z)}}9(K.1r){27(A.r(B))}X{9(K.2F&&1F){1Q=11;A.r(B).2E(c.1P||0);22()}}14=(d=(c.4c||"").4b(/:/))[0]/d[1];O.r(B).U("1O",2A);9(c.1E||c.1D===Y){A.U("P",1d).U("1O",2C);$(3h).U("19",2B)}X{9(c.1D||c.1E===Y){9(c.2D||c.26){A.P(1d).1O(2C)}$(3h).19(2B)}9(!c.4a){O.r(B).1O(2A)}}c.1D=c.1E=1Y};J.1o=m(){25({1E:11});A.r(B).1o()};J.49=m(){C c};J.33=25;J.48=13;J.47=23;J.46=1N;J.45=22;18 1j=(/44 ([\\w.]+)/i.43(1M)||[])[1],3c=/42/i.1c(1M),3d=/41/i.1c(1M)&&!/3Z/i.1c(1M);$p=O;3g($p.3f){12=D(12,!1L($p.q("z-3e"))?$p.q("z-3e"):12);9($p.q("1p")=="21"){1J="21"}$p=$p.20(":3Y(1q)")}12=c.1I||12;9(1j){O.3X("3W","3V")}$.N.2z=1j||3d?"3U":"3T";9(3c){1K=V().q({H:"1Z%",L:"1Z%",1p:"1H",1I:12+2||2})}A.r(B).q({3b:"3a",1p:1J,3S:"3a",1I:12||"0"});A.q({1I:12+2||2});1k.r(I).q({1p:"1H",36:0});T.35||T.3R=="35"||!O.2y("3Q")?2x():O.1G("3P",2x);9(!1F&&1j&&1j>=7){T.34=T.34}};$.2w.N=m(Z){Z=Z||{};J.3O(m(){9($(J).1C("N")){9(Z.1o){$(J).1C("N").1o();$(J).3N("N")}X{$(J).1C("N").33(Z)}}X{9(!Z.1o){9(Z.1D===1Y&&Z.1E===1Y){Z.1D=11}$(J).1C("N",3M $.N(J,Z))}}});9(Z.3L){C $(J).1C("N")}C J}})(3K);',62,304,'|||||||||if|x1|y1|_7|||_23|y2|||x2||top|function||||css|add|||_4|left|||||_a|_d|return|_2|_e|_3|_10|width|_c|this|_55|height|_13|imgAreaSelect|_8|mousemove|_12|document|_1c|_6|unbind|_5|_1|else|false|_58||true|_16|_2c|_21|case|_50|_11|var|resize|_29|_28|test|_3a|opacity|_30|_15|sy|sx|_35|_b|_14|_27|_26|remove|position|body|hide|_1b|_1a|break|_45|_42|mouseup|evY|evX|_1e|_1d|data|enable|disable|_9|one|absolute|zIndex|_17|_f|isNaN|ua|_4a|mousedown|fadeSpeed|_22|_51|classPrefix|keys|_31|_32|resizeMargin|_40|undefined|100|parent|fixed|_36|_2e||_4f|movable|_38|color|addClass|ctrl|shift|extend|_54|altKey|onSelectEnd|onSelectChange|_49|_4c|_19|_18|_3e|_48|_20|_1f|_25|outerWidth|scrollTop|scrollLeft|offset|_24|Math|fn|_4e|is|keyPress|_4b|_4d|_3f|resizable|fadeIn|show|append|borderStyle|background|border|borderWidth|handles|_53|key|switch|alt|arrows|_34|_3c|_41|_44|cursor|_3b|_39|innerWidth|onKeyPress|outerHeight|_2f|_2d|setOptions|src|complete|fontSize||||hidden|visibility|_56|_57|index|length|while|window|match|filter|borderColor|borderColor2|solid|borderColor1|borderOpacity|for|null|_52|default|originalEvent|ctrlKey|shiftKey|onInit|setTimeout|onSelectStart|which|_47|_46|_43|_37|margin|_33|slice|innerHeight|_2b|_2a|jQuery|instance|new|removeData|each|load|img|readyState|overflow|keypress|keydown|on|unselectable|attr|not|chrome||webkit|opera|exec|msie|update|cancelSelection|setSelection|getSelection|getOptions|persistent|split|aspectRatio|dashed|outerOpacity|outerColor|selectionOpacity|selectionColor|selection|outer|imageHeight|imageWidth|parseInt|handle|corners|in|keyCode|imgareaselect|animated|instanceof|visible|preventDefault|autoHide|_3d|toggle|move|mouseout|fadeOut|auto|relative|getBoundingClientRect|jquery|maxHeight|maxWidth|minHeight|minWidth|pageY|pageX|userAgent|navigator|documentElement|div|round|min|max|abs'.split('|'))) +(function (factory) { + if (typeof define === "function" && define.amd) { + // AMD. Register as anonymous module. + define(["jquery"], factory); + } else if (typeof exports === "object") { + // Node / CommonJS + factory(require("jquery")); + } else { + // Browser globals. + factory(jQuery); + } +})(function ($) { + + "use strict"; + + var $window = $(window), + $document = $(document), + location = window.location, + + // Constants + TRUE = true, + FALSE = false, + NULL = null, + NAN = NaN, + INFINITY = Infinity, + STRING_UNDEFINED = "undefined", + STRING_DIRECTIVE = "directive", + CROPPER_NAMESPACE = ".cropper", + + // RegExps + REGEXP_DIRECTIVES = /^(e|n|w|s|ne|nw|sw|se|all|crop|move|zoom)$/, + REGEXP_OPTIONS = /^(x|y|width|height)$/, + REGEXP_PROPERTIES = /^(naturalWidth|naturalHeight|width|height|aspectRatio|ratio|rotate)$/, + + // Classes + CLASS_MODAL = "cropper-modal", + CLASS_HIDDEN = "cropper-hidden", + CLASS_INVISIBLE = "cropper-invisible", + CLASS_MOVE = "cropper-move", + CLASS_CROP = "cropper-crop", + CLASS_DISABLED = "cropper-disabled", + + // Events + EVENT_MOUSE_DOWN = "mousedown touchstart", + EVENT_MOUSE_MOVE = "mousemove touchmove", + EVENT_MOUSE_UP = "mouseup mouseleave touchend touchleave touchcancel", + EVENT_WHEEL = "wheel mousewheel DOMMouseScroll", + EVENT_RESIZE = "resize" + CROPPER_NAMESPACE, // Bind to window with namespace + EVENT_DBLCLICK = "dblclick", + EVENT_BUILD = "build" + CROPPER_NAMESPACE, + EVENT_BUILT = "built" + CROPPER_NAMESPACE, + EVENT_DRAG_START = "dragstart" + CROPPER_NAMESPACE, + EVENT_DRAG_MOVE = "dragmove" + CROPPER_NAMESPACE, + EVENT_DRAG_END = "dragend" + CROPPER_NAMESPACE, + + // Functions + isNumber = function (n) { + return typeof n === "number"; + }, + + toArray = function (obj, offset) { + var args = []; + + if (typeof offset === "number") { // It's necessary for IE8 + args.push(offset); + } + + return args.slice.apply(obj, args); + }, + + // Custom proxy to avoid jQuery's guid + proxy = function (fn, context) { + var args = toArray(arguments, 2); + + return function () { + return fn.apply(context, args.concat(toArray(arguments))); + }; + }, + + // Constructor + Cropper = function (element, options) { + this.element = element; + this.$element = $(element); + this.defaults = $.extend({}, Cropper.DEFAULTS, $.isPlainObject(options) ? options : {}); + this.$original = NULL; + this.ready = FALSE; + this.built = FALSE; + this.cropped = FALSE; + this.rotated = FALSE; + this.disabled = FALSE; + this.replaced = FALSE; + this.init(); + }, + + // Others + sqrt = Math.sqrt, + min = Math.min, + max = Math.max, + abs = Math.abs, + sin = Math.sin, + cos = Math.cos, + num = parseFloat; + + Cropper.prototype = { + constructor: Cropper, + + support: { + canvas: $.isFunction($("")[0].getContext) + }, + + init: function () { + var defaults = this.defaults; + + $.each(defaults, function (i, n) { + switch (i) { + case "aspectRatio": + defaults[i] = abs(num(n)) || NAN; // 0 -> NaN + break; + + case "autoCropArea": + defaults[i] = abs(num(n)) || 0.8; // 0 | NaN -> 0.8 + break; + + case "minWidth": + case "minHeight": + defaults[i] = abs(num(n)) || 0; // NaN -> 0 + break; + + case "maxWidth": + case "maxHeight": + defaults[i] = abs(num(n)) || INFINITY; // 0 | NaN -> Infinity + break; + + // No default + } + }); + + // Set default image data + this.image = { + rotate: 0 + }; + + this.load(); + }, + + load: function () { + var _this = this, + $this = this.$element, + element = this.element, + image = this.image, + crossOrigin = "", + $clone, + url; + + if ($this.is("img")) { + url = $this.prop("src"); + } else if ($this.is("canvas") && this.support.canvas) { + url = element.toDataURL(); + } + + if (!url) { + return; + } + + // Reset image rotate degree + if (this.replaced) { + image.rotate = 0; + } + + if (this.defaults.checkImageOrigin) { + if ($this.prop("crossOrigin") || this.isCrossOriginURL(url)) { + crossOrigin = " crossOrigin"; + } + } + + this.$clone = ($clone = $("')); + + $clone.one("load", function () { + image.naturalWidth = this.naturalWidth || $clone.width(); + image.naturalHeight = this.naturalHeight || $clone.height(); + image.aspectRatio = image.naturalWidth / image.naturalHeight; + + _this.url = url; + _this.ready = TRUE; + _this.build(); + }); + + // Hide and prepend the clone iamge to the document body (Don't append to). + $clone.addClass(CLASS_INVISIBLE).prependTo("body"); + }, + + isCrossOriginURL: function (url) { + var parts = url.match(/^(https?:)\/\/([^\:\/\?#]+):?(\d*)/i); + + if ((parts && (parts[1] !== location.protocol || parts[2] !== location.hostname || parts[3] !== location.port))) { + return TRUE; + } + + return FALSE; + }, + + build: function () { + var $this = this.$element, + defaults = this.defaults, + buildEvent, + $cropper; + + if (!this.ready) { + return; + } + + if (this.built) { + this.unbuild(); + } + + $this.one(EVENT_BUILD, defaults.build); // Only trigger once + buildEvent = $.Event(EVENT_BUILD); + $this.trigger(buildEvent); + + if (buildEvent.isDefaultPrevented()) { + return; + } + + // Create cropper elements + this.$cropper = ($cropper = $(Cropper.TEMPLATE)); + + // Hide the original image + $this.addClass(CLASS_HIDDEN); + + // Show and prepend the clone iamge to the cropper + this.$clone.removeClass(CLASS_INVISIBLE).prependTo($cropper); + + // Save original image for rotation + if (!this.rotated) { + this.$original = this.$clone.clone(); + + // Append the image to document to avoid "NS_ERROR_NOT_AVAILABLE" error on Firefox when call the "drawImage" method. + this.$original.addClass(CLASS_HIDDEN).prependTo(this.$cropper); + + this.originalImage = $.extend({}, this.image); + } + + this.$container = $this.parent(); + this.$container.append($cropper); + + this.$canvas = $cropper.find(".cropper-canvas"); + this.$dragger = $cropper.find(".cropper-dragger"); + this.$viewer = $cropper.find(".cropper-viewer"); + + defaults.autoCrop ? (this.cropped = TRUE) : this.$dragger.addClass(CLASS_HIDDEN); + defaults.dragCrop && this.setDragMode("crop"); + defaults.modal && this.$canvas.addClass(CLASS_MODAL); + !defaults.dashed && this.$dragger.find(".cropper-dashed").addClass(CLASS_HIDDEN); + !defaults.movable && this.$dragger.find(".cropper-face").data(STRING_DIRECTIVE, "move"); + !defaults.resizable && this.$dragger.find(".cropper-line, .cropper-point").addClass(CLASS_HIDDEN); + + this.addListeners(); + this.initPreview(); + + this.built = TRUE; // Set `true` before update + this.update(); + this.replaced = FALSE; // Reset to `false` after update + + $this.one(EVENT_BUILT, defaults.built); // Only trigger once + $this.trigger(EVENT_BUILT); + }, + + unbuild: function () { + if (!this.built) { + return; + } + + this.built = FALSE; + this.removeListeners(); + + this.$preview.empty(); + this.$preview = NULL; + + this.$dragger = NULL; + this.$canvas = NULL; + this.$container = NULL; + + this.$cropper.remove(); + this.$cropper = NULL; + }, + + update: function (data) { + this.initContainer(); + this.initCropper(); + this.initImage(); + this.initDragger(); + + if (data) { + this.setData(data, TRUE); + this.setDragMode("crop"); + } else { + this.setData(this.defaults.data); + } + }, + + resize: function () { + clearTimeout(this.resizing); + this.resizing = setTimeout($.proxy(this.update, this, this.getData()), 200); + }, + + preview: function () { + var image = this.image, + dragger = this.dragger, + width = image.width, + height = image.height, + left = dragger.left - image.left, + top = dragger.top - image.top; + + this.$viewer.find("img").css({ + width: width, + height: height, + marginLeft: -left, + marginTop: -top + }); + + this.$preview.each(function () { + var $this = $(this), + ratio = $this.width() / dragger.width; + + $this.find("img").css({ + width: width * ratio, + height: height * ratio, + marginLeft: -left * ratio, + marginTop: -top * ratio + }); + }); + }, + + addListeners: function () { + var defaults = this.defaults; + + this.$element.on(EVENT_DRAG_START, defaults.dragstart).on(EVENT_DRAG_MOVE, defaults.dragmove).on(EVENT_DRAG_END, defaults.dragend); + this.$cropper.on(EVENT_MOUSE_DOWN, $.proxy(this.dragstart, this)).on(EVENT_DBLCLICK, $.proxy(this.dblclick, this)); + + if (defaults.zoomable) { + this.$cropper.on(EVENT_WHEEL, $.proxy(this.wheel, this)); + } + + if (defaults.multiple) { + this.$cropper.on(EVENT_MOUSE_MOVE, $.proxy(this.dragmove, this)).on(EVENT_MOUSE_UP, $.proxy(this.dragend, this)); + } else { + $document.on(EVENT_MOUSE_MOVE, (this._dragmove = proxy(this.dragmove, this))).on(EVENT_MOUSE_UP, (this._dragend = proxy(this.dragend, this))); + } + + $window.on(EVENT_RESIZE, (this._resize = proxy(this.resize, this))); + }, + + removeListeners: function () { + var defaults = this.defaults; + + this.$element.off(EVENT_DRAG_START, defaults.dragstart).off(EVENT_DRAG_MOVE, defaults.dragmove).off(EVENT_DRAG_END, defaults.dragend); + this.$cropper.off(EVENT_MOUSE_DOWN, this.dragstart).off(EVENT_DBLCLICK, this.dblclick); + + if (defaults.zoomable) { + this.$cropper.off(EVENT_WHEEL, this.wheel); + } + + if (defaults.multiple) { + this.$cropper.off(EVENT_MOUSE_MOVE, this.dragmove).off(EVENT_MOUSE_UP, this.dragend); + } else { + $document.off(EVENT_MOUSE_MOVE, this._dragmove).off(EVENT_MOUSE_UP, this._dragend); + } + + $window.off(EVENT_RESIZE, this._resize); + }, + + initPreview: function () { + var img = ''; + + this.$preview = $(this.defaults.preview); + this.$viewer.html(img); + this.$preview.html(img).find("img").css("cssText", "min-width:0!important;min-height:0!important;max-width:none!important;max-height:none!important;"); + }, + + initContainer: function () { + var $container = this.$container; + + this.container = { + width: max($container.width(), 300), + height: max($container.height(), 150) + }; + }, + + initCropper: function () { + var container = this.container, + image = this.image, + cropper; + + if (((image.naturalWidth * container.height / image.naturalHeight) - container.width) >= 0) { + cropper = { + width: container.width, + height: container.width / image.aspectRatio, + left: 0 + }; + + cropper.top = (container.height - cropper.height) / 2; + } else { + cropper = { + width: container.height * image.aspectRatio, + height: container.height, + top: 0 + }; + + cropper.left = (container.width - cropper.width) / 2; + } + + this.$cropper.css({ + width: cropper.width, + height: cropper.height, + left: cropper.left, + top: cropper.top + }); + + this.cropper = cropper; + }, + + initImage: function () { + var image = this.image, + cropper = this.cropper, + defaultImage = { + _width: cropper.width, + _height: cropper.height, + width: cropper.width, + height: cropper.height, + left: 0, + top: 0, + ratio: cropper.width / image.naturalWidth + }; + + this.defaultImage = $.extend({}, image, defaultImage); + + if (image._width !== cropper.width || image._height !== cropper.height) { + $.extend(image, defaultImage); + } else { + image = $.extend({}, defaultImage, image); + + // Reset image ratio + if (this.replaced) { + image.ratio = defaultImage.ratio; + } + } + + this.image = image; + this.renderImage(); + }, + + renderImage: function (mode) { + var image = this.image; + + if (mode === "zoom") { + image.left -= (image.width - image.oldWidth) / 2; + image.top -= (image.height - image.oldHeight) / 2; + } + + image.left = min(max(image.left, image._width - image.width), 0); + image.top = min(max(image.top, image._height - image.height), 0); + + this.$clone.css({ + width: image.width, + height: image.height, + marginLeft: image.left, + marginTop: image.top + }); + + if (mode) { + this.defaults.done(this.getData()); + this.preview(); + } + }, + + initDragger: function () { + var defaults = this.defaults, + cropper = this.cropper, + // If not set, use the original aspect ratio of the image. + aspectRatio = defaults.aspectRatio || this.image.aspectRatio, + ratio = this.image.ratio, + dragger; + + if (((cropper.height * aspectRatio) - cropper.width) >= 0) { + dragger = { + height: cropper.width / aspectRatio, + width: cropper.width, + left: 0, + top: (cropper.height - (cropper.width / aspectRatio)) / 2, + maxWidth: cropper.width, + maxHeight: cropper.width / aspectRatio + }; + } else { + dragger = { + height: cropper.height, + width: cropper.height * aspectRatio, + left: (cropper.width - (cropper.height * aspectRatio)) / 2, + top: 0, + maxWidth: cropper.height * aspectRatio, + maxHeight: cropper.height + }; + } + + dragger.minWidth = 0; + dragger.minHeight = 0; + + if (defaults.aspectRatio) { + if (isFinite(defaults.maxWidth)) { + dragger.maxWidth = min(dragger.maxWidth, defaults.maxWidth * ratio); + dragger.maxHeight = dragger.maxWidth / aspectRatio; + } else if (isFinite(defaults.maxHeight)) { + dragger.maxHeight = min(dragger.maxHeight, defaults.maxHeight * ratio); + dragger.maxWidth = dragger.maxHeight * aspectRatio; + } + + if (defaults.minWidth > 0) { + dragger.minWidth = max(0, defaults.minWidth * ratio); + dragger.minHeight = dragger.minWidth / aspectRatio; + } else if (defaults.minHeight > 0) { + dragger.minHeight = max(0, defaults.minHeight * ratio); + dragger.minWidth = dragger.minHeight * aspectRatio; + } + } else { + dragger.maxWidth = min(dragger.maxWidth, defaults.maxWidth * ratio); + dragger.maxHeight = min(dragger.maxHeight, defaults.maxHeight * ratio); + dragger.minWidth = max(0, defaults.minWidth * ratio); + dragger.minHeight = max(0, defaults.minHeight * ratio); + } + + // minWidth can't be greater than maxWidth, and minHeight too. + dragger.minWidth = min(dragger.maxWidth, dragger.minWidth); + dragger.minHeight = min(dragger.maxHeight, dragger.minHeight); + + // Center the dragger by default + dragger.height *= defaults.autoCropArea; + dragger.width *= defaults.autoCropArea; + dragger.left = (cropper.width - dragger.width) / 2; + dragger.top = (cropper.height - dragger.height) / 2; + dragger.oldLeft = dragger.left; + dragger.oldTop = dragger.top; + + this.defaultDragger = dragger; + this.dragger = $.extend({}, dragger); + }, + + renderDragger: function () { + var dragger = this.dragger, + cropper = this.cropper; + + if (dragger.width > dragger.maxWidth) { + dragger.width = dragger.maxWidth; + dragger.left = dragger.oldLeft; + } else if (dragger.width < dragger.minWidth) { + dragger.width = dragger.minWidth; + dragger.left = dragger.oldLeft; + } + + if (dragger.height > dragger.maxHeight) { + dragger.height = dragger.maxHeight; + dragger.top = dragger.oldTop; + } else if (dragger.height < dragger.minHeight) { + dragger.height = dragger.minHeight; + dragger.top = dragger.oldTop; + } + + dragger.left = min(max(dragger.left, 0), cropper.width - dragger.width); + dragger.top = min(max(dragger.top, 0), cropper.height - dragger.height); + dragger.oldLeft = dragger.left; + dragger.oldTop = dragger.top; + + // Re-render the dragger + this.dragger = dragger; + + if (!this.disabled) { + this.defaults.done(this.getData()); + } + + this.$dragger.css({ + width: dragger.width, + height: dragger.height, + left: dragger.left, + top: dragger.top + }); + + this.preview(); + }, + + reset: function (deep) { + if (!this.cropped) { + return; + } + + if (deep) { + this.defaults.data = {}; + } + + this.image = $.extend({}, this.defaultImage); + this.renderImage(); + this.dragger = $.extend({}, this.defaultDragger); + this.setData(this.defaults.data); + }, + + clear: function () { + if (!this.cropped) { + return; + } + + this.cropped = FALSE; + + this.setData({ + x: 0, + y: 0, + width: 0, + height: 0 + }); + + this.$canvas.removeClass(CLASS_MODAL); + this.$dragger.addClass(CLASS_HIDDEN); + }, + + destroy: function () { + var $this = this.$element; + + if (!this.ready) { + return; + } + + this.unbuild(); + $this.removeClass(CLASS_HIDDEN).removeData("cropper"); + + if (this.rotated) { + $this.attr("src", this.$original.attr("src")); + } + }, + + replace: function (url, /*INTERNAL*/ rotated) { + var _this = this, + $this = this.$element, + element = this.element, + context; + + if (url && url !== this.url && url !== $this.attr("src")) { + if (!rotated) { + this.rotated = FALSE; + this.replaced = TRUE; + } + + if ($this.is("img")) { + $this.attr("src", url); + this.load(); + } else if ($this.is("canvas") && this.support.canvas) { + context = element.getContext("2d"); + + $('').one("load", function () { + element.width = this.width; + element.height = this.height; + context.clearRect(0, 0, element.width, element.height); + context.drawImage(this, 0, 0); + _this.load(); + }); + } + } + }, + + setData: function (data, /*INTERNAL*/ once) { + var cropper = this.cropper, + dragger = this.dragger, + image = this.image, + aspectRatio = this.defaults.aspectRatio; + + if (!this.built || typeof data === STRING_UNDEFINED) { + return; + } + + if (data === NULL || $.isEmptyObject(data)) { + dragger = $.extend({}, this.defaultDragger); + } + + if ($.isPlainObject(data) && !$.isEmptyObject(data)) { + + if (!once) { + this.defaults.data = data; + } + + data = this.transformData(data); + + if (isNumber(data.x) && data.x <= cropper.width - image.left) { + dragger.left = data.x + image.left; + } + + if (isNumber(data.y) && data.y <= cropper.height - image.top) { + dragger.top = data.y + image.top; + } + + if (aspectRatio) { + if (isNumber(data.width) && data.width <= dragger.maxWidth && data.width >= dragger.minWidth) { + dragger.width = data.width; + dragger.height = dragger.width / aspectRatio; + } else if (isNumber(data.height) && data.height <= dragger.maxHeight && data.height >= dragger.minHeight) { + dragger.height = data.height; + dragger.width = dragger.height * aspectRatio; + } + } else { + if (isNumber(data.width) && data.width <= dragger.maxWidth && data.width >= dragger.minWidth) { + dragger.width = data.width; + } + + if (isNumber(data.height) && data.height <= dragger.maxHeight && data.height >= dragger.minHeight) { + dragger.height = data.height; + } + } + } + + this.dragger = dragger; + this.renderDragger(); + }, + + getData: function (rounded) { + var dragger = this.dragger, + image = this.image, + data = {}; + + if (this.built) { + data = { + x: dragger.left - image.left, + y: dragger.top - image.top, + width: dragger.width, + height: dragger.height + }; + + data = this.transformData(data, TRUE, rounded); + } + + return data; + }, + + transformData: function (data, reversed, rounded) { + var ratio = this.image.ratio, + result = {}; + + $.each(data, function (i, n) { + n = num(n); + + if (REGEXP_OPTIONS.test(i) && !isNaN(n)) { + result[i] = reversed ? (rounded ? Math.round(n / ratio) : n / ratio) : n * ratio; + } + }); + + return result; + }, + + setAspectRatio: function (aspectRatio) { + var freeRatio = aspectRatio === "auto"; + + aspectRatio = num(aspectRatio); + + if (freeRatio || (!isNaN(aspectRatio) && aspectRatio > 0)) { + this.defaults.aspectRatio = freeRatio ? NAN : aspectRatio; + + if (this.built) { + this.initDragger(); + this.renderDragger(); + } + } + }, + + getImageData: function () { + var data = {}; + + if (this.ready) { + $.each(this.image, function (name, value) { + if (REGEXP_PROPERTIES.test(name)) { + data[name] = value; + } + }); + } + + return data; + }, + + getDataURL: function (options, type, quality) { + var canvas = $("")[0], + data = this.getData(), + dataURL = "", + context; + + if (!$.isPlainObject(options)) { + quality = type; + type = options; + options = {}; + } + + options = $.extend({ + width: data.width, + height: data.height + }, options); + + if (this.cropped && this.support.canvas) { + canvas.width = options.width; + canvas.height = options.height; + context = canvas.getContext("2d"); + + if (type === "image/jpeg") { + context.fillStyle = "#fff"; + context.fillRect(0, 0, options.width, options.height); + } + + context.drawImage(this.$clone[0], data.x, data.y, data.width, data.height, 0, 0, options.width, options.height); + dataURL = canvas.toDataURL(type, quality); + } + + return dataURL; + }, + + setDragMode: function (mode) { + var $canvas = this.$canvas, + defaults = this.defaults, + cropable = FALSE, + movable = FALSE; + + if (!this.built || this.disabled) { + return; + } + + switch (mode) { + case "crop": + if (defaults.dragCrop) { + cropable = TRUE; + $canvas.data(STRING_DIRECTIVE, mode); + } + + break; + + case "move": + movable = TRUE; + $canvas.data(STRING_DIRECTIVE, mode); + + break; + + default: + $canvas.removeData(STRING_DIRECTIVE); + } + + $canvas.toggleClass(CLASS_CROP, cropable).toggleClass(CLASS_MOVE, movable); + }, + + enable: function () { + if (this.built) { + this.disabled = FALSE; + this.$cropper.removeClass(CLASS_DISABLED); + } + }, + + disable: function () { + if (this.built) { + this.disabled = TRUE; + this.$cropper.addClass(CLASS_DISABLED); + } + }, + + rotate: function (degree) { + var image = this.image; + + degree = num(degree) || 0; + + if (!this.built || degree === 0 || this.disabled || !this.defaults.rotatable || !this.support.canvas) { + return; + } + + this.rotated = TRUE; + degree = (image.rotate = (image.rotate + degree) % 360); + + // replace with "true" to prevent to override the original image + this.replace(this.getRotatedDataURL(degree), true); + }, + + getRotatedDataURL: function (degree) { + var canvas = $("")[0], + context = canvas.getContext("2d"), + arc = degree * Math.PI / 180, + deg = abs(degree) % 180, + acuteAngle = deg > 90 ? (180 - deg) : deg, + acuteAngleArc = acuteAngle * Math.PI / 180, + originalImage = this.originalImage, + naturalWidth = originalImage.naturalWidth, + naturalHeight = originalImage.naturalHeight, + width = abs(naturalWidth * cos(acuteAngleArc) + naturalHeight * sin(acuteAngleArc)), + height = abs(naturalWidth * sin(acuteAngleArc) + naturalHeight * cos(acuteAngleArc)); + + canvas.width = width; + canvas.height = height; + context.save(); + context.translate(width / 2, height / 2); + context.rotate(arc); + context.drawImage(this.$original[0], -naturalWidth / 2, -naturalHeight / 2, naturalWidth, naturalHeight); + context.restore(); + + return canvas.toDataURL(); + }, + + zoom: function (delta) { + var image = this.image, + width, + height, + range; + + delta = num(delta); + + if (!this.built || !delta || this.disabled || !this.defaults.zoomable) { + return; + } + + width = image.width * (1 + delta); + height = image.height * (1 + delta); + range = width / image._width; + + if (range > 10) { + return; + } + + if (range < 1) { + width = image._width; + height = image._height; + } + + if (range <= 1) { + this.setDragMode("crop"); + } else { + this.setDragMode("move"); + } + + image.oldWidth = image.width; + image.oldHeight = image.height; + + image.width = width; + image.height = height; + image.ratio = image.width / image.naturalWidth; + + this.renderImage("zoom"); + }, + + dblclick: function () { + if (this.disabled) { + return; + } + + if (this.$canvas.hasClass(CLASS_CROP)) { + this.setDragMode("move"); + } else { + this.setDragMode("crop"); + } + }, + + wheel: function (event) { + var e = event.originalEvent, + msDeltaY = 117.25, // IE + mozDelatY = 5, // Firefox + webkitDelatY = 166.66665649414062, // Chrome, Opera + zoomDelta = 0.1, // 10% + delta; + + if (this.disabled) { + return; + } + + event.preventDefault(); + + if (e.deltaY) { + delta = e.deltaY; + delta = delta % mozDelatY === 0 ? delta / mozDelatY : delta % msDeltaY === 0 ? delta / msDeltaY : delta / webkitDelatY; + } else { + delta = e.wheelDelta ? -e.wheelDelta / 120 : (e.detail ? e.detail / 3 : 0); + } + + this.zoom(delta * zoomDelta); + }, + + dragstart: function (event) { + var touches = event.originalEvent.touches, + e = event, + directive, + dragStartEvent, + touchesLength; + + if (this.disabled) { + return; + } + + if (touches) { + touchesLength = touches.length; + + if (touchesLength > 1) { + if (this.defaults.zoomable && touchesLength === 2) { + e = touches[1]; + this.startX2 = e.pageX; + this.startY2 = e.pageY; + directive = "zoom"; + } else { + return; + } + } + + e = touches[0]; + } + + directive = directive || $(e.target).data(STRING_DIRECTIVE); + + if (REGEXP_DIRECTIVES.test(directive)) { + event.preventDefault(); + + dragStartEvent = $.Event(EVENT_DRAG_START); + this.$element.trigger(dragStartEvent); + + if (dragStartEvent.isDefaultPrevented()) { + return; + } + + this.directive = directive; + this.cropping = FALSE; + this.startX = e.pageX; + this.startY = e.pageY; + + if (directive === "crop") { + this.cropping = TRUE; + this.$canvas.addClass(CLASS_MODAL); + } + } + }, + + dragmove: function (event) { + var touches = event.originalEvent.touches, + e = event, + dragMoveEvent, + touchesLength; + + if (this.disabled) { + return; + } + + if (touches) { + touchesLength = touches.length; + + if (touchesLength > 1) { + if (this.defaults.zoomable && touchesLength === 2) { + e = touches[1]; + this.endX2 = e.pageX; + this.endY2 = e.pageY; + } else { + return; + } + } + + e = touches[0]; + } + + if (this.directive) { + event.preventDefault(); + + dragMoveEvent = $.Event(EVENT_DRAG_MOVE); + this.$element.trigger(dragMoveEvent); + + if (dragMoveEvent.isDefaultPrevented()) { + return; + } + + this.endX = e.pageX; + this.endY = e.pageY; + + this.dragging(); + } + }, + + dragend: function (event) { + var dragEndEvent; + + if (this.disabled) { + return; + } + + if (this.directive) { + event.preventDefault(); + + dragEndEvent = $.Event(EVENT_DRAG_END); + this.$element.trigger(dragEndEvent); + + if (dragEndEvent.isDefaultPrevented()) { + return; + } + + if (this.cropping) { + this.cropping = FALSE; + this.$canvas.toggleClass(CLASS_MODAL, this.cropped && this.defaults.modal); + } + + this.directive = ""; + } + }, + + dragging: function () { + var directive = this.directive, + image = this.image, + cropper = this.cropper, + maxWidth = cropper.width, + maxHeight = cropper.height, + dragger = this.dragger, + width = dragger.width, + height = dragger.height, + left = dragger.left, + top = dragger.top, + right = left + width, + bottom = top + height, + renderable = TRUE, + defaults = this.defaults, + aspectRatio = defaults.aspectRatio, + range = { + x: this.endX - this.startX, + y: this.endY - this.startY + }, + offset; + + if (aspectRatio) { + range.X = range.y * aspectRatio; + range.Y = range.x / aspectRatio; + } + + switch (directive) { + // Move dragger + case "all": + left += range.x; + top += range.y; + + break; + + // Resize dragger + case "e": + if (range.x >= 0 && (right >= maxWidth || aspectRatio && (top <= 0 || bottom >= maxHeight))) { + renderable = FALSE; + break; + } + + width += range.x; + + if (aspectRatio) { + height = width / aspectRatio; + top -= range.Y / 2; + } + + if (width < 0) { + directive = "w"; + width = 0; + } + + break; + + case "n": + if (range.y <= 0 && (top <= 0 || aspectRatio && (left <= 0 || right >= maxWidth))) { + renderable = FALSE; + break; + } + + height -= range.y; + top += range.y; + + if (aspectRatio) { + width = height * aspectRatio; + left += range.X / 2; + } + + if (height < 0) { + directive = "s"; + height = 0; + } + + break; + + case "w": + if (range.x <= 0 && (left <= 0 || aspectRatio && (top <= 0 || bottom >= maxHeight))) { + renderable = FALSE; + break; + } + + width -= range.x; + left += range.x; + + if (aspectRatio) { + height = width / aspectRatio; + top += range.Y / 2; + } + + if (width < 0) { + directive = "e"; + width = 0; + } + + break; + + case "s": + if (range.y >= 0 && (bottom >= maxHeight || aspectRatio && (left <= 0 || right >= maxWidth))) { + renderable = FALSE; + break; + } + + height += range.y; + + if (aspectRatio) { + width = height * aspectRatio; + left -= range.X / 2; + } + + if (height < 0) { + directive = "n"; + height = 0; + } + + break; + + case "ne": + if (aspectRatio) { + if (range.y <= 0 && (top <= 0 || right >= maxWidth)) { + renderable = FALSE; + break; + } + + height -= range.y; + top += range.y; + width = height * aspectRatio; + } else { + if (range.x >= 0) { + if (right < maxWidth) { + width += range.x; + } else if (range.y <= 0 && top <= 0) { + renderable = FALSE; + } + } else { + width += range.x; + } + + if (range.y <= 0) { + if (top > 0) { + height -= range.y; + top += range.y; + } + } else { + height -= range.y; + top += range.y; + } + } + + if (height < 0) { + directive = "sw"; + height = 0; + width = 0; + } + + break; + + case "nw": + if (aspectRatio) { + if (range.y <= 0 && (top <= 0 || left <= 0)) { + renderable = FALSE; + break; + } + + height -= range.y; + top += range.y; + width = height * aspectRatio; + left += range.X; + } else { + if (range.x <= 0) { + if (left > 0) { + width -= range.x; + left += range.x; + } else if (range.y <= 0 && top <= 0) { + renderable = FALSE; + } + } else { + width -= range.x; + left += range.x; + } + + if (range.y <= 0) { + if (top > 0) { + height -= range.y; + top += range.y; + } + } else { + height -= range.y; + top += range.y; + } + } + + if (height < 0) { + directive = "se"; + height = 0; + width = 0; + } + + break; + + case "sw": + if (aspectRatio) { + if (range.x <= 0 && (left <= 0 || bottom >= maxHeight)) { + renderable = FALSE; + break; + } + + width -= range.x; + left += range.x; + height = width / aspectRatio; + } else { + if (range.x <= 0) { + if (left > 0) { + width -= range.x; + left += range.x; + } else if (range.y >= 0 && bottom >= maxHeight) { + renderable = FALSE; + } + } else { + width -= range.x; + left += range.x; + } + + if (range.y >= 0) { + if (bottom < maxHeight) { + height += range.y; + } + } else { + height += range.y; + } + } + + if (width < 0) { + directive = "ne"; + height = 0; + width = 0; + } + + break; + + case "se": + if (aspectRatio) { + if (range.x >= 0 && (right >= maxWidth || bottom >= maxHeight)) { + renderable = FALSE; + break; + } + + width += range.x; + height = width / aspectRatio; + } else { + if (range.x >= 0) { + if (right < maxWidth) { + width += range.x; + } else if (range.y >= 0 && bottom >= maxHeight) { + renderable = FALSE; + } + } else { + width += range.x; + } + + if (range.y >= 0) { + if (bottom < maxHeight) { + height += range.y; + } + } else { + height += range.y; + } + } + + if (width < 0) { + directive = "nw"; + height = 0; + width = 0; + } + + break; + + // Move image + case "move": + image.left += range.x; + image.top += range.y; + this.renderImage("move"); + renderable = FALSE; + + break; + + // Scale image + case "zoom": + if (defaults.zoomable) { + this.zoom(function (x, y, x1, y1, x2, y2) { + return (sqrt(x2 * x2 + y2 * y2) - sqrt(x1 * x1 + y1 * y1)) / sqrt(x * x + y * y); + }( + image.width, + image.height, + abs(this.startX - this.startX2), + abs(this.startY - this.startY2), + abs(this.endX - this.endX2), + abs(this.endY - this.endY2) + )); + + this.endX2 = this.startX2; + this.endY2 = this.startY2; + } + + break; + + // Crop image + case "crop": + if (range.x && range.y) { + offset = this.$cropper.offset(); + left = this.startX - offset.left; + top = this.startY - offset.top; + width = dragger.minWidth; + height = dragger.minHeight; + + if (range.x > 0) { + if (range.y > 0) { + directive = "se"; + } else { + directive = "ne"; + top -= height; + } + } else { + if (range.y > 0) { + directive = "sw"; + left -= width; + } else { + directive = "nw"; + left -= width; + top -= height; + } + } + + // Show the dragger if is hidden + if (!this.cropped) { + this.cropped = TRUE; + this.$dragger.removeClass(CLASS_HIDDEN); + } + } + + break; + + // No default + } + + if (renderable) { + dragger.width = width; + dragger.height = height; + dragger.left = left; + dragger.top = top; + this.directive = directive; + + this.renderDragger(); + } + + // Override + this.startX = this.endX; + this.startY = this.endY; + } + }; + + // Use the string compressor: Strmin (https://github.com/fengyuanchen/strmin) + Cropper.TEMPLATE = (function (source, words) { + words = words.split(","); + return source.replace(/\d+/g, function (i) { + return words[i]; + }); + })('<0 6="5-container"><0 6="5-canvas"><0 6="5-dragger"><1 6="5-viewer"><1 6="5-8 8-h"><1 6="5-8 8-v"><1 6="5-face" 3-2="all"><1 6="5-7 7-e" 3-2="e"><1 6="5-7 7-n" 3-2="n"><1 6="5-7 7-w" 3-2="w"><1 6="5-7 7-s" 3-2="s"><1 6="5-4 4-e" 3-2="e"><1 6="5-4 4-n" 3-2="n"><1 6="5-4 4-w" 3-2="w"><1 6="5-4 4-s" 3-2="s"><1 6="5-4 4-ne" 3-2="ne"><1 6="5-4 4-nw" 3-2="nw"><1 6="5-4 4-sw" 3-2="sw"><1 6="5-4 4-se" 3-2="se">', "div,span,directive,data,point,cropper,class,line,dashed"); + + /* Template source: +

+
+
+ + + + + + + + + + + + + + + + +
+
+ */ + + Cropper.DEFAULTS = { + // Basic + aspectRatio: "auto", + autoCropArea: 0.8, // 80% + data: { + // x: 0, + // y: 0, + // width: 300, + // height: 150 + }, + done: $.noop, + preview: "", + + // Toggles + multiple: FALSE, + autoCrop: TRUE, + dragCrop: TRUE, + dashed: TRUE, + modal: TRUE, + movable: TRUE, + resizable: TRUE, + zoomable: TRUE, + rotatable: TRUE, + checkImageOrigin: TRUE, + + // Dimensions + minWidth: 0, + minHeight: 0, + maxWidth: INFINITY, + maxHeight: INFINITY, + + // Events + build: NULL, + built: NULL, + dragstart: NULL, + dragmove: NULL, + dragend: NULL + }; + + Cropper.setDefaults = function (options) { + $.extend(Cropper.DEFAULTS, options); + }; + + // Save the other cropper + Cropper.other = $.fn.cropper; + + // Register as jQuery plugin + $.fn.cropper = function (options) { + var args = toArray(arguments, 1), + result; + + this.each(function () { + var $this = $(this), + data = $this.data("cropper"), + fn; + + if (!data) { + $this.data("cropper", (data = new Cropper(this, options))); + } + + if (typeof options === "string" && $.isFunction((fn = data[options]))) { + result = fn.apply(data, args); + } + }); + + return (typeof result !== STRING_UNDEFINED ? result : this); + }; + + $.fn.cropper.Constructor = Cropper; + $.fn.cropper.setDefaults = Cropper.setDefaults; + + // No conflict + $.fn.cropper.noConflict = function () { + $.fn.cropper = Cropper.other; + return this; + }; +}); diff --git a/assets/js/um-functions.js b/assets/js/um-functions.js index cd5ad3a1..44cc2b3d 100644 --- a/assets/js/um-functions.js +++ b/assets/js/um-functions.js @@ -1,11 +1,3 @@ -function initCrop_UM() { - - jQuery('.um-single-image-preview.crop img').imgAreaSelect({ - handles: true, - }); - -} - function initImageUpload_UM( trigger ) { if (trigger.data('upload_help_text')){ @@ -43,7 +35,7 @@ function initImageUpload_UM( trigger ) { showAbort: false, showDone: false, showFileCounter: false, - showStatusAfterSuccess: false, + showStatusAfterSuccess: true, onSubmit:function(files){ trigger.parents('.um-modal-body').find('.um-error-block').remove(); @@ -57,24 +49,114 @@ function initImageUpload_UM( trigger ) { if (data.error && data.error != '') { trigger.parents('.um-modal-body').append('
'+data.error+'
'); + trigger.parents('.um-modal-body').find('.upload-statusbar').hide(0); + um_modal_responsive(); } else { jQuery.each( data, function(key, value) { - trigger.parents('.um-modal-body').find('.um-single-image-preview img').attr('src', value); + + var img_id = trigger.parents('.um-modal-body').find('.um-single-image-preview img'); + var img_id_h = trigger.parents('.um-modal-body').find('.um-single-image-preview'); + + img_id.attr("src", value); + img_id.load(function(){ + + trigger.parents('.um-modal-body').find('.um-modal-btn.um-finish-upload.disabled').removeClass('disabled'); + trigger.parents('.um-modal-body').find('.ajax-upload-dragdrop,.upload-statusbar').hide(0); + img_id_h.show(0); + um_modal_responsive(); + + }); + }); - - trigger.parents('.um-modal-body').find('.ajax-upload-dragdrop').fadeOut( function () { - trigger.parents('.um-modal-body').find('.um-single-image-preview').show(); - trigger.parents('.um-modal-body').find('.um-modal-btn.um-finish-upload.disabled').removeClass('disabled'); - um_modal_responsive(); - } ); - - initCrop_UM(); - + } } }); +} + +function initFileUpload_UM( trigger ) { + + if (trigger.data('upload_help_text')){ + upload_help_text = '' + trigger.data('upload_help_text') + ''; + } else { + upload_help_text = ''; + } + + if ( trigger.data('icon') ) { + icon = ''; + } else { + icon = ''; + } + + if ( trigger.data('upload_text') ) { + upload_text = '' + trigger.data('upload_text') + ''; + } else { + upload_text = ''; + } + + trigger.uploadFile({ + url: ultimatemember_file_upload_url, + method: "POST", + multiple: false, + formData: {key: trigger.data('key'), set_id: trigger.data('set_id'), set_mode: trigger.data('set_mode') }, + fileName: trigger.data('key'), + allowedTypes: trigger.data('allowed_types'), + maxFileSize: trigger.data('max_size'), + dragDropStr: icon + upload_text + upload_help_text, + sizeErrorStr: trigger.data('max_size_error'), + extErrorStr: trigger.data('extension_error'), + maxFileCountErrorStr: trigger.data('max_files_error'), + maxFileCount: 1, + showDelete: false, + showAbort: false, + showDone: false, + showFileCounter: false, + showStatusAfterSuccess: true, + onSubmit:function(files){ + + trigger.parents('.um-modal-body').find('.um-error-block').remove(); + + }, + onSuccess:function(files,data,xhr){ + + trigger.selectedFiles = 0; + + data = jQuery.parseJSON(data); + if (data.error && data.error != '') { + + trigger.parents('.um-modal-body').append('
'+data.error+'
'); + trigger.parents('.um-modal-body').find('.upload-statusbar').hide(0); + um_modal_responsive(); + + } else { + + jQuery.each( data, function(key, value) { + + trigger.parents('.um-modal-body').find('.um-modal-btn.um-finish-upload.disabled').removeClass('disabled'); + trigger.parents('.um-modal-body').find('.ajax-upload-dragdrop,.upload-statusbar').hide(0); + trigger.parents('.um-modal-body').find('.um-single-file-preview').show(0); + + if (key == 'icon') { + trigger.parents('.um-modal-body').find('.um-single-fileinfo i').removeClass().addClass(value); + } else if ( key == 'icon_bg' ) { + trigger.parents('.um-modal-body').find('.um-single-fileinfo span.icon').css({'background-color' : value } ); + } else if ( key == 'filename' ) { + trigger.parents('.um-modal-body').find('.um-single-fileinfo span.filename').html(value); + } else { + trigger.parents('.um-modal-body').find('.um-single-fileinfo a').attr('href', value); + } + + }); + + um_modal_responsive(); + + } + + } + }); + } \ No newline at end of file diff --git a/assets/js/um-modal.js b/assets/js/um-modal.js index bcfb6fba..3c01d6b9 100644 --- a/assets/js/um-modal.js +++ b/assets/js/um-modal.js @@ -1,10 +1,103 @@ +function initCrop_UM() { + + // only when a crop image is in view + var target_img = jQuery('.um-modal:visible .um-single-image-preview.crop img'); + var target_img_parent = jQuery('.um-modal:visible .um-single-image-preview.crop'); + + var crop_data = target_img.parent().attr('data-crop'); + var min_width = target_img.parent().attr('data-min_width'); + var min_height = target_img.parent().attr('data-min_height'); + var ratio = target_img.parent().attr('data-ratio'); + + // custom defined ratio maybe + if ( jQuery('.um-modal').find('#um_upload_single').attr('data-ratio') ) { + var ratio = jQuery('.um-modal').find('#um_upload_single').attr('data-ratio'); + var ratio_split = ratio.split(':'); + var ratio = ratio_split[0]; + } + + if ( target_img.length ) { + + if ( target_img.attr('src') != '' ) { + + var max_height = jQuery(window).height() - ( jQuery('.um-modal-footer a').height() + 20 ) - 50 - ( jQuery('.um-modal-header:visible').height() ); + + target_img.css({'height' : 'auto'}); + target_img_parent.css({'height' : 'auto'}); + + if ( jQuery(window).height() <= 400 ) { + target_img_parent.css({ 'height': max_height +'px', 'max-height' : max_height + 'px' }); + target_img.css({ 'height' : 'auto' }); + } else { + target_img.css({ 'height': 'auto', 'max-height' : max_height + 'px' }); + target_img_parent.css({ 'height': target_img.height(), 'max-height' : max_height + 'px' }); + } + + if ( crop_data == 'square' ) { + + var opts = { + minWidth: min_width, + minHeight: min_height, + dragCrop: false, + aspectRatio: 1.0, + zoomable: false, + rotatable: false, + dashed: false, + done: function(data) { + target_img.parent().attr('data-coord', Math.round(data.x) + ',' + Math.round(data.y) + ',' + Math.round(data.width) + ',' + Math.round(data.height) ); + } + }; + + } else if ( crop_data == 'cover' ) { + + var opts = { + minWidth: min_width, + minHeight: Math.round( min_width / ratio ), + dragCrop: false, + aspectRatio: ratio, + zoomable: false, + rotatable: false, + dashed: false, + done: function(data) { + target_img.parent().attr('data-coord', Math.round(data.x) + ',' + Math.round(data.y) + ',' + Math.round(data.width) + ',' + Math.round(data.height) ); + } + }; + + } else { + + var opts = { + dragCrop: true, + aspectRatio: "auto", + zoomable: false, + rotatable: false, + dashed: false, + done: function(data) { + target_img.parent().attr('data-coord', Math.round(data.x) + ',' + Math.round(data.y) + ',' + Math.round(data.width) + ',' + Math.round(data.height) ); + } + }; + + } + + target_img.cropper( opts ); + + } + } + +} + function um_new_modal( id, size ){ var modal = jQuery('body').find('.um-modal-overlay'); jQuery('.tipsy').hide(); + jQuery('.um-dropdown').hide(); + jQuery('body,html').css("overflow", "hidden"); + + jQuery(document).bind("touchmove", function(e){e.preventDefault();}); + jQuery('.um-modal').on('touchmove', function(e){e.stopPropagation();}); + jQuery('body').append('
'); jQuery('#' + id).prependTo('.um-modal'); @@ -14,6 +107,8 @@ function um_new_modal( id, size ){ um_modal_size( size ); initImageUpload_UM( jQuery('.um-modal:visible').find('.um-single-image-upload') ); + + initFileUpload_UM( jQuery('.um-modal:visible').find('.um-single-file-upload') ); um_modal_responsive(); @@ -22,13 +117,9 @@ function um_new_modal( id, size ){ function um_modal_responsive() { var modal = jQuery('.um-modal:visible'); - + if ( modal.length ) { - - var window_height_diff = jQuery(window).height() - modal.innerHeight(); - - var half_gap = window_height_diff / 2 + 'px'; - + var element_width = jQuery(window).width(); modal.removeClass('uimob340'); @@ -37,35 +128,49 @@ function um_modal_responsive() { if ( element_width <= 340 ) { modal.addClass('uimob340'); + initCrop_UM(); modal.animate({ 'bottom' : 0 }, 300); } else if ( element_width <= 500 ) { modal.addClass('uimob500'); + initCrop_UM(); modal.animate({ 'bottom' : 0 }, 300); } else if ( element_width <= 800 ) { + initCrop_UM(); + var half_gap = ( jQuery(window).height() - modal.innerHeight() ) / 2 + 'px'; modal.animate({ 'bottom' : half_gap }, 300); } else if ( element_width <= 960 ) { + initCrop_UM(); + var half_gap = ( jQuery(window).height() - modal.innerHeight() ) / 2 + 'px'; modal.animate({ 'bottom' : half_gap }, 300); } else if ( element_width > 960 ) { + initCrop_UM(); + var half_gap = ( jQuery(window).height() - modal.innerHeight() ) / 2 + 'px'; modal.animate({ 'bottom' : half_gap }, 300); } - + } } function um_remove_modal(){ - jQuery('.um-single-image-preview.crop img').imgAreaSelect({ hide: true }); + + jQuery('.um-single-image-preview img').cropper("destroy"); + jQuery('body,html').css("overflow", "auto"); + + jQuery(document).unbind('touchmove'); + jQuery('.um-modal div[id^="um_"]').hide().appendTo('body'); jQuery('.um-modal,.um-modal-overlay').remove(); + } function um_modal_size( aclass ) { @@ -97,6 +202,25 @@ jQuery(document).ready(function() { return false; }); + /** + remove uploaded file + **/ + jQuery(document).on('click', '.um-modal .um-single-file-preview a.cancel', function(e){ + e.preventDefault(); + + var parent = jQuery(this).parents('.um-modal-body'); + + parent.find('.um-single-file-preview').hide(); + + parent.find('.ajax-upload-dragdrop').show(); + + parent.find('.um-modal-btn.um-finish-upload').addClass('disabled'); + + um_modal_responsive(); + + return false; + }); + /** remove uploaded image **/ @@ -105,6 +229,10 @@ jQuery(document).ready(function() { var parent = jQuery(this).parents('.um-modal-body'); + parent.find('.um-single-image-preview img').cropper("destroy"); + + parent.find('.um-single-image-preview img').attr('src', ''); + parent.find('.um-single-image-preview').hide(); parent.find('.ajax-upload-dragdrop').show(); @@ -117,19 +245,80 @@ jQuery(document).ready(function() { }); /** - finish upload + finish file upload **/ - jQuery(document).on('click', '.um-finish-upload', function(){ + jQuery(document).on('click', '.um-finish-upload.file', function(){ var key = jQuery(this).attr('data-key'); - - var src = jQuery(this).parents('.um-modal-body').find('.um-single-image-preview img').attr('src'); - - jQuery('.um-single-image-preview[data-key='+key+']').fadeIn().find('img').attr('src', src); + var preview = jQuery(this).parents('.um-modal-body').find('.um-single-file-preview').html(); um_remove_modal(); - jQuery('.um-single-image-preview[data-key='+key+']').parents('.um-field').find('.um-btn-auto-width').html('Change/Modify Photo'); + jQuery('.um-single-file-preview[data-key='+key+']').fadeIn().html( preview ); + + jQuery('.um-single-file-preview[data-key='+key+']').parents('.um-field').find('.um-btn-auto-width').html( jQuery(this).attr('data-change') ); + + }); + + /** + finish image upload + **/ + jQuery(document).on('click', '.um-finish-upload.image', function(){ + + var elem = jQuery(this); + var key = jQuery(this).attr('data-key'); + var img_c = jQuery(this).parents('.um-modal-body').find('.um-single-image-preview'); + var src = img_c.find('img').attr('src'); + var coord = img_c.attr('data-coord'); + + if ( jQuery(this).parents('#um_upload_single').attr('data-user_id') ) { + var user_id = jQuery(this).parents('#um_upload_single').attr('data-user_id'); + } else { + var user_id = 0; + } + + if ( coord ) { // crop image first before processing + + jQuery(this).html( jQuery(this).attr('data-processing') ).addClass('disabled'); + + jQuery.ajax({ + url: ultimatemember_ajax_url, + type: 'POST', + data: { + action: 'ultimatemember_resize_image', + src : src, + coord : coord, + user_id : user_id, + key: key, + }, + success: function(data){ + + d = new Date(); + + if ( key == 'profile_photo') { + jQuery('.um-profile-photo-img img').attr('src', data + "?"+d.getTime()); + } + + if ( key == 'cover_photo') { + jQuery('.um-cover-e').empty().html(''); + jQuery('.um-cover-overlay').show(); + } + + jQuery('.um-single-image-preview[data-key='+key+']').fadeIn().find('img').attr('src', data + "?"+d.getTime()); + um_remove_modal(); + jQuery('.um-single-image-preview[data-key='+key+']').parents('.um-field').find('.um-btn-auto-width').html( elem.attr('data-change') ); + + } + }); + + } else { + + d = new Date(); + jQuery('.um-single-image-preview[data-key='+key+']').fadeIn().find('img').attr('src', src + "?"+d.getTime()); + um_remove_modal(); + jQuery('.um-single-image-preview[data-key='+key+']').parents('.um-field').find('.um-btn-auto-width').html( elem.attr('data-change') ); + + } }); @@ -148,6 +337,19 @@ jQuery(document).ready(function() { if ( jQuery(this).data('modal-copy') ) { jQuery('#' + modal_id).html( jQuery(this).parents('.um-field').find('.um-modal-hidden-content').html() ); + + if ( jQuery(this).parents('.um-profile-photo').attr('data-user_id') ) { + jQuery('#' + modal_id).attr('data-user_id', jQuery(this).parents('.um-profile-photo').attr('data-user_id') ); + } + + if ( jQuery(this).parents('.um-cover').attr('data-ratio') ) { + jQuery('#' + modal_id).attr('data-ratio', jQuery(this).parents('.um-cover').attr('data-ratio') ); + } + + if ( jQuery(this).parents('.um-cover').attr('data-user_id') ) { + jQuery('#' + modal_id).attr('data-user_id', jQuery(this).parents('.um-cover').attr('data-user_id') ); + } + um_new_modal( modal_id, size ); } else { um_new_modal( modal_id, size ); diff --git a/assets/js/um-profile.js b/assets/js/um-profile.js index 3a53b6e2..53fe332c 100644 --- a/assets/js/um-profile.js +++ b/assets/js/um-profile.js @@ -8,5 +8,57 @@ jQuery(document).ready(function() { return false; }); + + /* No need to allow # */ + jQuery(document).on('click', '.um-cover a', function(e){ + e.preventDefault(); + return false; + }); + + /* Reset profile photo */ + jQuery(document).on('click', '.um-reset-profile-photo', function(e){ + + jQuery('.um-profile-photo-img img').attr('src', jQuery(this).attr('data-default_src') ); + + user_id = jQuery(this).attr('data-user_id'); + metakey = 'profile_photo'; + + jQuery.ajax({ + url: ultimatemember_ajax_url, + type: 'post', + data: { + action: 'ultimatemember_delete_profile_photo', + metakey: metakey, + user_id: user_id + } + }); + + }); + + /* Reset cover photo */ + jQuery(document).on('click', '.um-reset-cover-photo', function(e){ + + jQuery('.um-cover-overlay').hide(); + + jQuery('.um-cover-e').html(''); + + jQuery('.um-dropdown').hide(); + + um_responsive(); + + user_id = jQuery(this).attr('data-user_id'); + metakey = 'cover_photo'; + + jQuery.ajax({ + url: ultimatemember_ajax_url, + type: 'post', + data: { + action: 'ultimatemember_delete_cover_photo', + metakey: metakey, + user_id: user_id + } + }); + + }); }); \ No newline at end of file diff --git a/assets/js/um-responsive.js b/assets/js/um-responsive.js index f06c4bd1..3ab010c0 100644 --- a/assets/js/um-responsive.js +++ b/assets/js/um-responsive.js @@ -1,5 +1,57 @@ -function um_responsive(){ +function UM_domenus(){ + jQuery('.um-dropdown').each(function(){ + + var menu = jQuery(this); + var element = jQuery(this).attr('data-element'); + var position = jQuery(this).attr('data-position'); + + jQuery(element).addClass('um-trigger-menu-on-'+menu.attr('data-trigger')); + + if ( position == 'bc' ){ + + if ( 200 > jQuery(element).find('img').width() ) { + left_p = ( ( jQuery(element).width() - jQuery(element).find('img').width() ) / 2 ) + ( ( jQuery(element).find('img').width() - 200 ) / 2 ); + } else { + left_p = ( ( jQuery(element).width() - jQuery(element).find('img').width() ) / 2 ); + } + + top_ = parseInt( jQuery(element).find('a').css('top') ); + + if ( top_ ) { + top_p = jQuery(element).find('img').height() + 4 + top_; + left_gap = 4; + } else { + top_p = jQuery(element).find('img').height() + 4; + left_gap = 0; + } + + if ( top_p == 4 && element == 'div.um-cover' ) { + top_p = jQuery(element).height() / 2 + ( menu.height() / 2 ); + } + + menu.css({ + 'top' : top_p, + 'width': 200, + 'left': left_p + left_gap, + 'text-align' : 'center', + }); + + menu.find('.um-dropdown-arr').find('i').addClass('um-icon-arrow-sans-up'); + + menu.find('.um-dropdown-arr').css({ + 'top' : '-18px', + 'left' : ( menu.width() / 2 ) - 12, + }); + + } + }); + +} + +function um_responsive(){ + + // responsive um shortcode jQuery('.um').each(function(){ element_width = jQuery(this).width(); @@ -48,19 +100,39 @@ function um_responsive(){ jQuery(this).removeClass('uimob960'); } - - jQuery('.um-members').each(function(){ - UM_Member_Grid( jQuery(this) ); - }); + // account nav if ( jQuery('.um-account-nav').length > 0 && jQuery('.um-account-side').is(':visible') && jQuery('.um-account-tab:visible').length == 0 ) { jQuery('.um-account-side li a.current').trigger('click'); } + // show jQuery(this).css('opacity',1); }); + // responsive cover + jQuery('.um-cover, .um-member-cover').each(function(){ + + var elem = jQuery(this); + var ratio = elem.data('ratio'); + var width = elem.width(); + var ratios = ratio.split(':'); + + calcHeight = Math.round( width / ratios[0] ) + 'px'; + elem.height( calcHeight ); + elem.find('.um-cover-add').height( calcHeight ); + + }); + + // members directory + jQuery('.um-members').each(function(){ + UM_Member_Grid( jQuery(this) ); + }); + + // menus + UM_domenus(); + } /* Run only when window is loaded */ @@ -74,6 +146,8 @@ jQuery(window).load(function() { /* Run only when window is resized */ jQuery(window).resize(function() { + jQuery('.um-single-image-preview.crop:visible img').cropper("destroy"); + um_responsive(); um_modal_responsive(); diff --git a/assets/js/um-scripts.js b/assets/js/um-scripts.js index 581b8bf0..be100634 100644 --- a/assets/js/um-scripts.js +++ b/assets/js/um-scripts.js @@ -1,12 +1,40 @@ jQuery(document).ready(function() { - /* Tooltips */ + /* dropdown menu links */ + jQuery(document).on('click', '.um-dropdown a', function(e){ + e.preventDefault(); + return false; + }); + + /* trigger dropdown on click */ + jQuery(document).on('click', '.um-trigger-menu-on-click', function(e){ + e.preventDefault(); + jQuery('.um-dropdown').hide(); + menu = jQuery(this).find('.um-dropdown'); + menu.show(); + return false; + }); + + /* hide dropdown */ + jQuery(document).on('click', '.um-dropdown-hide', function(e){ + menu = jQuery(this).parents('.um-dropdown'); + menu.hide(); + }); + + /* manual triggers */ + jQuery(document).on('click', 'a.um-manual-trigger', function(){ + var child = jQuery(this).attr('data-child'); + var parent = jQuery(this).attr('data-parent'); + jQuery(this).parents( parent ).find( child ).trigger('click'); + }); + + /* tooltips */ jQuery('.um-tip-n').tipsy({gravity: 'n', opacity: 1, live: true, offset: 3 }); jQuery('.um-tip-w').tipsy({gravity: 'w', opacity: 1, live: true, offset: 3 }); jQuery('.um-tip-e').tipsy({gravity: 'e', opacity: 1, live: true, offset: 3 }); jQuery('.um-tip-s').tipsy({gravity: 's', opacity: 1, live: true, offset: 3 }); - /* Custom Radio Buttons */ + /* custom radio buttons */ jQuery('.um-field-radio').mouseenter(function(){ if (!jQuery(this).hasClass('active')) { jQuery(this).find('i').removeClass().addClass('um-icon-check-3'); @@ -26,7 +54,7 @@ jQuery(document).ready(function() { this_field.find('i').removeClass('um-icon-blank').addClass('um-icon-check-3'); }); - /* Custom Checkbox Buttons */ + /* custom checkbox buttons */ jQuery('.um-field-checkbox').mouseenter(function(){ if (!jQuery(this).hasClass('active')) { jQuery(this).find('i').removeClass().addClass('um-icon-cross'); @@ -51,7 +79,7 @@ jQuery(document).ready(function() { }); - /* Date picker */ + /* datepicker */ jQuery('.um-datepicker').pickadate({ min: [1900,1,1], max: true, @@ -60,7 +88,7 @@ jQuery(document).ready(function() { hiddenSuffix: '__true' }); - /* Time picker */ + /* timepicker */ jQuery('.um-timepicker').pickatime({ formatSubmit: 'HH:i', hiddenSuffix: '__true' @@ -95,108 +123,18 @@ jQuery(document).ready(function() { /* remove uploaded image */ jQuery(document).on('click', '.um .um-single-image-preview a.cancel', function(e){ e.preventDefault(); - var parent = jQuery(this).parents('.um-field'); - parent.find('.um-single-image-preview').hide(); - parent.find('.um-btn-auto-width').html('Upload'); - return false; }); - /* File Upload */ - jQuery(".um-single-file-upload").each(function(){ - - var trigger = jQuery(this); - - if (trigger.data('upload_help_text')){ - upload_help_text = '' + trigger.data('upload_help_text') + ''; - } else { - upload_help_text = ''; - } - - if ( trigger.data('icon') ) { - icon = ''; - } else { - icon = ''; - } - - if ( trigger.data('upload_text') ) { - upload_text = '' + trigger.data('upload_text') + ''; - } else { - upload_text = ''; - } - - trigger.uploadFile({ - url: ultimatemember_file_upload_url, - method: "POST", - multiple: false, - formData: {key: trigger.data('key'), set_id: trigger.data('set_id'), set_mode: trigger.data('set_mode') }, - fileName: trigger.data('key'), - allowedTypes: trigger.data('allowed_types'), - maxFileSize: trigger.data('max_size'), - dragDropStr: icon + upload_text + upload_help_text, - sizeErrorStr: trigger.data('max_size_error'), - extErrorStr: trigger.data('extension_error'), - maxFileCountErrorStr: trigger.data('max_files_error'), - maxFileCount: 1, - showDelete: false, - showAbort: false, - showDone: false, - showFileCounter: false, - showStatusAfterSuccess: false, - onSubmit:function(files){ - - trigger.parents('.um-field').find('.um-error-block').remove(); - - }, - onSuccess:function(files,data,xhr){ - - trigger.selectedFiles = 0; - - data = jQuery.parseJSON(data); - if (data.error && data.error != '') { - - trigger.parents('.um-field').append('
'+data.error+'
'); - - } else { - - trigger.parents('.um-field').find('.ajax-upload-dragdrop').fadeOut( function() { - trigger.parents('.um-field').find('.um-single-file-preview').fadeIn(); - }); - - jQuery.each( data, function(key, value) { - - if (key == 'icon') { - trigger.parents('.um-field').find('.um-single-fileinfo i').removeClass().addClass(value); - } else if ( key == 'icon_bg' ) { - trigger.parents('.um-field').find('.um-single-fileinfo span.icon').css({'background-color' : value } ); - } else if ( key == 'filename' ) { - trigger.parents('.um-field').find('.um-single-fileinfo span.filename').html(value); - } else { - trigger.parents('.um-field').find('.um-single-fileinfo a').attr('href', value); - } - - }); - - } - - } - }); - - }); - - /* Remove a single file upload */ - jQuery(document).on('click', '.um-single-file-preview a.cancel', function(e){ + /* remove uploaded file */ + jQuery(document).on('click', '.um .um-single-file-preview a.cancel', function(e){ e.preventDefault(); - - var trigger = jQuery(this).parents('.um-field').find('.um-single-file-upload'); - - trigger.parents('.um-field').find('.um-single-file-preview').fadeOut(function(){ - trigger.parents('.um-field').find('.ajax-upload-dragdrop').fadeIn(); - }); - + var parent = jQuery(this).parents('.um-field'); + parent.find('.um-single-file-preview').hide(); + parent.find('.um-btn-auto-width').html('Upload'); return false; }); diff --git a/core/lib/mobiledetect/Mobile_Detect.php b/core/lib/mobiledetect/Mobile_Detect.php new file mode 100644 index 00000000..3a82c296 --- /dev/null +++ b/core/lib/mobiledetect/Mobile_Detect.php @@ -0,0 +1,1341 @@ + + * Nick Ilyin + * + * Original author: Victor Stanciu + * + * @license Code and contributions have 'MIT License' + * More details: https://github.com/serbanghita/Mobile-Detect/blob/master/LICENSE.txt + * + * @link Homepage: http://mobiledetect.net + * GitHub Repo: https://github.com/serbanghita/Mobile-Detect + * Google Code: http://code.google.com/p/php-mobile-detect/ + * README: https://github.com/serbanghita/Mobile-Detect/blob/master/README.md + * HOWTO: https://github.com/serbanghita/Mobile-Detect/wiki/Code-examples + * + * @version 2.8.11 + */ + +class Mobile_Detect +{ + /** + * Mobile detection type. + * + * @deprecated since version 2.6.9 + */ + const DETECTION_TYPE_MOBILE = 'mobile'; + + /** + * Extended detection type. + * + * @deprecated since version 2.6.9 + */ + const DETECTION_TYPE_EXTENDED = 'extended'; + + /** + * A frequently used regular expression to extract version #s. + * + * @deprecated since version 2.6.9 + */ + const VER = '([\w._\+]+)'; + + /** + * Top-level device. + */ + const MOBILE_GRADE_A = 'A'; + + /** + * Mid-level device. + */ + const MOBILE_GRADE_B = 'B'; + + /** + * Low-level device. + */ + const MOBILE_GRADE_C = 'C'; + + /** + * Stores the version number of the current release. + */ + const VERSION = '2.8.11'; + + /** + * A type for the version() method indicating a string return value. + */ + const VERSION_TYPE_STRING = 'text'; + + /** + * A type for the version() method indicating a float return value. + */ + const VERSION_TYPE_FLOAT = 'float'; + + /** + * The User-Agent HTTP header is stored in here. + * @var string + */ + protected $userAgent = null; + + /** + * HTTP headers in the PHP-flavor. So HTTP_USER_AGENT and SERVER_SOFTWARE. + * @var array + */ + protected $httpHeaders = array(); + + /** + * The matching Regex. + * This is good for debug. + * @var string + */ + protected $matchingRegex = null; + + /** + * The matches extracted from the regex expression. + * This is good for debug. + * @var string + */ + protected $matchesArray = null; + + /** + * The detection type, using self::DETECTION_TYPE_MOBILE or self::DETECTION_TYPE_EXTENDED. + * + * @deprecated since version 2.6.9 + * + * @var string + */ + protected $detectionType = self::DETECTION_TYPE_MOBILE; + + /** + * HTTP headers that trigger the 'isMobile' detection + * to be true. + * + * @var array + */ + protected static $mobileHeaders = array( + + 'HTTP_ACCEPT' => array('matches' => array( + // Opera Mini; @reference: http://dev.opera.com/articles/view/opera-binary-markup-language/ + 'application/x-obml2d', + // BlackBerry devices. + 'application/vnd.rim.html', + 'text/vnd.wap.wml', + 'application/vnd.wap.xhtml+xml' + )), + 'HTTP_X_WAP_PROFILE' => null, + 'HTTP_X_WAP_CLIENTID' => null, + 'HTTP_WAP_CONNECTION' => null, + 'HTTP_PROFILE' => null, + // Reported by Opera on Nokia devices (eg. C3). + 'HTTP_X_OPERAMINI_PHONE_UA' => null, + 'HTTP_X_NOKIA_GATEWAY_ID' => null, + 'HTTP_X_ORANGE_ID' => null, + 'HTTP_X_VODAFONE_3GPDPCONTEXT' => null, + 'HTTP_X_HUAWEI_USERID' => null, + // Reported by Windows Smartphones. + 'HTTP_UA_OS' => null, + // Reported by Verizon, Vodafone proxy system. + 'HTTP_X_MOBILE_GATEWAY' => null, + // Seen this on HTC Sensation. SensationXE_Beats_Z715e. + 'HTTP_X_ATT_DEVICEID' => null, + // Seen this on a HTC. + 'HTTP_UA_CPU' => array('matches' => array('ARM')), + ); + + /** + * List of mobile devices (phones). + * + * @var array + */ + protected static $phoneDevices = array( + 'iPhone' => '\biPhone\b|\biPod\b', // |\biTunes + 'BlackBerry' => 'BlackBerry|\bBB10\b|rim[0-9]+', + 'HTC' => 'HTC|HTC.*(Sensation|Evo|Vision|Explorer|6800|8100|8900|A7272|S510e|C110e|Legend|Desire|T8282)|APX515CKT|Qtek9090|APA9292KT|HD_mini|Sensation.*Z710e|PG86100|Z715e|Desire.*(A8181|HD)|ADR6200|ADR6400L|ADR6425|001HT|Inspire 4G|Android.*\bEVO\b|T-Mobile G1|Z520m', + 'Nexus' => 'Nexus One|Nexus S|Galaxy.*Nexus|Android.*Nexus.*Mobile|Nexus 4|Nexus 5|Nexus 6', + // @todo: Is 'Dell Streak' a tablet or a phone? ;) + 'Dell' => 'Dell.*Streak|Dell.*Aero|Dell.*Venue|DELL.*Venue Pro|Dell Flash|Dell Smoke|Dell Mini 3iX|XCD28|XCD35|\b001DL\b|\b101DL\b|\bGS01\b', + 'Motorola' => 'Motorola|DROIDX|DROID BIONIC|\bDroid\b.*Build|Android.*Xoom|HRI39|MOT-|A1260|A1680|A555|A853|A855|A953|A955|A956|Motorola.*ELECTRIFY|Motorola.*i1|i867|i940|MB200|MB300|MB501|MB502|MB508|MB511|MB520|MB525|MB526|MB611|MB612|MB632|MB810|MB855|MB860|MB861|MB865|MB870|ME501|ME502|ME511|ME525|ME600|ME632|ME722|ME811|ME860|ME863|ME865|MT620|MT710|MT716|MT720|MT810|MT870|MT917|Motorola.*TITANIUM|WX435|WX445|XT300|XT301|XT311|XT316|XT317|XT319|XT320|XT390|XT502|XT530|XT531|XT532|XT535|XT603|XT610|XT611|XT615|XT681|XT701|XT702|XT711|XT720|XT800|XT806|XT860|XT862|XT875|XT882|XT883|XT894|XT901|XT907|XT909|XT910|XT912|XT928|XT926|XT915|XT919|XT925', + 'Samsung' => 'Samsung|SGH-I337|BGT-S5230|GT-B2100|GT-B2700|GT-B2710|GT-B3210|GT-B3310|GT-B3410|GT-B3730|GT-B3740|GT-B5510|GT-B5512|GT-B5722|GT-B6520|GT-B7300|GT-B7320|GT-B7330|GT-B7350|GT-B7510|GT-B7722|GT-B7800|GT-C3010|GT-C3011|GT-C3060|GT-C3200|GT-C3212|GT-C3212I|GT-C3262|GT-C3222|GT-C3300|GT-C3300K|GT-C3303|GT-C3303K|GT-C3310|GT-C3322|GT-C3330|GT-C3350|GT-C3500|GT-C3510|GT-C3530|GT-C3630|GT-C3780|GT-C5010|GT-C5212|GT-C6620|GT-C6625|GT-C6712|GT-E1050|GT-E1070|GT-E1075|GT-E1080|GT-E1081|GT-E1085|GT-E1087|GT-E1100|GT-E1107|GT-E1110|GT-E1120|GT-E1125|GT-E1130|GT-E1160|GT-E1170|GT-E1175|GT-E1180|GT-E1182|GT-E1200|GT-E1210|GT-E1225|GT-E1230|GT-E1390|GT-E2100|GT-E2120|GT-E2121|GT-E2152|GT-E2220|GT-E2222|GT-E2230|GT-E2232|GT-E2250|GT-E2370|GT-E2550|GT-E2652|GT-E3210|GT-E3213|GT-I5500|GT-I5503|GT-I5700|GT-I5800|GT-I5801|GT-I6410|GT-I6420|GT-I7110|GT-I7410|GT-I7500|GT-I8000|GT-I8150|GT-I8160|GT-I8190|GT-I8320|GT-I8330|GT-I8350|GT-I8530|GT-I8700|GT-I8703|GT-I8910|GT-I9000|GT-I9001|GT-I9003|GT-I9010|GT-I9020|GT-I9023|GT-I9070|GT-I9082|GT-I9100|GT-I9103|GT-I9220|GT-I9250|GT-I9300|GT-I9305|GT-I9500|GT-I9505|GT-M3510|GT-M5650|GT-M7500|GT-M7600|GT-M7603|GT-M8800|GT-M8910|GT-N7000|GT-S3110|GT-S3310|GT-S3350|GT-S3353|GT-S3370|GT-S3650|GT-S3653|GT-S3770|GT-S3850|GT-S5210|GT-S5220|GT-S5229|GT-S5230|GT-S5233|GT-S5250|GT-S5253|GT-S5260|GT-S5263|GT-S5270|GT-S5300|GT-S5330|GT-S5350|GT-S5360|GT-S5363|GT-S5369|GT-S5380|GT-S5380D|GT-S5560|GT-S5570|GT-S5600|GT-S5603|GT-S5610|GT-S5620|GT-S5660|GT-S5670|GT-S5690|GT-S5750|GT-S5780|GT-S5830|GT-S5839|GT-S6102|GT-S6500|GT-S7070|GT-S7200|GT-S7220|GT-S7230|GT-S7233|GT-S7250|GT-S7500|GT-S7530|GT-S7550|GT-S7562|GT-S7710|GT-S8000|GT-S8003|GT-S8500|GT-S8530|GT-S8600|SCH-A310|SCH-A530|SCH-A570|SCH-A610|SCH-A630|SCH-A650|SCH-A790|SCH-A795|SCH-A850|SCH-A870|SCH-A890|SCH-A930|SCH-A950|SCH-A970|SCH-A990|SCH-I100|SCH-I110|SCH-I400|SCH-I405|SCH-I500|SCH-I510|SCH-I515|SCH-I600|SCH-I730|SCH-I760|SCH-I770|SCH-I830|SCH-I910|SCH-I920|SCH-I959|SCH-LC11|SCH-N150|SCH-N300|SCH-R100|SCH-R300|SCH-R351|SCH-R400|SCH-R410|SCH-T300|SCH-U310|SCH-U320|SCH-U350|SCH-U360|SCH-U365|SCH-U370|SCH-U380|SCH-U410|SCH-U430|SCH-U450|SCH-U460|SCH-U470|SCH-U490|SCH-U540|SCH-U550|SCH-U620|SCH-U640|SCH-U650|SCH-U660|SCH-U700|SCH-U740|SCH-U750|SCH-U810|SCH-U820|SCH-U900|SCH-U940|SCH-U960|SCS-26UC|SGH-A107|SGH-A117|SGH-A127|SGH-A137|SGH-A157|SGH-A167|SGH-A177|SGH-A187|SGH-A197|SGH-A227|SGH-A237|SGH-A257|SGH-A437|SGH-A517|SGH-A597|SGH-A637|SGH-A657|SGH-A667|SGH-A687|SGH-A697|SGH-A707|SGH-A717|SGH-A727|SGH-A737|SGH-A747|SGH-A767|SGH-A777|SGH-A797|SGH-A817|SGH-A827|SGH-A837|SGH-A847|SGH-A867|SGH-A877|SGH-A887|SGH-A897|SGH-A927|SGH-B100|SGH-B130|SGH-B200|SGH-B220|SGH-C100|SGH-C110|SGH-C120|SGH-C130|SGH-C140|SGH-C160|SGH-C170|SGH-C180|SGH-C200|SGH-C207|SGH-C210|SGH-C225|SGH-C230|SGH-C417|SGH-C450|SGH-D307|SGH-D347|SGH-D357|SGH-D407|SGH-D415|SGH-D780|SGH-D807|SGH-D980|SGH-E105|SGH-E200|SGH-E315|SGH-E316|SGH-E317|SGH-E335|SGH-E590|SGH-E635|SGH-E715|SGH-E890|SGH-F300|SGH-F480|SGH-I200|SGH-I300|SGH-I320|SGH-I550|SGH-I577|SGH-I600|SGH-I607|SGH-I617|SGH-I627|SGH-I637|SGH-I677|SGH-I700|SGH-I717|SGH-I727|SGH-i747M|SGH-I777|SGH-I780|SGH-I827|SGH-I847|SGH-I857|SGH-I896|SGH-I897|SGH-I900|SGH-I907|SGH-I917|SGH-I927|SGH-I937|SGH-I997|SGH-J150|SGH-J200|SGH-L170|SGH-L700|SGH-M110|SGH-M150|SGH-M200|SGH-N105|SGH-N500|SGH-N600|SGH-N620|SGH-N625|SGH-N700|SGH-N710|SGH-P107|SGH-P207|SGH-P300|SGH-P310|SGH-P520|SGH-P735|SGH-P777|SGH-Q105|SGH-R210|SGH-R220|SGH-R225|SGH-S105|SGH-S307|SGH-T109|SGH-T119|SGH-T139|SGH-T209|SGH-T219|SGH-T229|SGH-T239|SGH-T249|SGH-T259|SGH-T309|SGH-T319|SGH-T329|SGH-T339|SGH-T349|SGH-T359|SGH-T369|SGH-T379|SGH-T409|SGH-T429|SGH-T439|SGH-T459|SGH-T469|SGH-T479|SGH-T499|SGH-T509|SGH-T519|SGH-T539|SGH-T559|SGH-T589|SGH-T609|SGH-T619|SGH-T629|SGH-T639|SGH-T659|SGH-T669|SGH-T679|SGH-T709|SGH-T719|SGH-T729|SGH-T739|SGH-T746|SGH-T749|SGH-T759|SGH-T769|SGH-T809|SGH-T819|SGH-T839|SGH-T919|SGH-T929|SGH-T939|SGH-T959|SGH-T989|SGH-U100|SGH-U200|SGH-U800|SGH-V205|SGH-V206|SGH-X100|SGH-X105|SGH-X120|SGH-X140|SGH-X426|SGH-X427|SGH-X475|SGH-X495|SGH-X497|SGH-X507|SGH-X600|SGH-X610|SGH-X620|SGH-X630|SGH-X700|SGH-X820|SGH-X890|SGH-Z130|SGH-Z150|SGH-Z170|SGH-ZX10|SGH-ZX20|SHW-M110|SPH-A120|SPH-A400|SPH-A420|SPH-A460|SPH-A500|SPH-A560|SPH-A600|SPH-A620|SPH-A660|SPH-A700|SPH-A740|SPH-A760|SPH-A790|SPH-A800|SPH-A820|SPH-A840|SPH-A880|SPH-A900|SPH-A940|SPH-A960|SPH-D600|SPH-D700|SPH-D710|SPH-D720|SPH-I300|SPH-I325|SPH-I330|SPH-I350|SPH-I500|SPH-I600|SPH-I700|SPH-L700|SPH-M100|SPH-M220|SPH-M240|SPH-M300|SPH-M305|SPH-M320|SPH-M330|SPH-M350|SPH-M360|SPH-M370|SPH-M380|SPH-M510|SPH-M540|SPH-M550|SPH-M560|SPH-M570|SPH-M580|SPH-M610|SPH-M620|SPH-M630|SPH-M800|SPH-M810|SPH-M850|SPH-M900|SPH-M910|SPH-M920|SPH-M930|SPH-N100|SPH-N200|SPH-N240|SPH-N300|SPH-N400|SPH-Z400|SWC-E100|SCH-i909|GT-N7100|GT-N7105|SCH-I535|SM-N900A|SGH-I317|SGH-T999L|GT-S5360B|GT-I8262|GT-S6802|GT-S6312|GT-S6310|GT-S5312|GT-S5310|GT-I9105|GT-I8510|GT-S6790N|SM-G7105|SM-N9005|GT-S5301|GT-I9295|GT-I9195|SM-C101|GT-S7392|GT-S7560|GT-B7610|GT-I5510|GT-S7582|GT-S7530E|GT-I8750', + 'LG' => '\bLG\b;|LG[- ]?(C800|C900|E400|E610|E900|E-900|F160|F180K|F180L|F180S|730|855|L160|LS740|LS840|LS970|LU6200|MS690|MS695|MS770|MS840|MS870|MS910|P500|P700|P705|VM696|AS680|AS695|AX840|C729|E970|GS505|272|C395|E739BK|E960|L55C|L75C|LS696|LS860|P769BK|P350|P500|P509|P870|UN272|US730|VS840|VS950|LN272|LN510|LS670|LS855|LW690|MN270|MN510|P509|P769|P930|UN200|UN270|UN510|UN610|US670|US740|US760|UX265|UX840|VN271|VN530|VS660|VS700|VS740|VS750|VS910|VS920|VS930|VX9200|VX11000|AX840A|LW770|P506|P925|P999|E612|D955|D802)', + 'Sony' => 'SonyST|SonyLT|SonyEricsson|SonyEricssonLT15iv|LT18i|E10i|LT28h|LT26w|SonyEricssonMT27i|C5303|C6902|C6903|C6906|C6943|D2533', + 'Asus' => 'Asus.*Galaxy|PadFone.*Mobile', + // http://www.micromaxinfo.com/mobiles/smartphones + // Added because the codes might conflict with Acer Tablets. + 'Micromax' => 'Micromax.*\b(A210|A92|A88|A72|A111|A110Q|A115|A116|A110|A90S|A26|A51|A35|A54|A25|A27|A89|A68|A65|A57|A90)\b', + // @todo Complete the regex. + 'Palm' => 'PalmSource|Palm', // avantgo|blazer|elaine|hiptop|plucker|xiino ; + 'Vertu' => 'Vertu|Vertu.*Ltd|Vertu.*Ascent|Vertu.*Ayxta|Vertu.*Constellation(F|Quest)?|Vertu.*Monika|Vertu.*Signature', // Just for fun ;) + // http://www.pantech.co.kr/en/prod/prodList.do?gbrand=VEGA (PANTECH) + // Most of the VEGA devices are legacy. PANTECH seem to be newer devices based on Android. + 'Pantech' => 'PANTECH|IM-A850S|IM-A840S|IM-A830L|IM-A830K|IM-A830S|IM-A820L|IM-A810K|IM-A810S|IM-A800S|IM-T100K|IM-A725L|IM-A780L|IM-A775C|IM-A770K|IM-A760S|IM-A750K|IM-A740S|IM-A730S|IM-A720L|IM-A710K|IM-A690L|IM-A690S|IM-A650S|IM-A630K|IM-A600S|VEGA PTL21|PT003|P8010|ADR910L|P6030|P6020|P9070|P4100|P9060|P5000|CDM8992|TXT8045|ADR8995|IS11PT|P2030|P6010|P8000|PT002|IS06|CDM8999|P9050|PT001|TXT8040|P2020|P9020|P2000|P7040|P7000|C790', + // http://www.fly-phone.com/devices/smartphones/ ; Included only smartphones. + 'Fly' => 'IQ230|IQ444|IQ450|IQ440|IQ442|IQ441|IQ245|IQ256|IQ236|IQ255|IQ235|IQ245|IQ275|IQ240|IQ285|IQ280|IQ270|IQ260|IQ250', + 'iMobile' => 'i-mobile (IQ|i-STYLE|idea|ZAA|Hitz)', + // Added simvalley mobile just for fun. They have some interesting devices. + // http://www.simvalley.fr/telephonie---gps-_22_telephonie-mobile_telephones_.html + 'SimValley' => '\b(SP-80|XT-930|SX-340|XT-930|SX-310|SP-360|SP60|SPT-800|SP-120|SPT-800|SP-140|SPX-5|SPX-8|SP-100|SPX-8|SPX-12)\b', + // Wolfgang - a brand that is sold by Aldi supermarkets. + // http://www.wolfgangmobile.com/ + 'Wolfgang' => 'AT-B24D|AT-AS50HD|AT-AS40W|AT-AS55HD|AT-AS45q2|AT-B26D|AT-AS50Q', + 'Alcatel' => 'Alcatel', + 'Nintendo' => 'Nintendo 3DS', + // http://en.wikipedia.org/wiki/Amoi + 'Amoi' => 'Amoi', + // http://en.wikipedia.org/wiki/INQ + 'INQ' => 'INQ', + // @Tapatalk is a mobile app; http://support.tapatalk.com/threads/smf-2-0-2-os-and-browser-detection-plugin-and-tapatalk.15565/#post-79039 + 'GenericPhone' => 'Tapatalk|PDA;|SAGEM|\bmmp\b|pocket|\bpsp\b|symbian|Smartphone|smartfon|treo|up.browser|up.link|vodafone|\bwap\b|nokia|Series40|Series60|S60|SonyEricsson|N900|MAUI.*WAP.*Browser', + ); + + /** + * List of tablet devices. + * + * @var array + */ + protected static $tabletDevices = array( + 'iPad' => 'iPad|iPad.*Mobile', // @todo: check for mobile friendly emails topic. + 'NexusTablet' => 'Android.*Nexus[\s]+(7|9|10)|^.*Android.*Nexus(?:(?!Mobile).)*$', + 'SamsungTablet' => 'SAMSUNG.*Tablet|Galaxy.*Tab|SC-01C|GT-P1000|GT-P1003|GT-P1010|GT-P3105|GT-P6210|GT-P6800|GT-P6810|GT-P7100|GT-P7300|GT-P7310|GT-P7500|GT-P7510|SCH-I800|SCH-I815|SCH-I905|SGH-I957|SGH-I987|SGH-T849|SGH-T859|SGH-T869|SPH-P100|GT-P3100|GT-P3108|GT-P3110|GT-P5100|GT-P5110|GT-P6200|GT-P7320|GT-P7511|GT-N8000|GT-P8510|SGH-I497|SPH-P500|SGH-T779|SCH-I705|SCH-I915|GT-N8013|GT-P3113|GT-P5113|GT-P8110|GT-N8010|GT-N8005|GT-N8020|GT-P1013|GT-P6201|GT-P7501|GT-N5100|GT-N5105|GT-N5110|SHV-E140K|SHV-E140L|SHV-E140S|SHV-E150S|SHV-E230K|SHV-E230L|SHV-E230S|SHW-M180K|SHW-M180L|SHW-M180S|SHW-M180W|SHW-M300W|SHW-M305W|SHW-M380K|SHW-M380S|SHW-M380W|SHW-M430W|SHW-M480K|SHW-M480S|SHW-M480W|SHW-M485W|SHW-M486W|SHW-M500W|GT-I9228|SCH-P739|SCH-I925|GT-I9200|GT-I9205|GT-P5200|GT-P5210|GT-P5210X|SM-T311|SM-T310|SM-T310X|SM-T210|SM-T210R|SM-T211|SM-P600|SM-P601|SM-P605|SM-P900|SM-P901|SM-T217|SM-T217A|SM-T217S|SM-P6000|SM-T3100|SGH-I467|XE500|SM-T110|GT-P5220|GT-I9200X|GT-N5110X|GT-N5120|SM-P905|SM-T111|SM-T2105|SM-T315|SM-T320|SM-T320X|SM-T321|SM-T520|SM-T525|SM-T530NU|SM-T230NU|SM-T330NU|SM-T900|XE500T1C|SM-P605V|SM-P905V|SM-P600X|SM-P900X|SM-T210X|SM-T230|SM-T230X|SM-T325|GT-P7503|SM-T531|SM-T330|SM-T530|SM-T705C|SM-T535|SM-T331|SM-T800|SM-T700|SM-T537|SM-T807|SM-P907A|SM-T337A|SM-T707A|SM-T807A|SM-T237P|SM-T807P|SM-P607T|SM-T217T|SM-T337T', // SCH-P709|SCH-P729|SM-T2558 - Samsung Mega - treat them like a regular phone. + // http://docs.aws.amazon.com/silk/latest/developerguide/user-agent.html + 'Kindle' => 'Kindle|Silk.*Accelerated|Android.*\b(KFOT|KFTT|KFJWI|KFJWA|KFOTE|KFSOWI|KFTHWI|KFTHWA|KFAPWI|KFAPWA|WFJWAE|KFSAWA|KFSAWI|KFASWI)\b', + // Only the Surface tablets with Windows RT are considered mobile. + // http://msdn.microsoft.com/en-us/library/ie/hh920767(v=vs.85).aspx + 'SurfaceTablet' => 'Windows NT [0-9.]+; ARM;.*(Tablet|ARMBJS)', + // http://shopping1.hp.com/is-bin/INTERSHOP.enfinity/WFS/WW-USSMBPublicStore-Site/en_US/-/USD/ViewStandardCatalog-Browse?CatalogCategoryID=JfIQ7EN5lqMAAAEyDcJUDwMT + 'HPTablet' => 'HP Slate (7|8|10)|HP ElitePad 900|hp-tablet|EliteBook.*Touch|HP 8|Slate 21|HP SlateBook 10', + // Watch out for PadFone, see #132. + // http://www.asus.com/de/Tablets_Mobile/Memo_Pad_Products/ + 'AsusTablet' => '^.*PadFone((?!Mobile).)*$|Transformer|TF101|TF101G|TF300T|TF300TG|TF300TL|TF700T|TF700KL|TF701T|TF810C|ME171|ME301T|ME302C|ME371MG|ME370T|ME372MG|ME172V|ME173X|ME400C|Slider SL101|\bK00F\b|\bK00C\b|\bK00E\b|\bK00L\b|TX201LA|ME176C|ME102A|\bM80TA\b|ME372CL|ME560CG|ME372CG', + 'BlackBerryTablet' => 'PlayBook|RIM Tablet', + 'HTCtablet' => 'HTC_Flyer_P512|HTC Flyer|HTC Jetstream|HTC-P715a|HTC EVO View 4G|PG41200|PG09410', + 'MotorolaTablet' => 'xoom|sholest|MZ615|MZ605|MZ505|MZ601|MZ602|MZ603|MZ604|MZ606|MZ607|MZ608|MZ609|MZ615|MZ616|MZ617', + 'NookTablet' => 'Android.*Nook|NookColor|nook browser|BNRV200|BNRV200A|BNTV250|BNTV250A|BNTV400|BNTV600|LogicPD Zoom2', + // http://www.acer.ro/ac/ro/RO/content/drivers + // http://www.packardbell.co.uk/pb/en/GB/content/download (Packard Bell is part of Acer) + // http://us.acer.com/ac/en/US/content/group/tablets + // http://www.acer.de/ac/de/DE/content/models/tablets/ + // Can conflict with Micromax and Motorola phones codes. + 'AcerTablet' => 'Android.*; \b(A100|A101|A110|A200|A210|A211|A500|A501|A510|A511|A700|A701|W500|W500P|W501|W501P|W510|W511|W700|G100|G100W|B1-A71|B1-710|B1-711|A1-810|A1-811|A1-830)\b|W3-810|\bA3-A10\b', + // http://eu.computers.toshiba-europe.com/innovation/family/Tablets/1098744/banner_id/tablet_footerlink/ + // http://us.toshiba.com/tablets/tablet-finder + // http://www.toshiba.co.jp/regza/tablet/ + 'ToshibaTablet' => 'Android.*(AT100|AT105|AT200|AT205|AT270|AT275|AT300|AT305|AT1S5|AT500|AT570|AT700|AT830)|TOSHIBA.*FOLIO', + // http://www.nttdocomo.co.jp/english/service/developer/smart_phone/technical_info/spec/index.html + // http://www.lg.com/us/tablets + 'LGTablet' => '\bL-06C|LG-V909|LG-V900|LG-V700|LG-V510|LG-V500|LG-V410|LG-V400|LG-VK810\b', + 'FujitsuTablet' => 'Android.*\b(F-01D|F-02F|F-05E|F-10D|M532|Q572)\b', + // Prestigio Tablets http://www.prestigio.com/support + 'PrestigioTablet' => 'PMP3170B|PMP3270B|PMP3470B|PMP7170B|PMP3370B|PMP3570C|PMP5870C|PMP3670B|PMP5570C|PMP5770D|PMP3970B|PMP3870C|PMP5580C|PMP5880D|PMP5780D|PMP5588C|PMP7280C|PMP7280C3G|PMP7280|PMP7880D|PMP5597D|PMP5597|PMP7100D|PER3464|PER3274|PER3574|PER3884|PER5274|PER5474|PMP5097CPRO|PMP5097|PMP7380D|PMP5297C|PMP5297C_QUAD', + // http://support.lenovo.com/en_GB/downloads/default.page?# + 'LenovoTablet' => 'Idea(Tab|Pad)( A1|A10| K1|)|ThinkPad([ ]+)?Tablet|Lenovo.*(S2109|S2110|S5000|S6000|K3011|A3000|A3500|A1000|A2107|A2109|A1107|A5500|A7600|B6000|B8000|B8080)(-|)(FL|F|HV|H|)', + // http://www.dell.com/support/home/us/en/04/Products/tab_mob/tablets + 'DellTablet' => 'Venue 11|Venue 8|Venue 7|Dell Streak 10|Dell Streak 7', + // http://www.yarvik.com/en/matrix/tablets/ + 'YarvikTablet' => 'Android.*\b(TAB210|TAB211|TAB224|TAB250|TAB260|TAB264|TAB310|TAB360|TAB364|TAB410|TAB411|TAB420|TAB424|TAB450|TAB460|TAB461|TAB464|TAB465|TAB467|TAB468|TAB07-100|TAB07-101|TAB07-150|TAB07-151|TAB07-152|TAB07-200|TAB07-201-3G|TAB07-210|TAB07-211|TAB07-212|TAB07-214|TAB07-220|TAB07-400|TAB07-485|TAB08-150|TAB08-200|TAB08-201-3G|TAB08-201-30|TAB09-100|TAB09-211|TAB09-410|TAB10-150|TAB10-201|TAB10-211|TAB10-400|TAB10-410|TAB13-201|TAB274EUK|TAB275EUK|TAB374EUK|TAB462EUK|TAB474EUK|TAB9-200)\b', + 'MedionTablet' => 'Android.*\bOYO\b|LIFE.*(P9212|P9514|P9516|S9512)|LIFETAB', + 'ArnovaTablet' => 'AN10G2|AN7bG3|AN7fG3|AN8G3|AN8cG3|AN7G3|AN9G3|AN7dG3|AN7dG3ST|AN7dG3ChildPad|AN10bG3|AN10bG3DT|AN9G2', + // http://www.intenso.de/kategorie_en.php?kategorie=33 + // @todo: http://www.nbhkdz.com/read/b8e64202f92a2df129126bff.html - investigate + 'IntensoTablet' => 'INM8002KP|INM1010FP|INM805ND|Intenso Tab|TAB1004', + // IRU.ru Tablets http://www.iru.ru/catalog/soho/planetable/ + 'IRUTablet' => 'M702pro', + 'MegafonTablet' => 'MegaFon V9|\bZTE V9\b|Android.*\bMT7A\b', + // http://www.e-boda.ro/tablete-pc.html + 'EbodaTablet' => 'E-Boda (Supreme|Impresspeed|Izzycomm|Essential)', + // http://www.allview.ro/produse/droseries/lista-tablete-pc/ + 'AllViewTablet' => 'Allview.*(Viva|Alldro|City|Speed|All TV|Frenzy|Quasar|Shine|TX1|AX1|AX2)', + // http://wiki.archosfans.com/index.php?title=Main_Page + 'ArchosTablet' => '\b(101G9|80G9|A101IT)\b|Qilive 97R|Archos5|\bARCHOS (70|79|80|90|97|101|FAMILYPAD|)(b|)(G10| Cobalt| TITANIUM(HD|)| Xenon| Neon|XSK| 2| XS 2| PLATINUM| CARBON|GAMEPAD)\b', + // http://www.ainol.com/plugin.php?identifier=ainol&module=product + 'AinolTablet' => 'NOVO7|NOVO8|NOVO10|Novo7Aurora|Novo7Basic|NOVO7PALADIN|novo9-Spark', + // @todo: inspect http://esupport.sony.com/US/p/select-system.pl?DIRECTOR=DRIVER + // Readers http://www.atsuhiro-me.net/ebook/sony-reader/sony-reader-web-browser + // http://www.sony.jp/support/tablet/ + 'SonyTablet' => 'Sony.*Tablet|Xperia Tablet|Sony Tablet S|SO-03E|SGPT12|SGPT13|SGPT114|SGPT121|SGPT122|SGPT123|SGPT111|SGPT112|SGPT113|SGPT131|SGPT132|SGPT133|SGPT211|SGPT212|SGPT213|SGP311|SGP312|SGP321|EBRD1101|EBRD1102|EBRD1201|SGP351|SGP341|SGP511|SGP512|SGP521|SGP541|SGP551', + // http://www.support.philips.com/support/catalog/worldproducts.jsp?userLanguage=en&userCountry=cn&categoryid=3G_LTE_TABLET_SU_CN_CARE&title=3G%20tablets%20/%20LTE%20range&_dyncharset=UTF-8 + 'PhilipsTablet' => '\b(PI2010|PI3000|PI3100|PI3105|PI3110|PI3205|PI3210|PI3900|PI4010|PI7000|PI7100)\b', + // db + http://www.cube-tablet.com/buy-products.html + 'CubeTablet' => 'Android.*(K8GT|U9GT|U10GT|U16GT|U17GT|U18GT|U19GT|U20GT|U23GT|U30GT)|CUBE U8GT', + // http://www.cobyusa.com/?p=pcat&pcat_id=3001 + 'CobyTablet' => 'MID1042|MID1045|MID1125|MID1126|MID7012|MID7014|MID7015|MID7034|MID7035|MID7036|MID7042|MID7048|MID7127|MID8042|MID8048|MID8127|MID9042|MID9740|MID9742|MID7022|MID7010', + // http://www.match.net.cn/products.asp + 'MIDTablet' => 'M9701|M9000|M9100|M806|M1052|M806|T703|MID701|MID713|MID710|MID727|MID760|MID830|MID728|MID933|MID125|MID810|MID732|MID120|MID930|MID800|MID731|MID900|MID100|MID820|MID735|MID980|MID130|MID833|MID737|MID960|MID135|MID860|MID736|MID140|MID930|MID835|MID733', + // http://www.msi.com/support + // @todo Research the Windows Tablets. + 'MSITablet' => 'MSI \b(Primo 73K|Primo 73L|Primo 81L|Primo 77|Primo 93|Primo 75|Primo 76|Primo 73|Primo 81|Primo 91|Primo 90|Enjoy 71|Enjoy 7|Enjoy 10)\b', + // @todo http://www.kyoceramobile.com/support/drivers/ + // 'KyoceraTablet' => null, + // @todo http://intexuae.com/index.php/category/mobile-devices/tablets-products/ + // 'IntextTablet' => null, + // http://pdadb.net/index.php?m=pdalist&list=SMiT (NoName Chinese Tablets) + // http://www.imp3.net/14/show.php?itemid=20454 + 'SMiTTablet' => 'Android.*(\bMID\b|MID-560|MTV-T1200|MTV-PND531|MTV-P1101|MTV-PND530)', + // http://www.rock-chips.com/index.php?do=prod&pid=2 + 'RockChipTablet' => 'Android.*(RK2818|RK2808A|RK2918|RK3066)|RK2738|RK2808A', + // http://www.fly-phone.com/devices/tablets/ ; http://www.fly-phone.com/service/ + 'FlyTablet' => 'IQ310|Fly Vision', + // http://www.bqreaders.com/gb/tablets-prices-sale.html + 'bqTablet' => 'bq.*(Elcano|Curie|Edison|Maxwell|Kepler|Pascal|Tesla|Hypatia|Platon|Newton|Livingstone|Cervantes|Avant)|Maxwell.*Lite|Maxwell.*Plus', + // http://www.huaweidevice.com/worldwide/productFamily.do?method=index&directoryId=5011&treeId=3290 + // http://www.huaweidevice.com/worldwide/downloadCenter.do?method=index&directoryId=3372&treeId=0&tb=1&type=software (including legacy tablets) + 'HuaweiTablet' => 'MediaPad|MediaPad 7 Youth|IDEOS S7|S7-201c|S7-202u|S7-101|S7-103|S7-104|S7-105|S7-106|S7-201|S7-Slim', + // Nec or Medias Tab + 'NecTablet' => '\bN-06D|\bN-08D', + // Pantech Tablets: http://www.pantechusa.com/phones/ + 'PantechTablet' => 'Pantech.*P4100', + // Broncho Tablets: http://www.broncho.cn/ (hard to find) + 'BronchoTablet' => 'Broncho.*(N701|N708|N802|a710)', + // http://versusuk.com/support.html + 'VersusTablet' => 'TOUCHPAD.*[78910]|\bTOUCHTAB\b', + // http://www.zync.in/index.php/our-products/tablet-phablets + 'ZyncTablet' => 'z1000|Z99 2G|z99|z930|z999|z990|z909|Z919|z900', + // http://www.positivoinformatica.com.br/www/pessoal/tablet-ypy/ + 'PositivoTablet' => 'TB07STA|TB10STA|TB07FTA|TB10FTA', + // https://www.nabitablet.com/ + 'NabiTablet' => 'Android.*\bNabi', + 'KoboTablet' => 'Kobo Touch|\bK080\b|\bVox\b Build|\bArc\b Build', + // French Danew Tablets http://www.danew.com/produits-tablette.php + 'DanewTablet' => 'DSlide.*\b(700|701R|702|703R|704|802|970|971|972|973|974|1010|1012)\b', + // Texet Tablets and Readers http://www.texet.ru/tablet/ + 'TexetTablet' => 'NaviPad|TB-772A|TM-7045|TM-7055|TM-9750|TM-7016|TM-7024|TM-7026|TM-7041|TM-7043|TM-7047|TM-8041|TM-9741|TM-9747|TM-9748|TM-9751|TM-7022|TM-7021|TM-7020|TM-7011|TM-7010|TM-7023|TM-7025|TM-7037W|TM-7038W|TM-7027W|TM-9720|TM-9725|TM-9737W|TM-1020|TM-9738W|TM-9740|TM-9743W|TB-807A|TB-771A|TB-727A|TB-725A|TB-719A|TB-823A|TB-805A|TB-723A|TB-715A|TB-707A|TB-705A|TB-709A|TB-711A|TB-890HD|TB-880HD|TB-790HD|TB-780HD|TB-770HD|TB-721HD|TB-710HD|TB-434HD|TB-860HD|TB-840HD|TB-760HD|TB-750HD|TB-740HD|TB-730HD|TB-722HD|TB-720HD|TB-700HD|TB-500HD|TB-470HD|TB-431HD|TB-430HD|TB-506|TB-504|TB-446|TB-436|TB-416|TB-146SE|TB-126SE', + // Avoid detecting 'PLAYSTATION 3' as mobile. + 'PlaystationTablet' => 'Playstation.*(Portable|Vita)', + // http://www.trekstor.de/surftabs.html + 'TrekstorTablet' => 'ST10416-1|VT10416-1|ST70408-1|ST702xx-1|ST702xx-2|ST80208|ST97216|ST70104-2|VT10416-2|ST10216-2A|SurfTab', + // http://www.pyleaudio.com/Products.aspx?%2fproducts%2fPersonal-Electronics%2fTablets + 'PyleAudioTablet' => '\b(PTBL10CEU|PTBL10C|PTBL72BC|PTBL72BCEU|PTBL7CEU|PTBL7C|PTBL92BC|PTBL92BCEU|PTBL9CEU|PTBL9CUK|PTBL9C)\b', + // http://www.advandigital.com/index.php?link=content-product&jns=JP001 + // because of the short codenames we have to include whitespaces to reduce the possible conflicts. + 'AdvanTablet' => 'Android.* \b(E3A|T3X|T5C|T5B|T3E|T3C|T3B|T1J|T1F|T2A|T1H|T1i|E1C|T1-E|T5-A|T4|E1-B|T2Ci|T1-B|T1-D|O1-A|E1-A|T1-A|T3A|T4i)\b ', + // http://www.danytech.com/category/tablet-pc + 'DanyTechTablet' => 'Genius Tab G3|Genius Tab S2|Genius Tab Q3|Genius Tab G4|Genius Tab Q4|Genius Tab G-II|Genius TAB GII|Genius TAB GIII|Genius Tab S1', + // http://www.galapad.net/product.html + 'GalapadTablet' => 'Android.*\bG1\b', + // http://www.micromaxinfo.com/tablet/funbook + 'MicromaxTablet' => 'Funbook|Micromax.*\b(P250|P560|P360|P362|P600|P300|P350|P500|P275)\b', + // http://www.karbonnmobiles.com/products_tablet.php + 'KarbonnTablet' => 'Android.*\b(A39|A37|A34|ST8|ST10|ST7|Smart Tab3|Smart Tab2)\b', + // http://www.myallfine.com/Products.asp + 'AllFineTablet' => 'Fine7 Genius|Fine7 Shine|Fine7 Air|Fine8 Style|Fine9 More|Fine10 Joy|Fine11 Wide', + // http://www.proscanvideo.com/products-search.asp?itemClass=TABLET&itemnmbr= + 'PROSCANTablet' => '\b(PEM63|PLT1023G|PLT1041|PLT1044|PLT1044G|PLT1091|PLT4311|PLT4311PL|PLT4315|PLT7030|PLT7033|PLT7033D|PLT7035|PLT7035D|PLT7044K|PLT7045K|PLT7045KB|PLT7071KG|PLT7072|PLT7223G|PLT7225G|PLT7777G|PLT7810K|PLT7849G|PLT7851G|PLT7852G|PLT8015|PLT8031|PLT8034|PLT8036|PLT8080K|PLT8082|PLT8088|PLT8223G|PLT8234G|PLT8235G|PLT8816K|PLT9011|PLT9045K|PLT9233G|PLT9735|PLT9760G|PLT9770G)\b', + // http://www.yonesnav.com/products/products.php + 'YONESTablet' => 'BQ1078|BC1003|BC1077|RK9702|BC9730|BC9001|IT9001|BC7008|BC7010|BC708|BC728|BC7012|BC7030|BC7027|BC7026', + // http://www.cjshowroom.com/eproducts.aspx?classcode=004001001 + // China manufacturer makes tablets for different small brands (eg. http://www.zeepad.net/index.html) + 'ChangJiaTablet' => 'TPC7102|TPC7103|TPC7105|TPC7106|TPC7107|TPC7201|TPC7203|TPC7205|TPC7210|TPC7708|TPC7709|TPC7712|TPC7110|TPC8101|TPC8103|TPC8105|TPC8106|TPC8203|TPC8205|TPC8503|TPC9106|TPC9701|TPC97101|TPC97103|TPC97105|TPC97106|TPC97111|TPC97113|TPC97203|TPC97603|TPC97809|TPC97205|TPC10101|TPC10103|TPC10106|TPC10111|TPC10203|TPC10205|TPC10503', + // http://www.gloryunion.cn/products.asp + // http://www.allwinnertech.com/en/apply/mobile.html + // http://www.ptcl.com.pk/pd_content.php?pd_id=284 (EVOTAB) + // @todo: Softwiner tablets? + // aka. Cute or Cool tablets. Not sure yet, must research to avoid collisions. + 'GUTablet' => 'TX-A1301|TX-M9002|Q702|kf026', // A12R|D75A|D77|D79|R83|A95|A106C|R15|A75|A76|D71|D72|R71|R73|R77|D82|R85|D92|A97|D92|R91|A10F|A77F|W71F|A78F|W78F|W81F|A97F|W91F|W97F|R16G|C72|C73E|K72|K73|R96G + // http://www.pointofview-online.com/showroom.php?shop_mode=product_listing&category_id=118 + 'PointOfViewTablet' => 'TAB-P506|TAB-navi-7-3G-M|TAB-P517|TAB-P-527|TAB-P701|TAB-P703|TAB-P721|TAB-P731N|TAB-P741|TAB-P825|TAB-P905|TAB-P925|TAB-PR945|TAB-PL1015|TAB-P1025|TAB-PI1045|TAB-P1325|TAB-PROTAB[0-9]+|TAB-PROTAB25|TAB-PROTAB26|TAB-PROTAB27|TAB-PROTAB26XL|TAB-PROTAB2-IPS9|TAB-PROTAB30-IPS9|TAB-PROTAB25XXL|TAB-PROTAB26-IPS10|TAB-PROTAB30-IPS10', + // http://www.overmax.pl/pl/katalog-produktow,p8/tablety,c14/ + // @todo: add more tests. + 'OvermaxTablet' => 'OV-(SteelCore|NewBase|Basecore|Baseone|Exellen|Quattor|EduTab|Solution|ACTION|BasicTab|TeddyTab|MagicTab|Stream|TB-08|TB-09)', + // http://hclmetablet.com/India/index.php + 'HCLTablet' => 'HCL.*Tablet|Connect-3G-2.0|Connect-2G-2.0|ME Tablet U1|ME Tablet U2|ME Tablet G1|ME Tablet X1|ME Tablet Y2|ME Tablet Sync', + // http://www.edigital.hu/Tablet_es_e-book_olvaso/Tablet-c18385.html + 'DPSTablet' => 'DPS Dream 9|DPS Dual 7', + // http://www.visture.com/index.asp + 'VistureTablet' => 'V97 HD|i75 3G|Visture V4( HD)?|Visture V5( HD)?|Visture V10', + // http://www.mijncresta.nl/tablet + 'CrestaTablet' => 'CTP(-)?810|CTP(-)?818|CTP(-)?828|CTP(-)?838|CTP(-)?888|CTP(-)?978|CTP(-)?980|CTP(-)?987|CTP(-)?988|CTP(-)?989', + // MediaTek - http://www.mediatek.com/_en/01_products/02_proSys.php?cata_sn=1&cata1_sn=1&cata2_sn=309 + 'MediatekTablet' => '\bMT8125|MT8389|MT8135|MT8377\b', + // Concorde tab + 'ConcordeTablet' => 'Concorde([ ]+)?Tab|ConCorde ReadMan', + // GoClever Tablets - http://www.goclever.com/uk/products,c1/tablet,c5/ + 'GoCleverTablet' => 'GOCLEVER TAB|A7GOCLEVER|M1042|M7841|M742|R1042BK|R1041|TAB A975|TAB A7842|TAB A741|TAB A741L|TAB M723G|TAB M721|TAB A1021|TAB I921|TAB R721|TAB I720|TAB T76|TAB R70|TAB R76.2|TAB R106|TAB R83.2|TAB M813G|TAB I721|GCTA722|TAB I70|TAB I71|TAB S73|TAB R73|TAB R74|TAB R93|TAB R75|TAB R76.1|TAB A73|TAB A93|TAB A93.2|TAB T72|TAB R83|TAB R974|TAB R973|TAB A101|TAB A103|TAB A104|TAB A104.2|R105BK|M713G|A972BK|TAB A971|TAB R974.2|TAB R104|TAB R83.3|TAB A1042', + // Modecom Tablets - http://www.modecom.eu/tablets/portal/ + 'ModecomTablet' => 'FreeTAB 9000|FreeTAB 7.4|FreeTAB 7004|FreeTAB 7800|FreeTAB 2096|FreeTAB 7.5|FreeTAB 1014|FreeTAB 1001 |FreeTAB 8001|FreeTAB 9706|FreeTAB 9702|FreeTAB 7003|FreeTAB 7002|FreeTAB 1002|FreeTAB 7801|FreeTAB 1331|FreeTAB 1004|FreeTAB 8002|FreeTAB 8014|FreeTAB 9704|FreeTAB 1003', + // Vonino Tablets - http://www.vonino.eu/tablets + 'VoninoTablet' => '\b(Argus[ _]?S|Diamond[ _]?79HD|Emerald[ _]?78E|Luna[ _]?70C|Onyx[ _]?S|Onyx[ _]?Z|Orin[ _]?HD|Orin[ _]?S|Otis[ _]?S|SpeedStar[ _]?S|Magnet[ _]?M9|Primus[ _]?94[ _]?3G|Primus[ _]?94HD|Primus[ _]?QS|Android.*\bQ8\b|Sirius[ _]?EVO[ _]?QS|Sirius[ _]?QS|Spirit[ _]?S)\b', + // ECS Tablets - http://www.ecs.com.tw/ECSWebSite/Product/Product_Tablet_List.aspx?CategoryID=14&MenuID=107&childid=M_107&LanID=0 + 'ECSTablet' => 'V07OT2|TM105A|S10OT1|TR10CS1', + // Storex Tablets - http://storex.fr/espace_client/support.html + // @note: no need to add all the tablet codes since they are guided by the first regex. + 'StorexTablet' => 'eZee[_\']?(Tab|Go)[0-9]+|TabLC7|Looney Tunes Tab', + // Generic Vodafone tablets. + 'VodafoneTablet' => 'SmartTab([ ]+)?[0-9]+|SmartTabII10|SmartTabII7', + // French tablets - Essentiel B http://www.boulanger.fr/tablette_tactile_e-book/tablette_tactile_essentiel_b/cl_68908.htm?multiChoiceToDelete=brand&mc_brand=essentielb + // Aka: http://www.essentielb.fr/ + 'EssentielBTablet' => 'Smart[ \']?TAB[ ]+?[0-9]+|Family[ \']?TAB2', + // Ross & Moor - http://ross-moor.ru/ + 'RossMoorTablet' => 'RM-790|RM-997|RMD-878G|RMD-974R|RMT-705A|RMT-701|RME-601|RMT-501|RMT-711', + // i-mobile http://product.i-mobilephone.com/Mobile_Device + 'iMobileTablet' => 'i-mobile i-note', + // http://www.tolino.de/de/vergleichen/ + 'TolinoTablet' => 'tolino tab [0-9.]+|tolino shine', + // AudioSonic - a Kmart brand + // http://www.kmart.com.au/webapp/wcs/stores/servlet/Search?langId=-1&storeId=10701&catalogId=10001&categoryId=193001&pageSize=72¤tPage=1&searchCategory=193001%2b4294965664&sortBy=p_MaxPrice%7c1 + 'AudioSonicTablet' => '\bC-22Q|T7-QC|T-17B|T-17P\b', + // AMPE Tablets - http://www.ampe.com.my/product-category/tablets/ + // @todo: add them gradually to avoid conflicts. + 'AMPETablet' => 'Android.* A78 ', + // Skk Mobile - http://skkmobile.com.ph/product_tablets.php + 'SkkTablet' => 'Android.* (SKYPAD|PHOENIX|CYCLOPS)', + // Tecno Mobile (only tablet) - http://www.tecno-mobile.com/index.php/product?filterby=smart&list_order=all&page=1 + 'TecnoTablet' => 'TECNO P9', + // JXD (consoles & tablets) - http://jxd.hk/products.asp?selectclassid=009008&clsid=3 + 'JXDTablet' => 'Android.*\b(F3000|A3300|JXD5000|JXD3000|JXD2000|JXD300B|JXD300|S5800|S7800|S602b|S5110b|S7300|S5300|S602|S603|S5100|S5110|S601|S7100a|P3000F|P3000s|P101|P200s|P1000m|P200m|P9100|P1000s|S6600b|S908|P1000|P300|S18|S6600|S9100)\b', + // i-Joy tablets - http://www.i-joy.es/en/cat/products/tablets/ + 'iJoyTablet' => 'Tablet (Spirit 7|Essentia|Galatea|Fusion|Onix 7|Landa|Titan|Scooby|Deox|Stella|Themis|Argon|Unique 7|Sygnus|Hexen|Finity 7|Cream|Cream X2|Jade|Neon 7|Neron 7|Kandy|Scape|Saphyr 7|Rebel|Biox|Rebel|Rebel 8GB|Myst|Draco 7|Myst|Tab7-004|Myst|Tadeo Jones|Tablet Boing|Arrow|Draco Dual Cam|Aurix|Mint|Amity|Revolution|Finity 9|Neon 9|T9w|Amity 4GB Dual Cam|Stone 4GB|Stone 8GB|Andromeda|Silken|X2|Andromeda II|Halley|Flame|Saphyr 9,7|Touch 8|Planet|Triton|Unique 10|Hexen 10|Memphis 4GB|Memphis 8GB|Onix 10)', + // http://www.intracon.eu/tablet + 'FX2Tablet' => 'FX2 PAD7|FX2 PAD10', + // http://www.xoro.de/produkte/ + // @note: Might be the same brand with 'Simply tablets' + 'XoroTablet' => 'KidsPAD 701|PAD[ ]?712|PAD[ ]?714|PAD[ ]?716|PAD[ ]?717|PAD[ ]?718|PAD[ ]?720|PAD[ ]?721|PAD[ ]?722|PAD[ ]?790|PAD[ ]?792|PAD[ ]?900|PAD[ ]?9715D|PAD[ ]?9716DR|PAD[ ]?9718DR|PAD[ ]?9719QR|PAD[ ]?9720QR|TelePAD1030|Telepad1032|TelePAD730|TelePAD731|TelePAD732|TelePAD735Q|TelePAD830|TelePAD9730|TelePAD795|MegaPAD 1331|MegaPAD 1851|MegaPAD 2151', + // http://www1.viewsonic.com/products/computing/tablets/ + 'ViewsonicTablet' => 'ViewPad 10pi|ViewPad 10e|ViewPad 10s|ViewPad E72|ViewPad7|ViewPad E100|ViewPad 7e|ViewSonic VB733|VB100a', + // http://www.odys.de/web/internet-tablet_en.html + 'OdysTablet' => 'LOOX|XENO10|ODYS[ -](Space|EVO|Xpress|NOON)|\bXELIO\b|Xelio10Pro|XELIO7PHONETAB|XELIO10EXTREME|XELIOPT2|NEO_QUAD10', + // http://www.captiva-power.de/products.html#tablets-en + 'CaptivaTablet' => 'CAPTIVA PAD', + // IconBIT - http://www.iconbit.com/products/tablets/ + 'IconbitTablet' => 'NetTAB|NT-3702|NT-3702S|NT-3702S|NT-3603P|NT-3603P|NT-0704S|NT-0704S|NT-3805C|NT-3805C|NT-0806C|NT-0806C|NT-0909T|NT-0909T|NT-0907S|NT-0907S|NT-0902S|NT-0902S', + // http://www.teclast.com/topic.php?channelID=70&topicID=140&pid=63 + 'TeclastTablet' => 'T98 4G|\bP80\b|\bX90HD\b|X98 Air|X98 Air 3G|\bX89\b|P80 3G|\bX80h\b|P98 Air|\bX89HD\b|P98 3G|\bP90HD\b|P89 3G|X98 3G|\bP70h\b|P79HD 3G|G18d 3G|\bP79HD\b|\bP89s\b|\bA88\b|\bP10HD\b|\bP19HD\b|G18 3G|\bP78HD\b|\bA78\b|\bP75\b|G17s 3G|G17h 3G|\bP85t\b|\bP90\b|\bP11\b|\bP98t\b|\bP98HD\b|\bG18d\b|\bP85s\b|\bP11HD\b|\bP88s\b|\bA80HD\b|\bA80se\b|\bA10h\b|\bP89\b|\bP78s\b|\bG18\b|\bP85\b|\bA70h\b|\bA70\b|\bG17\b|\bP18\b|\bA80s\b|\bA11s\b|\bP88HD\b|\bA80h\b|\bP76s\b|\bP76h\b|\bP98\b|\bA10HD\b|\bP78\b|\bP88\b|\bA11\b|\bA10t\b|\bP76a\b|\bP76t\b|\bP76e\b|\bP85HD\b|\bP85a\b|\bP86\b|\bP75HD\b|\bP76v\b|\bA12\b|\bP75a\b|\bA15\b|\bP76Ti\b|\bP81HD\b|\bA10\b|\bT760VE\b|\bT720HD\b|\bP76\b|\bP73\b|\bP71\b|\bP72\b|\bT720SE\b|\bC520Ti\b|\bT760\b|\bT720VE\b|T720-3GE|T720-WiFi', + 'JaytechTablet' => 'TPC-PA762', + 'BlaupunktTablet' => 'Endeavour 800NG|Endeavour 1010', + // http://www.digma.ru/support/download/ + // @todo: Ebooks also (if requested) + 'DigmaTablet' => '\b(iDx10|iDx9|iDx8|iDx7|iDxD7|iDxD8|iDsQ8|iDsQ7|iDsQ8|iDsD10|iDnD7|3TS804H|iDsQ11|iDj7|iDs10)\b', + // http://www.evolioshop.com/ro/tablete-pc.html + // http://www.evolio.ro/support/downloads_static.html?cat=2 + // @todo: Research some more + 'EvolioTablet' => 'ARIA_Mini_wifi|Aria[ _]Mini|Evolio X10|Evolio X7|Evolio X8|\bEvotab\b|\bNeura\b', + // @todo http://www.lavamobiles.com/tablets-data-cards + 'LavaTablet' => 'QPAD E704|\bIvoryS\b|E-TAB IVORY', + // https://www.celkonmobiles.com/?_a=categoryphones&sid=2 + 'CelkonTablet' => 'CT695|CT888|CT[\s]?910|CT7 Tab|CT9 Tab|CT3 Tab|CT2 Tab|CT1 Tab|C820|C720|\bCT-1\b', + // http://www.mi.com/en + 'MiTablet' => '\bMI PAD\b|\bHM NOTE 1W\b', + // http://www.nbru.cn/index.html + 'NibiruTablet' => 'Nibiru M1|Nibiru Jupiter One', + // http://navroad.com/products/produkty/tablety/ + 'NexoTablet' => 'NEXO NOVA|NEXO 10|NEXO AVIO|NEXO FREE|NEXO GO|NEXO EVO|NEXO 3G|NEXO SMART|NEXO KIDDO|NEXO MOBI', + // http://www.datawind.com/ubislate/ + 'UbislateTablet' => 'UbiSlate[\s]?7C', + // http://www.pocketbook-int.com/ru/support + 'PocketBookTablet' => 'Pocketbook', + // http://www.tesco.com/direct/hudl/ + 'Hudl' => 'Hudl HT7S3', + // http://www.telstra.com.au/home-phone/thub-2/ + 'TelstraTablet' => 'T-Hub2', + 'GenericTablet' => 'Android.*\b97D\b|Tablet(?!.*PC)|BNTV250A|MID-WCDMA|LogicPD Zoom2|\bA7EB\b|CatNova8|A1_07|CT704|CT1002|\bM721\b|rk30sdk|\bEVOTAB\b|M758A|ET904|ALUMIUM10|Smartfren Tab|Endeavour 1010|Tablet-PC-4|Tagi Tab|\bM6pro\b|CT1020W|arc 10HD|\bJolla\b' + ); + + /** + * List of mobile Operating Systems. + * + * @var array + */ + protected static $operatingSystems = array( + 'AndroidOS' => 'Android', + 'BlackBerryOS' => 'blackberry|\bBB10\b|rim tablet os', + 'PalmOS' => 'PalmOS|avantgo|blazer|elaine|hiptop|palm|plucker|xiino', + 'SymbianOS' => 'Symbian|SymbOS|Series60|Series40|SYB-[0-9]+|\bS60\b', + // @reference: http://en.wikipedia.org/wiki/Windows_Mobile + 'WindowsMobileOS' => 'Windows CE.*(PPC|Smartphone|Mobile|[0-9]{3}x[0-9]{3})|Window Mobile|Windows Phone [0-9.]+|WCE;', + // @reference: http://en.wikipedia.org/wiki/Windows_Phone + // http://wifeng.cn/?r=blog&a=view&id=106 + // http://nicksnettravels.builttoroam.com/post/2011/01/10/Bogus-Windows-Phone-7-User-Agent-String.aspx + // http://msdn.microsoft.com/library/ms537503.aspx + 'WindowsPhoneOS' => 'Windows Phone 8.0|Windows Phone OS|XBLWP7|ZuneWP7|Windows NT 6.[23]; ARM;', + 'iOS' => '\biPhone.*Mobile|\biPod|\biPad', + // http://en.wikipedia.org/wiki/MeeGo + // @todo: research MeeGo in UAs + 'MeeGoOS' => 'MeeGo', + // http://en.wikipedia.org/wiki/Maemo + // @todo: research Maemo in UAs + 'MaemoOS' => 'Maemo', + 'JavaOS' => 'J2ME/|\bMIDP\b|\bCLDC\b', // '|Java/' produces bug #135 + 'webOS' => 'webOS|hpwOS', + 'badaOS' => '\bBada\b', + 'BREWOS' => 'BREW', + ); + + /** + * List of mobile User Agents. + * + * @var array + */ + protected static $browsers = array( + // @reference: https://developers.google.com/chrome/mobile/docs/user-agent + 'Chrome' => '\bCrMo\b|CriOS|Android.*Chrome/[.0-9]* (Mobile)?', + 'Dolfin' => '\bDolfin\b', + 'Opera' => 'Opera.*Mini|Opera.*Mobi|Android.*Opera|Mobile.*OPR/[0-9.]+|Coast/[0-9.]+', + 'Skyfire' => 'Skyfire', + 'IE' => 'IEMobile|MSIEMobile', // |Trident/[.0-9]+ + 'Firefox' => 'fennec|firefox.*maemo|(Mobile|Tablet).*Firefox|Firefox.*Mobile', + 'Bolt' => 'bolt', + 'TeaShark' => 'teashark', + 'Blazer' => 'Blazer', + // @reference: http://developer.apple.com/library/safari/#documentation/AppleApplications/Reference/SafariWebContent/OptimizingforSafarioniPhone/OptimizingforSafarioniPhone.html#//apple_ref/doc/uid/TP40006517-SW3 + 'Safari' => 'Version.*Mobile.*Safari|Safari.*Mobile|MobileSafari', + // http://en.wikipedia.org/wiki/Midori_(web_browser) + //'Midori' => 'midori', + 'Tizen' => 'Tizen', + 'UCBrowser' => 'UC.*Browser|UCWEB', + 'baiduboxapp' => 'baiduboxapp', + 'baidubrowser' => 'baidubrowser', + // https://github.com/serbanghita/Mobile-Detect/issues/7 + 'DiigoBrowser' => 'DiigoBrowser', + // http://www.puffinbrowser.com/index.php + 'Puffin' => 'Puffin', + // http://mercury-browser.com/index.html + 'Mercury' => '\bMercury\b', + // http://en.wikipedia.org/wiki/Obigo_Browser + 'ObigoBrowser' => 'Obigo', + // http://en.wikipedia.org/wiki/NetFront + 'NetFront' => 'NF-Browser', + // @reference: http://en.wikipedia.org/wiki/Minimo + // http://en.wikipedia.org/wiki/Vision_Mobile_Browser + 'GenericBrowser' => 'NokiaBrowser|OviBrowser|OneBrowser|TwonkyBeamBrowser|SEMC.*Browser|FlyFlow|Minimo|NetFront|Novarra-Vision|MQQBrowser|MicroMessenger', + ); + + /** + * Utilities. + * + * @var array + */ + protected static $utilities = array( + // Experimental. When a mobile device wants to switch to 'Desktop Mode'. + // http://scottcate.com/technology/windows-phone-8-ie10-desktop-or-mobile/ + // https://github.com/serbanghita/Mobile-Detect/issues/57#issuecomment-15024011 + 'DesktopMode' => 'WPDesktop', + 'TV' => 'SonyDTV|HbbTV', // experimental + 'WebKit' => '(webkit)[ /]([\w.]+)', + 'Bot' => 'Googlebot|YandexBot|bingbot|ia_archiver|AhrefsBot|Ezooms|GSLFbot|WBSearchBot|Twitterbot|TweetmemeBot|Twikle|PaperLiBot|Wotbox|UnwindFetchor|facebookexternalhit', + 'MobileBot' => 'Googlebot-Mobile|YahooSeeker/M1A1-R2D2', + // @todo: Include JXD consoles. + 'Console' => '\b(Nintendo|Nintendo WiiU|Nintendo 3DS|PLAYSTATION|Xbox)\b', + 'Watch' => 'SM-V700', + ); + + /** + * All possible HTTP headers that represent the + * User-Agent string. + * + * @var array + */ + protected static $uaHttpHeaders = array( + // The default User-Agent string. + 'HTTP_USER_AGENT', + // Header can occur on devices using Opera Mini. + 'HTTP_X_OPERAMINI_PHONE_UA', + // Vodafone specific header: http://www.seoprinciple.com/mobile-web-community-still-angry-at-vodafone/24/ + 'HTTP_X_DEVICE_USER_AGENT', + 'HTTP_X_ORIGINAL_USER_AGENT', + 'HTTP_X_SKYFIRE_PHONE', + 'HTTP_X_BOLT_PHONE_UA', + 'HTTP_DEVICE_STOCK_UA', + 'HTTP_X_UCBROWSER_DEVICE_UA' + ); + + /** + * The individual segments that could exist in a User-Agent string. VER refers to the regular + * expression defined in the constant self::VER. + * + * @var array + */ + protected static $properties = array( + + // Build + 'Mobile' => 'Mobile/[VER]', + 'Build' => 'Build/[VER]', + 'Version' => 'Version/[VER]', + 'VendorID' => 'VendorID/[VER]', + + // Devices + 'iPad' => 'iPad.*CPU[a-z ]+[VER]', + 'iPhone' => 'iPhone.*CPU[a-z ]+[VER]', + 'iPod' => 'iPod.*CPU[a-z ]+[VER]', + //'BlackBerry' => array('BlackBerry[VER]', 'BlackBerry [VER];'), + 'Kindle' => 'Kindle/[VER]', + + // Browser + 'Chrome' => array('Chrome/[VER]', 'CriOS/[VER]', 'CrMo/[VER]'), + 'Coast' => array('Coast/[VER]'), + 'Dolfin' => 'Dolfin/[VER]', + // @reference: https://developer.mozilla.org/en-US/docs/User_Agent_Strings_Reference + 'Firefox' => 'Firefox/[VER]', + 'Fennec' => 'Fennec/[VER]', + // @reference: http://msdn.microsoft.com/en-us/library/ms537503(v=vs.85).aspx + 'IE' => array('IEMobile/[VER];', 'IEMobile [VER]', 'MSIE [VER];'), + // http://en.wikipedia.org/wiki/NetFront + 'NetFront' => 'NetFront/[VER]', + 'NokiaBrowser' => 'NokiaBrowser/[VER]', + 'Opera' => array( ' OPR/[VER]', 'Opera Mini/[VER]', 'Version/[VER]' ), + 'Opera Mini' => 'Opera Mini/[VER]', + 'Opera Mobi' => 'Version/[VER]', + 'UC Browser' => 'UC Browser[VER]', + 'MQQBrowser' => 'MQQBrowser/[VER]', + 'MicroMessenger' => 'MicroMessenger/[VER]', + 'baiduboxapp' => 'baiduboxapp/[VER]', + 'baidubrowser' => 'baidubrowser/[VER]', + 'Iron' => 'Iron/[VER]', + // @note: Safari 7534.48.3 is actually Version 5.1. + // @note: On BlackBerry the Version is overwriten by the OS. + 'Safari' => array( 'Version/[VER]', 'Safari/[VER]' ), + 'Skyfire' => 'Skyfire/[VER]', + 'Tizen' => 'Tizen/[VER]', + 'Webkit' => 'webkit[ /][VER]', + + // Engine + 'Gecko' => 'Gecko/[VER]', + 'Trident' => 'Trident/[VER]', + 'Presto' => 'Presto/[VER]', + + // OS + 'iOS' => ' \bOS\b [VER] ', + 'Android' => 'Android [VER]', + 'BlackBerry' => array('BlackBerry[\w]+/[VER]', 'BlackBerry.*Version/[VER]', 'Version/[VER]'), + 'BREW' => 'BREW [VER]', + 'Java' => 'Java/[VER]', + // @reference: http://windowsteamblog.com/windows_phone/b/wpdev/archive/2011/08/29/introducing-the-ie9-on-windows-phone-mango-user-agent-string.aspx + // @reference: http://en.wikipedia.org/wiki/Windows_NT#Releases + 'Windows Phone OS' => array( 'Windows Phone OS [VER]', 'Windows Phone [VER]'), + 'Windows Phone' => 'Windows Phone [VER]', + 'Windows CE' => 'Windows CE/[VER]', + // http://social.msdn.microsoft.com/Forums/en-US/windowsdeveloperpreviewgeneral/thread/6be392da-4d2f-41b4-8354-8dcee20c85cd + 'Windows NT' => 'Windows NT [VER]', + 'Symbian' => array('SymbianOS/[VER]', 'Symbian/[VER]'), + 'webOS' => array('webOS/[VER]', 'hpwOS/[VER];'), + ); + + /** + * Construct an instance of this class. + * + * @param array $headers Specify the headers as injection. Should be PHP _SERVER flavored. + * If left empty, will use the global _SERVER['HTTP_*'] vars instead. + * @param string $userAgent Inject the User-Agent header. If null, will use HTTP_USER_AGENT + * from the $headers array instead. + */ + public function __construct( + array $headers = null, + $userAgent = null + ) { + $this->setHttpHeaders($headers); + $this->setUserAgent($userAgent); + } + + /** + * Get the current script version. + * This is useful for the demo.php file, + * so people can check on what version they are testing + * for mobile devices. + * + * @return string The version number in semantic version format. + */ + public static function getScriptVersion() + { + return self::VERSION; + } + + /** + * Set the HTTP Headers. Must be PHP-flavored. This method will reset existing headers. + * + * @param array $httpHeaders The headers to set. If null, then using PHP's _SERVER to extract + * the headers. The default null is left for backwards compatibilty. + */ + public function setHttpHeaders($httpHeaders = null) + { + //use global _SERVER if $httpHeaders aren't defined + if (!is_array($httpHeaders) || !count($httpHeaders)) { + $httpHeaders = $_SERVER; + } + + //clear existing headers + $this->httpHeaders = array(); + + //Only save HTTP headers. In PHP land, that means only _SERVER vars that + //start with HTTP_. + foreach ($httpHeaders as $key => $value) { + if (substr($key,0,5) == 'HTTP_') { + $this->httpHeaders[$key] = $value; + } + } + } + + /** + * Retrieves the HTTP headers. + * + * @return array + */ + public function getHttpHeaders() + { + return $this->httpHeaders; + } + + /** + * Retrieves a particular header. If it doesn't exist, no exception/error is caused. + * Simply null is returned. + * + * @param string $header The name of the header to retrieve. Can be HTTP compliant such as + * "User-Agent" or "X-Device-User-Agent" or can be php-esque with the + * all-caps, HTTP_ prefixed, underscore seperated awesomeness. + * + * @return string|null The value of the header. + */ + public function getHttpHeader($header) + { + //are we using PHP-flavored headers? + if (strpos($header, '_') === false) { + $header = str_replace('-', '_', $header); + $header = strtoupper($header); + } + + //test the alternate, too + $altHeader = 'HTTP_' . $header; + + //Test both the regular and the HTTP_ prefix + if (isset($this->httpHeaders[$header])) { + return $this->httpHeaders[$header]; + } elseif (isset($this->httpHeaders[$altHeader])) { + return $this->httpHeaders[$altHeader]; + } + + return null; + } + + public function getMobileHeaders() + { + return self::$mobileHeaders; + } + + /** + * Get all possible HTTP headers that + * can contain the User-Agent string. + * + * @return array List of HTTP headers. + */ + public function getUaHttpHeaders() + { + return self::$uaHttpHeaders; + } + + /** + * Set the User-Agent to be used. + * + * @param string $userAgent The user agent string to set. + * + * @return string|null + */ + public function setUserAgent($userAgent = null) + { + if (!empty($userAgent)) { + return $this->userAgent = $userAgent; + } else { + $this->userAgent = null; + foreach ($this->getUaHttpHeaders() as $altHeader) { + if (!empty($this->httpHeaders[$altHeader])) { // @todo: should use getHttpHeader(), but it would be slow. (Serban) + $this->userAgent .= $this->httpHeaders[$altHeader] . " "; + } + } + + return $this->userAgent = (!empty($this->userAgent) ? trim($this->userAgent) : null); + + } + } + + /** + * Retrieve the User-Agent. + * + * @return string|null The user agent if it's set. + */ + public function getUserAgent() + { + return $this->userAgent; + } + + /** + * Set the detection type. Must be one of self::DETECTION_TYPE_MOBILE or + * self::DETECTION_TYPE_EXTENDED. Otherwise, nothing is set. + * + * @deprecated since version 2.6.9 + * + * @param string $type The type. Must be a self::DETECTION_TYPE_* constant. The default + * parameter is null which will default to self::DETECTION_TYPE_MOBILE. + */ + public function setDetectionType($type = null) + { + if ($type === null) { + $type = self::DETECTION_TYPE_MOBILE; + } + + if ($type != self::DETECTION_TYPE_MOBILE && $type != self::DETECTION_TYPE_EXTENDED) { + return; + } + + $this->detectionType = $type; + } + + public function getMatchingRegex() + { + return $this->matchingRegex; + } + + public function getMatchesArray() + { + return $this->matchesArray; + } + + /** + * Retrieve the list of known phone devices. + * + * @return array List of phone devices. + */ + public static function getPhoneDevices() + { + return self::$phoneDevices; + } + + /** + * Retrieve the list of known tablet devices. + * + * @return array List of tablet devices. + */ + public static function getTabletDevices() + { + return self::$tabletDevices; + } + + /** + * Alias for getBrowsers() method. + * + * @return array List of user agents. + */ + public static function getUserAgents() + { + return self::getBrowsers(); + } + + /** + * Retrieve the list of known browsers. Specifically, the user agents. + * + * @return array List of browsers / user agents. + */ + public static function getBrowsers() + { + return self::$browsers; + } + + /** + * Retrieve the list of known utilities. + * + * @return array List of utilities. + */ + public static function getUtilities() + { + return self::$utilities; + } + + /** + * Method gets the mobile detection rules. This method is used for the magic methods $detect->is*(). + * + * @deprecated since version 2.6.9 + * + * @return array All the rules (but not extended). + */ + public static function getMobileDetectionRules() + { + static $rules; + + if (!$rules) { + $rules = array_merge( + self::$phoneDevices, + self::$tabletDevices, + self::$operatingSystems, + self::$browsers + ); + } + + return $rules; + + } + + /** + * Method gets the mobile detection rules + utilities. + * The reason this is separate is because utilities rules + * don't necessary imply mobile. This method is used inside + * the new $detect->is('stuff') method. + * + * @deprecated since version 2.6.9 + * + * @return array All the rules + extended. + */ + public function getMobileDetectionRulesExtended() + { + static $rules; + + if (!$rules) { + // Merge all rules together. + $rules = array_merge( + self::$phoneDevices, + self::$tabletDevices, + self::$operatingSystems, + self::$browsers, + self::$utilities + ); + } + + return $rules; + } + + /** + * Retrieve the current set of rules. + * + * @deprecated since version 2.6.9 + * + * @return array + */ + public function getRules() + { + if ($this->detectionType == self::DETECTION_TYPE_EXTENDED) { + return self::getMobileDetectionRulesExtended(); + } else { + return self::getMobileDetectionRules(); + } + } + + /** + * Retrieve the list of mobile operating systems. + * + * @return array The list of mobile operating systems. + */ + public static function getOperatingSystems() + { + return self::$operatingSystems; + } + + /** + * Check the HTTP headers for signs of mobile. + * This is the fastest mobile check possible; it's used + * inside isMobile() method. + * + * @return bool + */ + public function checkHttpHeadersForMobile() + { + + foreach ($this->getMobileHeaders() as $mobileHeader => $matchType) { + if ( isset($this->httpHeaders[$mobileHeader]) ) { + if ( is_array($matchType['matches']) ) { + foreach ($matchType['matches'] as $_match) { + if ( strpos($this->httpHeaders[$mobileHeader], $_match) !== false ) { + return true; + } + } + + return false; + } else { + return true; + } + } + } + + return false; + + } + + /** + * Magic overloading method. + * + * @method boolean is[...]() + * @param string $name + * @param array $arguments + * @return mixed + * @throws BadMethodCallException when the method doesn't exist and doesn't start with 'is' + */ + public function __call($name, $arguments) + { + //make sure the name starts with 'is', otherwise + if (substr($name, 0, 2) != 'is') { + throw new BadMethodCallException("No such method exists: $name"); + } + + $this->setDetectionType(self::DETECTION_TYPE_MOBILE); + + $key = substr($name, 2); + + return $this->matchUAAgainstKey($key); + } + + /** + * Find a detection rule that matches the current User-agent. + * + * @param null $userAgent deprecated + * @return boolean + */ + protected function matchDetectionRulesAgainstUA($userAgent = null) + { + // Begin general search. + foreach ($this->getRules() as $_regex) { + if (empty($_regex)) { + continue; + } + if ($this->match($_regex, $userAgent)) { + return true; + } + } + + return false; + } + + /** + * Search for a certain key in the rules array. + * If the key is found the try to match the corresponding + * regex agains the User-Agent. + * + * @param string $key + * @param null $userAgent deprecated + * @return mixed + */ + protected function matchUAAgainstKey($key, $userAgent = null) + { + // Make the keys lowercase so we can match: isIphone(), isiPhone(), isiphone(), etc. + $key = strtolower($key); + + //change the keys to lower case + $_rules = array_change_key_case($this->getRules()); + + if (array_key_exists($key, $_rules)) { + if (empty($_rules[$key])) { + return null; + } + + return $this->match($_rules[$key], $userAgent); + } + + return false; + } + + /** + * Check if the device is mobile. + * Returns true if any type of mobile device detected, including special ones + * @param null $userAgent deprecated + * @param null $httpHeaders deprecated + * @return bool + */ + public function isMobile($userAgent = null, $httpHeaders = null) + { + + if ($httpHeaders) { + $this->setHttpHeaders($httpHeaders); + } + + if ($userAgent) { + $this->setUserAgent($userAgent); + } + + $this->setDetectionType(self::DETECTION_TYPE_MOBILE); + + if ($this->checkHttpHeadersForMobile()) { + return true; + } else { + return $this->matchDetectionRulesAgainstUA(); + } + + } + + /** + * Check if the device is a tablet. + * Return true if any type of tablet device is detected. + * + * @param string $userAgent deprecated + * @param array $httpHeaders deprecated + * @return bool + */ + public function isTablet($userAgent = null, $httpHeaders = null) + { + $this->setDetectionType(self::DETECTION_TYPE_MOBILE); + + foreach (self::$tabletDevices as $_regex) { + if ($this->match($_regex, $userAgent)) { + return true; + } + } + + return false; + } + + /** + * This method checks for a certain property in the + * userAgent. + * @todo: The httpHeaders part is not yet used. + * + * @param string $key + * @param string $userAgent deprecated + * @param string $httpHeaders deprecated + * @return bool|int|null + */ + public function is($key, $userAgent = null, $httpHeaders = null) + { + // Set the UA and HTTP headers only if needed (eg. batch mode). + if ($httpHeaders) { + $this->setHttpHeaders($httpHeaders); + } + + if ($userAgent) { + $this->setUserAgent($userAgent); + } + + $this->setDetectionType(self::DETECTION_TYPE_EXTENDED); + + return $this->matchUAAgainstKey($key); + } + + /** + * Some detection rules are relative (not standard), + * because of the diversity of devices, vendors and + * their conventions in representing the User-Agent or + * the HTTP headers. + * + * This method will be used to check custom regexes against + * the User-Agent string. + * + * @param $regex + * @param string $userAgent + * @return bool + * + * @todo: search in the HTTP headers too. + */ + public function match($regex, $userAgent = null) + { + // Escape the special character which is the delimiter. + $regex = str_replace('/', '\/', $regex); + $match = (bool) preg_match('/'.$regex.'/is', (!empty($userAgent) ? $userAgent : $this->userAgent), $matches); + // If positive match is found, store the results for debug. + if ($match) { + $this->matchingRegex = $regex; + $this->matchesArray = $matches; + } + + return $match; + } + + /** + * Get the properties array. + * + * @return array + */ + public static function getProperties() + { + return self::$properties; + } + + /** + * Prepare the version number. + * + * @todo Remove the error supression from str_replace() call. + * + * @param string $ver The string version, like "2.6.21.2152"; + * + * @return float + */ + public function prepareVersionNo($ver) + { + $ver = str_replace(array('_', ' ', '/'), '.', $ver); + $arrVer = explode('.', $ver, 2); + + if (isset($arrVer[1])) { + $arrVer[1] = @str_replace('.', '', $arrVer[1]); // @todo: treat strings versions. + } + + return (float) implode('.', $arrVer); + } + + /** + * Check the version of the given property in the User-Agent. + * Will return a float number. (eg. 2_0 will return 2.0, 4.3.1 will return 4.31) + * + * @param string $propertyName The name of the property. See self::getProperties() array + * keys for all possible properties. + * @param string $type Either self::VERSION_TYPE_STRING to get a string value or + * self::VERSION_TYPE_FLOAT indicating a float value. This parameter + * is optional and defaults to self::VERSION_TYPE_STRING. Passing an + * invalid parameter will default to the this type as well. + * + * @return string|float The version of the property we are trying to extract. + */ + public function version($propertyName, $type = self::VERSION_TYPE_STRING) + { + if (empty($propertyName)) { + return false; + } + + //set the $type to the default if we don't recognize the type + if ($type != self::VERSION_TYPE_STRING && $type != self::VERSION_TYPE_FLOAT) { + $type = self::VERSION_TYPE_STRING; + } + + $properties = self::getProperties(); + + // Check if the property exists in the properties array. + if (array_key_exists($propertyName, $properties)) { + + // Prepare the pattern to be matched. + // Make sure we always deal with an array (string is converted). + $properties[$propertyName] = (array) $properties[$propertyName]; + + foreach ($properties[$propertyName] as $propertyMatchString) { + + $propertyPattern = str_replace('[VER]', self::VER, $propertyMatchString); + + // Escape the special character which is the delimiter. + $propertyPattern = str_replace('/', '\/', $propertyPattern); + + // Identify and extract the version. + preg_match('/'.$propertyPattern.'/is', $this->userAgent, $match); + + if (!empty($match[1])) { + $version = ( $type == self::VERSION_TYPE_FLOAT ? $this->prepareVersionNo($match[1]) : $match[1] ); + + return $version; + } + + } + + } + + return false; + } + + /** + * Retrieve the mobile grading, using self::MOBILE_GRADE_* constants. + * + * @return string One of the self::MOBILE_GRADE_* constants. + */ + public function mobileGrade() + { + $isMobile = $this->isMobile(); + + if ( + // Apple iOS 4-7.0 – Tested on the original iPad (4.3 / 5.0), iPad 2 (4.3 / 5.1 / 6.1), iPad 3 (5.1 / 6.0), iPad Mini (6.1), iPad Retina (7.0), iPhone 3GS (4.3), iPhone 4 (4.3 / 5.1), iPhone 4S (5.1 / 6.0), iPhone 5 (6.0), and iPhone 5S (7.0) + $this->isIOS() && $this->version('iPad', self::VERSION_TYPE_FLOAT)>=4.3 || + $this->isIOS() && $this->version('iPhone', self::VERSION_TYPE_FLOAT)>=4.3 || + $this->isIOS() && $this->version('iPod', self::VERSION_TYPE_FLOAT)>=4.3 || + + // Android 2.1-2.3 - Tested on the HTC Incredible (2.2), original Droid (2.2), HTC Aria (2.1), Google Nexus S (2.3). Functional on 1.5 & 1.6 but performance may be sluggish, tested on Google G1 (1.5) + // Android 3.1 (Honeycomb) - Tested on the Samsung Galaxy Tab 10.1 and Motorola XOOM + // Android 4.0 (ICS) - Tested on a Galaxy Nexus. Note: transition performance can be poor on upgraded devices + // Android 4.1 (Jelly Bean) - Tested on a Galaxy Nexus and Galaxy 7 + ( $this->version('Android', self::VERSION_TYPE_FLOAT)>2.1 && $this->is('Webkit') ) || + + // Windows Phone 7.5-8 - Tested on the HTC Surround (7.5), HTC Trophy (7.5), LG-E900 (7.5), Nokia 800 (7.8), HTC Mazaa (7.8), Nokia Lumia 520 (8), Nokia Lumia 920 (8), HTC 8x (8) + $this->version('Windows Phone OS', self::VERSION_TYPE_FLOAT)>=7.5 || + + // Tested on the Torch 9800 (6) and Style 9670 (6), BlackBerry® Torch 9810 (7), BlackBerry Z10 (10) + $this->is('BlackBerry') && $this->version('BlackBerry', self::VERSION_TYPE_FLOAT)>=6.0 || + // Blackberry Playbook (1.0-2.0) - Tested on PlayBook + $this->match('Playbook.*Tablet') || + + // Palm WebOS (1.4-3.0) - Tested on the Palm Pixi (1.4), Pre (1.4), Pre 2 (2.0), HP TouchPad (3.0) + ( $this->version('webOS', self::VERSION_TYPE_FLOAT)>=1.4 && $this->match('Palm|Pre|Pixi') ) || + // Palm WebOS 3.0 - Tested on HP TouchPad + $this->match('hp.*TouchPad') || + + // Firefox Mobile 18 - Tested on Android 2.3 and 4.1 devices + ( $this->is('Firefox') && $this->version('Firefox', self::VERSION_TYPE_FLOAT)>=18 ) || + + // Chrome for Android - Tested on Android 4.0, 4.1 device + ( $this->is('Chrome') && $this->is('AndroidOS') && $this->version('Android', self::VERSION_TYPE_FLOAT)>=4.0 ) || + + // Skyfire 4.1 - Tested on Android 2.3 device + ( $this->is('Skyfire') && $this->version('Skyfire', self::VERSION_TYPE_FLOAT)>=4.1 && $this->is('AndroidOS') && $this->version('Android', self::VERSION_TYPE_FLOAT)>=2.3 ) || + + // Opera Mobile 11.5-12: Tested on Android 2.3 + ( $this->is('Opera') && $this->version('Opera Mobi', self::VERSION_TYPE_FLOAT)>=11.5 && $this->is('AndroidOS') ) || + + // Meego 1.2 - Tested on Nokia 950 and N9 + $this->is('MeeGoOS') || + + // Tizen (pre-release) - Tested on early hardware + $this->is('Tizen') || + + // Samsung Bada 2.0 - Tested on a Samsung Wave 3, Dolphin browser + // @todo: more tests here! + $this->is('Dolfin') && $this->version('Bada', self::VERSION_TYPE_FLOAT)>=2.0 || + + // UC Browser - Tested on Android 2.3 device + ( ($this->is('UC Browser') || $this->is('Dolfin')) && $this->version('Android', self::VERSION_TYPE_FLOAT)>=2.3 ) || + + // Kindle 3 and Fire - Tested on the built-in WebKit browser for each + ( $this->match('Kindle Fire') || + $this->is('Kindle') && $this->version('Kindle', self::VERSION_TYPE_FLOAT)>=3.0 ) || + + // Nook Color 1.4.1 - Tested on original Nook Color, not Nook Tablet + $this->is('AndroidOS') && $this->is('NookTablet') || + + // Chrome Desktop 16-24 - Tested on OS X 10.7 and Windows 7 + $this->version('Chrome', self::VERSION_TYPE_FLOAT)>=16 && !$isMobile || + + // Safari Desktop 5-6 - Tested on OS X 10.7 and Windows 7 + $this->version('Safari', self::VERSION_TYPE_FLOAT)>=5.0 && !$isMobile || + + // Firefox Desktop 10-18 - Tested on OS X 10.7 and Windows 7 + $this->version('Firefox', self::VERSION_TYPE_FLOAT)>=10.0 && !$isMobile || + + // Internet Explorer 7-9 - Tested on Windows XP, Vista and 7 + $this->version('IE', self::VERSION_TYPE_FLOAT)>=7.0 && !$isMobile || + + // Opera Desktop 10-12 - Tested on OS X 10.7 and Windows 7 + $this->version('Opera', self::VERSION_TYPE_FLOAT)>=10 && !$isMobile + ){ + return self::MOBILE_GRADE_A; + } + + if ( + $this->isIOS() && $this->version('iPad', self::VERSION_TYPE_FLOAT)<4.3 || + $this->isIOS() && $this->version('iPhone', self::VERSION_TYPE_FLOAT)<4.3 || + $this->isIOS() && $this->version('iPod', self::VERSION_TYPE_FLOAT)<4.3 || + + // Blackberry 5.0: Tested on the Storm 2 9550, Bold 9770 + $this->is('Blackberry') && $this->version('BlackBerry', self::VERSION_TYPE_FLOAT)>=5 && $this->version('BlackBerry', self::VERSION_TYPE_FLOAT)<6 || + + //Opera Mini (5.0-6.5) - Tested on iOS 3.2/4.3 and Android 2.3 + ($this->version('Opera Mini', self::VERSION_TYPE_FLOAT)>=5.0 && $this->version('Opera Mini', self::VERSION_TYPE_FLOAT)<=7.0 && + ($this->version('Android', self::VERSION_TYPE_FLOAT)>=2.3 || $this->is('iOS')) ) || + + // Nokia Symbian^3 - Tested on Nokia N8 (Symbian^3), C7 (Symbian^3), also works on N97 (Symbian^1) + $this->match('NokiaN8|NokiaC7|N97.*Series60|Symbian/3') || + + // @todo: report this (tested on Nokia N71) + $this->version('Opera Mobi', self::VERSION_TYPE_FLOAT)>=11 && $this->is('SymbianOS') + ){ + return self::MOBILE_GRADE_B; + } + + if ( + // Blackberry 4.x - Tested on the Curve 8330 + $this->version('BlackBerry', self::VERSION_TYPE_FLOAT)<=5.0 || + // Windows Mobile - Tested on the HTC Leo (WinMo 5.2) + $this->match('MSIEMobile|Windows CE.*Mobile') || $this->version('Windows Mobile', self::VERSION_TYPE_FLOAT)<=5.2 || + + // Tested on original iPhone (3.1), iPhone 3 (3.2) + $this->isIOS() && $this->version('iPad', self::VERSION_TYPE_FLOAT)<=3.2 || + $this->isIOS() && $this->version('iPhone', self::VERSION_TYPE_FLOAT)<=3.2 || + $this->isIOS() && $this->version('iPod', self::VERSION_TYPE_FLOAT)<=3.2 || + + // Internet Explorer 7 and older - Tested on Windows XP + $this->version('IE', self::VERSION_TYPE_FLOAT)<=7.0 && !$isMobile + ){ + return self::MOBILE_GRADE_C; + } + + //All older smartphone platforms and featurephones - Any device that doesn't support media queries + //will receive the basic, C grade experience. + return self::MOBILE_GRADE_C; + } +} diff --git a/core/um-actions-ajax.php b/core/um-actions-ajax.php index f25b34aa..d3594827 100644 --- a/core/um-actions-ajax.php +++ b/core/um-actions-ajax.php @@ -16,4 +16,69 @@ files->delete_core_user_photo( $user_id, 'profile_photo' ); + + } + + /*** + *** @remove cover photo silently + ***/ + add_action('wp_ajax_nopriv_ultimatemember_delete_cover_photo', 'ultimatemember_delete_cover_photo'); + add_action('wp_ajax_ultimatemember_delete_cover_photo', 'ultimatemember_delete_cover_photo'); + function ultimatemember_delete_cover_photo(){ + global $ultimatemember; + extract($_REQUEST); + + if ( !um_can_edit_profile( $user_id ) ) die( __('You can not edit this user') ); + + $ultimatemember->files->delete_core_user_photo( $user_id, 'cover_photo' ); + + } + + /*** + *** @resampling/crop images + ***/ + add_action('wp_ajax_nopriv_ultimatemember_resize_image', 'ultimatemember_resize_image'); + add_action('wp_ajax_ultimatemember_resize_image', 'ultimatemember_resize_image'); + function ultimatemember_resize_image(){ + global $ultimatemember; + $output = 0; + + extract($_REQUEST); + + if ( !isset($src) || !isset($coord) ) die( __('Invalid parameters') ); + + $coord_n = substr_count($coord, ","); + if ( $coord_n != 3 ) die( __('Invalid coordinates') ); + + $um_is_temp_image = um_is_temp_image( $src ); + if ( !$um_is_temp_image ) die( __('Invalid Image file') ); + + $crop = explode(',', $coord ); + $crop = array_map('intval', $crop); + + $uri = $ultimatemember->files->resize_image( $um_is_temp_image, $crop ); + + // If you're updating a user + if ( isset( $user_id ) && $user_id > 0 ) { + $uri = $ultimatemember->files->new_user_upload( $user_id, $um_is_temp_image, $key ); + } + + $output = $uri; + + if(is_array($output)){ print_r($output); }else{ echo $output; } die; + } \ No newline at end of file diff --git a/core/um-actions-members.php b/core/um-actions-members.php index 43540b93..1ae59a5c 100644 --- a/core/um-actions-members.php +++ b/core/um-actions-members.php @@ -191,6 +191,8 @@ if ( um_members('users_per_page') ) { + $default_size = str_replace( 'px', '', um_get_option('profile_photosize') ); + ?>
@@ -202,13 +204,13 @@
-
-
+
+
-
+
diff --git a/core/um-actions-profile.php b/core/um-actions-profile.php index b7590eb5..bf22087b 100644 --- a/core/um-actions-profile.php +++ b/core/um-actions-profile.php @@ -46,21 +46,62 @@ add_action('um_profile_header_cover_area', 'um_profile_header_cover_area' ); function um_profile_header_cover_area( $args ) { global $ultimatemember; - + if ( $args['cover_enabled'] == 1 ) { + $overlay = ' + + + + '.__('Change your cover photo').' + + + '; + ?> - -
+ +
+ + menu->new_ui( 'bc', 'div.um-cover', 'click', array( + ''.__('Change cover photo','ultimatemember').'', + ''.__('Remove','ultimatemember').'', + ''.__('Cancel','ultimatemember').'', + ), 'can_edit_user' ); + + ?> + + fields->add_hidden_field( 'cover_photo' ); ?> + + +
- - + + + + mobile->isMobile() ){ + echo um_user('cover_photo', 300); + } else { + echo um_user('cover_photo', 1000); + } + + ?> + + + + + +
+
- + + + + '.__('Change your profile photo').' + + + '; + ?>
-
- +
+ + + + fields->add_hidden_field( 'profile_photo' ); + + if ( !um_profile('profile_photo') ) { // has profile photo + + echo $ultimatemember->menu->new_ui( 'bc', 'div.um-profile-photo', 'click', array( + ''.__('Upload photo','ultimatemember').'', + ''.__('Cancel','ultimatemember').'', + ), 'can_edit_user' ); + + } else if ( $ultimatemember->fields->editing == true ) { + + echo $ultimatemember->menu->new_ui( 'bc', 'div.um-profile-photo', 'click', array( + ''.__('Change photo','ultimatemember').'', + ''.__('Remove photo','ultimatemember').'', + ''.__('Cancel','ultimatemember').'', + ), 'can_edit_user' ); + + } + + } + + ?> +
@@ -95,15 +176,15 @@
+
- Chief Executive Officer of Ultimate Member - - 28 Years Old - - United Kingdom + + profile->show_meta( $args['metafields'] ); ?> +
- - + + +
@@ -161,9 +242,9 @@ if ( isset( $ultimatemember->user->cannot_edit ) && $ultimatemember->user->cannot_edit == 1 ) return; if ( $ultimatemember->fields->editing == true ) { - $output .= '
'; + $output .= '
'; } else { - $output .= '
'; + $output .= '
'; } echo $output; diff --git a/core/um-builtin.php b/core/um-builtin.php index 074a6246..007f2566 100644 --- a/core/um-builtin.php +++ b/core/um-builtin.php @@ -520,6 +520,31 @@ class UM_Builtin { 'placeholder' => 'Enter a bit about yourself...', ), + 'profile_photo' => array( + 'title' => 'Profile Photo', + 'metakey' => 'profile_photo', + 'type' => 'image', + 'label' => 'Change your profile photo', + 'upload_text' => 'Upload your photo here', + 'icon' => 'um-icon-camera-5', + 'crop' => 1, + 'min_width' => str_replace('px','',um_get_option('profile_photosize')), + 'min_height' => str_replace('px','',um_get_option('profile_photosize')), + ), + + 'cover_photo' => array( + 'title' => 'Cover Photo', + 'metakey' => 'cover_photo', + 'type' => 'image', + 'label' => 'Change your cover photo', + 'upload_text' => 'Upload profile cover here', + 'icon' => 'um-icon-photo-2', + 'crop' => 2, + 'modal_size' => 'large', + 'ratio' => str_replace(':1','',um_get_option('profile_cover_ratio')), + 'min_width' => 1000, + ), + 'birth_date' => array( 'title' => 'Birth Date', 'metakey' => 'birth_date', diff --git a/core/um-fields.php b/core/um-fields.php index 5da36579..5d1dcfb1 100644 --- a/core/um-fields.php +++ b/core/um-fields.php @@ -11,6 +11,27 @@ class UM_Fields { } + /*** + *** @hidden fields inside shortcode + ***/ + function add_hidden_field( $field ) { + global $ultimatemember; + echo '
'; + + $fields = $ultimatemember->builtin->get_specific_fields( $field ); + + $output = null; + + foreach( $fields as $key => $data ) { + $output .= $ultimatemember->fields->edit_field( $key, $data ); + } + + echo $output; + + echo '
'; + + } + /*** *** @detect if we're editing profile in shortcode ***/ @@ -221,13 +242,23 @@ class UM_Fields { $output = null; $output .= '
'; - if ( isset($data['icon']) && $data['icon'] != '' && ( $this->field_icons == 'label' || $this->viewing == true ) ) { + if ( isset($data['icon']) && $data['icon'] != '' && isset( $this->field_icons ) && ( $this->field_icons == 'label' || $this->viewing == true ) ) { $output .= '
'; } $output .= ''; - if ( isset( $data['help'] ) && $this->viewing == false ) {$output .= '';} + if ( isset( $data['help'] ) && !empty( $data['help'] ) && $this->viewing == false ) { + + if ( !$ultimatemember->mobile->isMobile() ) { + $output .= ''; + } + + if ( $ultimatemember->mobile->isMobile() ) { + $output .= ''. $data['help'] . ''; + } + + } $output .= '
'; @@ -260,7 +291,7 @@ class UM_Fields { $classes .= 'um-timepicker '; } - if ( isset($data['icon']) && $this->field_icons == 'field' ) { + if ( isset($data['icon']) && $data['icon'] && $this->field_icons == 'field' ) { $classes .= 'um-iconed'; } @@ -594,7 +625,29 @@ class UM_Fields { case 'image': if ( !isset( $array['crop'] ) ) $array['crop'] = 0; - + + if ( $array['crop'] == 0 ) { + $array['crop_data'] = 0; + } else if ( $array['crop'] == 1 ) { + $array['crop_data'] = 'square'; + } else if ( $array['crop'] == 2 ) { + $array['crop_data'] = 'cover'; + } else { + $array['crop_data'] = 'user'; + } + + if ( !isset( $array['modal_size'] ) ) $array['modal_size'] = 'normal'; + + if ( $array['crop'] > 0 ) { + $array['crop_class'] = 'crop'; + } else { + $array['crop_class'] = ''; + } + + if ( !isset( $array['ratio'] ) ) $array['ratio'] = 1.0; + if ( !isset( $array['min_width'] ) ) $array['min_width'] = ''; + if ( !isset( $array['min_height'] ) ) $array['min_height'] = ''; + if (!isset($array['invalid_image'])) $array['invalid_image'] = "Please upload a valid image!"; if (!isset($array['allowed_types'])) { $array['allowed_types'] = "gif,jpg,jpeg,png"; @@ -615,6 +668,8 @@ class UM_Fields { case 'file': + if ( !isset( $array['modal_size'] ) ) $array['modal_size'] = 'normal'; + if (!isset($array['allowed_types'])) { $array['allowed_types'] = "pdf,txt"; } else { @@ -685,7 +740,7 @@ class UM_Fields { $output .= '
'; - if ( isset($icon) && $this->field_icons == 'field' ) { + if ( isset($icon) && $icon && $this->field_icons == 'field' ) { $output .= '
'; @@ -719,7 +774,7 @@ class UM_Fields { $output .= '
'; - if ( isset($icon) && $this->field_icons == 'field' ) { + if ( isset($icon) && $icon && $this->field_icons == 'field' ) { $output .= '
'; @@ -748,7 +803,7 @@ class UM_Fields { $output .= '
'; - if ( isset($icon) && $this->field_icons == 'field' ) { + if ( isset($icon) && $icon && $this->field_icons == 'field' ) { $output .= '
'; @@ -782,7 +837,7 @@ class UM_Fields { $output .= '
'; - if ( isset($icon) && $this->field_icons == 'field' ) { + if ( isset($icon) && $icon && $this->field_icons == 'field' ) { $output .= '
'; @@ -809,7 +864,7 @@ class UM_Fields { $output .= '
'; - if ( isset($icon) && $this->field_icons == 'field' ) { + if ( isset($icon) && $icon && $this->field_icons == 'field' ) { $output .= '
'; @@ -842,7 +897,7 @@ class UM_Fields { $output .= '
'; - if ( isset($icon) && $this->field_icons == 'field' ) { + if ( isset($icon) && $icon && $this->field_icons == 'field' ) { $output .= '
'; @@ -870,7 +925,7 @@ class UM_Fields { $output .= '
'; - if ( isset($icon) && $this->field_icons == 'field' ) { + if ( isset($icon) && $icon && $this->field_icons == 'field' ) { $output .= '
'; @@ -898,7 +953,7 @@ class UM_Fields { $output .= '
'; - if ( isset($icon) && $this->field_icons == 'field' ) { + if ( isset($icon) && $icon && $this->field_icons == 'field' ) { $output .= '
'; @@ -977,19 +1032,13 @@ class UM_Fields { $output .= $this->field_label($label, $key, $data); } - if ( $crop ) { - $crop_class = 'crop'; - } else { - $crop_class = ''; - } - $modal_label = ( isset( $data['label'] ) ) ? $data['label'] : __('Upload Photo','ultimatemember'); $output .= ''; @@ -1000,12 +1049,20 @@ class UM_Fields { $output .= '
'; - $output .= '
'; - $output .= '
'.$button_text.'
'; + if ( isset( $this->set_id ) ) { + $set_id = $this->set_id; + $set_mode = $this->set_mode; + } else { + $set_id = 0; + $set_mode = ''; + } + + $output .= '
'; + $output .= '
'.$button_text.'
'; $output .= ''; - $output .= '
'; break; /* Select dropdown */ @@ -1323,7 +1417,9 @@ class UM_Fields { } // Custom filter for field output + if ( isset( $this->set_mode ) ) { $output = apply_filters("um_{$key}_form_edit_field", $output, $this->set_mode); + } return $output; } diff --git a/core/um-files.php b/core/um-files.php index a4589f32..f4b631d4 100644 --- a/core/um-files.php +++ b/core/um-files.php @@ -89,29 +89,20 @@ class UM_Files { $this->upload_dir = wp_upload_dir(); $this->upload_basedir = $this->upload_dir['basedir'] . '/ultimatemember/'; - $this->upload_baseurl = $this->upload_dir['baseurl'] . '/ultimatemember/'; + + $this->upload_temp = $this->upload_basedir . 'temp/'; + $this->upload_temp_url = $this->upload_baseurl . 'temp/'; - // create plugin uploads directory if (!file_exists( $this->upload_basedir )) { @mkdir( $this->upload_basedir, 0777, true); } + + if (!file_exists( $this->upload_temp )) { + @mkdir( $this->upload_temp , 0777, true); + } } - - /*** - *** @Return upload directory - ***/ - function upload_dir(){ - return $this->upload_basedir; - } - - /*** - *** @Return upload URL - ***/ - function upload_url(){ - return $this->upload_baseurl; - } /*** *** @Generate unique temp directory @@ -119,34 +110,41 @@ class UM_Files { function unique_dir(){ global $ultimatemember; $unique_number = $ultimatemember->validation->generate(); - $array['dir'] = $this->upload_basedir . 'temp/'. $unique_number . '/'; - $array['url'] = $this->upload_baseurl . 'temp/'. $unique_number . '/'; + $array['dir'] = $this->upload_temp . $unique_number . '/'; + $array['url'] = $this->upload_temp_url . $unique_number . '/'; return $array; } /*** - *** @Fix image orientation + *** @get path only without file name ***/ - function fix_image_orientation(&$image, $source){ + function path_only( $file ) { + return trailingslashit( dirname( $file ) ); + } + + /*** + *** @fix image orientation + ***/ + function fix_image_orientation($rotate, $source){ $exif = @exif_read_data($source); if (isset($exif['Orientation'])) { switch ($exif['Orientation']) { case 3: - $image = imagerotate($image, 180, 0); + $rotate = imagerotate($rotate, 180, 0); break; case 6: - $image = imagerotate($image, -90, 0); + $rotate = imagerotate($rotate, -90, 0); break; case 8: - $image = imagerotate($image, 90, 0); + $rotate = imagerotate($rotate, 90, 0); break; } } - return $image; + return $rotate; } /*** @@ -154,7 +152,7 @@ class UM_Files { ***/ function create_and_copy_image($source, $destination, $quality = 100) { - $info = getimagesize($source); + $info = @getimagesize($source); if ($info['mime'] == 'image/jpeg'){ @@ -169,11 +167,26 @@ class UM_Files { $image = imagecreatefrompng($source); } + + list($w, $h) = @getimagesize( $source ); + if ( $w > um_get_option('image_max_width') ) { - $this->fix_image_orientation($image, $source); + $ratio = round( $w / $h, 2 ); + $new_w = um_get_option('image_max_width'); + $new_h = round( $new_w / $ratio, 2 ); + + $image_p = imagecreatetruecolor( $new_w, $new_h ); + imagecopyresampled( $image_p, $image, 0, 0, 0, 0, $new_w, $new_h, $w, $h ); + $image_p = $this->fix_image_orientation($image_p, $source); + imagejpeg( $image_p, $destination, $quality); - imagejpeg($image, $destination, $quality); - + } else { + + $image = $this->fix_image_orientation($image, $source); + imagejpeg( $image, $destination, $quality); + + } + } /*** @@ -292,6 +305,10 @@ class UM_Files { $error = $data['extension_error']; } elseif ( isset($data['min_size']) && ( $fileinfo['size'] < $data['min_size'] ) ) { $error = $data['min_size_error']; + } elseif ( isset($data['min_width']) && ( $fileinfo['width'] < $data['min_width'] ) ) { + $error = sprintf(__('Your photo is too small. It must be at least %spx wide.'), $data['min_width']); + } elseif ( isset($data['min_height']) && ( $fileinfo['height'] < $data['min_height'] ) ) { + $error = sprintf(__('Your photo is too small. It must be at least %spx wide.'), $data['min_height']); } return $error; @@ -325,4 +342,142 @@ class UM_Files { return false; } + /*** + *** @delete a main user photo + ***/ + function delete_core_user_photo( $user_id, $type ) { + + delete_user_meta( $user_id, $type ); + + $dir = $this->upload_basedir . $user_id . '/'; + $prefix = $type; + chdir($dir); + $matches = glob($prefix.'*',GLOB_MARK); + + if( is_array($matches) && !empty($matches)) { + foreach($matches as $match) { + if( is_file($dir.$match) ) unlink($dir.$match); + } + } + + if ( count(glob("$dir/*")) === 0) { + rmdir( $dir ); + } + + } + + /*** + *** @resize a local image + ***/ + function resize_image( $file, $crop ) { + + $targ_x1 = $crop[0]; + $targ_y1 = $crop[1]; + $targ_x2 = $crop[2]; + $targ_y2 = $crop[3]; + + $img_r = imagecreatefromjpeg($file); + $dst_r = imagecreatetruecolor( $targ_x2, $targ_y2 ); + + imagecopy( $dst_r, $img_r, 0, 0, $targ_x1, $targ_y1, $targ_x2, $targ_y2 ); + imagejpeg( $dst_r, $this->path_only( $file ) . basename( $file ), 100); + + $split = explode('/ultimatemember/temp/', $file); + return $this->upload_temp_url . $split[1]; + + } + + /*** + *** @new user upload + ***/ + function new_user_upload( $user_id, $source, $key ) { + + // if he does not have uploads dir yet + if ( !file_exists( $this->upload_basedir . $user_id . '/' ) ) { + @mkdir( $this->upload_basedir . $user_id . '/' , 0777, true); + } + + // name and extension stuff + $source_name = basename( $source ); + + if ( $key == 'profile_photo' ) { + $source_name = 'profile_photo.jpg'; + } + + if ( $key == 'cover_photo' ) { + $source_name = 'cover_photo.jpg'; + } + + $ext = '.' . pathinfo($source_name, PATHINFO_EXTENSION); + $name = str_replace( $ext, '', $source_name ); + $filename = $name . $ext; + + // copy image + copy( $source, $this->upload_basedir . $user_id . '/' . $filename ); + + // thumbs + if ( $key == 'profile_photo' ) { + + list($w, $h) = @getimagesize( $source ); + + $sizes = um_get_option('photo_thumb_sizes'); + foreach( $sizes as $size ) { + + if ( file_exists( $this->upload_basedir . $user_id . '/' . $name . '-' . $size . $ext ) ) { + unlink( $this->upload_basedir . $user_id . '/' . $name . '-' . $size . $ext ); + } + + if ( $size < $w ) { + + $thumb_s = imagecreatefromjpeg( $source ); + $thumb = imagecreatetruecolor( $size, $size ); + imagecopyresampled( $thumb, $thumb_s, 0, 0, 0, 0, $size, $size, $w, $h ); + imagejpeg( $thumb, $this->upload_basedir . $user_id . '/' . $name . '-' . $size . $ext, 100); + + } + + } + + } + + if ( $key == 'cover_photo' ) { + + list($w, $h) = @getimagesize( $source ); + + $sizes = um_get_option('cover_thumb_sizes'); + foreach( $sizes as $size ) { + + $ratio = round( $w / $h, 2 ); + $height = round( $size / $ratio, 2 ); + + if ( file_exists( $this->upload_basedir . $user_id . '/' . $name . '-' . $size . $ext ) ) { + unlink( $this->upload_basedir . $user_id . '/' . $name . '-' . $size . $ext ); + } + + if ( $size < $w ) { + + $thumb_s = imagecreatefromjpeg( $source ); + $thumb = imagecreatetruecolor( $size, $height ); + imagecopyresampled( $thumb, $thumb_s, 0, 0, 0, 0, $size, $height, $w, $h ); + imagejpeg( $thumb, $this->upload_basedir . $user_id . '/' . $name . '-' . $size . $ext, 100); + + } + + } + + } + + // clean up temp + $dir = dirname( $source ); + unlink( $source ); + rmdir( $dir ); + + // update user's meta + update_user_meta( $user_id, $key, $filename ); + + // the url of upload + return $this->upload_baseurl . $user_id . '/' . $filename; + + } + } \ No newline at end of file diff --git a/core/um-filters-arguments.php b/core/um-filters-arguments.php index 6ae2b9d1..cf853b22 100644 --- a/core/um-filters-arguments.php +++ b/core/um-filters-arguments.php @@ -4,16 +4,23 @@ *** @filter for shortcode args ***/ add_filter('um_shortcode_args_filter', 'um_shortcode_args_filter', 99); - function um_shortcode_args_filter( $array ) { + function um_shortcode_args_filter( $args ) { global $ultimatemember; - - // checks for message mode + if ($ultimatemember->shortcodes->message_mode == true) { - $array['template'] = 'message'; + $args['template'] = 'message'; $ultimatemember->shortcodes->custom_message = um_user( um_user('status') . '_message' ); um_reset_user(); } - return $array; + foreach( $args as $k => $v ) { + if ( $ultimatemember->validation->is_serialized( $args[$k] ) ) { + if ( !empty( $args[$k] ) ) { + $args[$k] = unserialize( $args[$k] ); + } + } + } + + return $args; } \ No newline at end of file diff --git a/core/um-filters-fields.php b/core/um-filters-fields.php index 37f80f4c..41cd1db5 100644 --- a/core/um-filters-fields.php +++ b/core/um-filters-fields.php @@ -11,7 +11,11 @@ $form_id = (isset ( $ultimatemember->fields->set_id ) ) ? $ultimatemember->fields->set_id : null; $mode = (isset( $ultimatemember->fields->set_mode ) ) ? $ultimatemember->fields->set_mode : null; + if ( $form_id && $mode ) { $array = $ultimatemember->query->get_attr('custom_fields', $form_id ); + } else { + $array = ''; + } return $array; diff --git a/core/um-filters-members.php b/core/um-filters-members.php index f9a170dd..4aadfa9e 100644 --- a/core/um-filters-members.php +++ b/core/um-filters-members.php @@ -1,23 +1,4 @@ $v ) { - if ( $ultimatemember->validation->is_serialized( $args[$k] ) ) { - if ( !empty( $args[$k] ) ) { - $args[$k] = unserialize( $args[$k] ); - } - } - } - - return $args; - } /*** *** @prepare the query args to show members diff --git a/core/um-menu.php b/core/um-menu.php new file mode 100644 index 00000000..2cfbfbfa --- /dev/null +++ b/core/um-menu.php @@ -0,0 +1,33 @@ + + +
+
+
+
    + $v ) { ?> + +
  • + + +
+
+
+ + ' . um_user( $k ) . ''; + $items[] = ''; + } + } + + if ( isset( $items ) ) { + array_pop($items); + foreach( $items as $item ) { + $output .= $item; + } + } + + return $output; + } + +} \ No newline at end of file diff --git a/core/um-setup.php b/core/um-setup.php index d8116908..3730773f 100644 --- a/core/um-setup.php +++ b/core/um-setup.php @@ -84,6 +84,7 @@ class UM_Setup { '_um_secondary_btn_color' => '#eee', '_um_secondary_btn_hover' => '#e5e5e5', '_um_secondary_btn_text' => '#666', + '_um_profile_show_bio' => 1, '_um_profile_role' => '0', '_um_profile_template' => 'profile', '_um_profile_max_width' => '1000px', diff --git a/core/um-short-functions.php b/core/um-short-functions.php index 7f55e2dd..b786095b 100644 --- a/core/um-short-functions.php +++ b/core/um-short-functions.php @@ -1,5 +1,40 @@ files->upload_temp . $url[1]; + + if ( !file_exists( $src ) ) + return false; + + list($width, $height, $type, $attr) = @getimagesize($src); + if ( isset( $width ) && isset( $height ) ) + return $src; + + } + return false; + } + /*** *** @Get core page url ***/ @@ -396,6 +431,56 @@ } } + /*** + *** @user uploads uri + ***/ + function um_user_uploads_uri() { + global $ultimatemember; + $uri = $ultimatemember->files->upload_baseurl . um_user('ID') . '/'; + return $uri; + } + + /*** + *** @get cover uri + ***/ + function um_get_cover_uri( $image, $attrs ) { + global $ultimatemember; + $uri = false; + if ( file_exists( $ultimatemember->files->upload_basedir . um_user('ID') . '/cover_photo.jpg' ) ) { + $uri = um_user_uploads_uri() . 'cover_photo.jpg?' . time(); + } + if ( file_exists( $ultimatemember->files->upload_basedir . um_user('ID') . '/cover_photo-' . $attrs. '.jpg' ) ){ + $uri = um_user_uploads_uri() . 'cover_photo-'.$attrs.'.jpg?' . time(); + } + return $uri; + } + + /*** + *** @get avatar uri + ***/ + function um_get_avatar_uri( $image, $attrs ) { + global $ultimatemember; + $uri = false; + if ( file_exists( $ultimatemember->files->upload_basedir . um_user('ID') . '/profile_photo.jpg' ) ) { + $uri = um_user_uploads_uri() . 'profile_photo.jpg?' . time(); + } + if ( file_exists( $ultimatemember->files->upload_basedir . um_user('ID') . '/profile_photo-' . $attrs. '.jpg' ) ){ + $uri = um_user_uploads_uri() . 'profile_photo-'.$attrs.'.jpg?' . time(); + } + return $uri; + } + + /*** + *** @default avatar + ***/ + function um_get_default_avatar_uri() { + $uri = um_get_option('default_avatar'); + $uri = $uri['url']; + if ( !$uri ) + $uri = um_url . 'assets/img/default_avatar.png'; + return $uri; + } + /*** *** @get user data ***/ @@ -526,24 +611,21 @@ case 'profile_photo': - $default_avatar_uri = um_get_option('default_avatar'); - $default_avatar_uri = $default_avatar_uri['url']; - - if ( !$default_avatar_uri ) { - $default_avatar_uri = um_url . 'assets/img/default_avatar.png'; + if ( um_profile('profile_photo') ) { + $avatar_uri = um_get_avatar_uri( um_profile('profile_photo'), $attrs ); + } else { + $avatar_uri = um_get_default_avatar_uri(); } - - $default_avatar_uri = um_url . 'assets/img/Dollarphotoclub_57189843.jpg'; - - return ''; + + return ''; break; case 'cover_photo': if ( um_profile('cover_photo') ) { - return ''; + $cover_uri = um_get_cover_uri( um_profile('cover_photo'), $attrs ); + return ''; } - return ''; break; } diff --git a/core/um-uninstall.php b/core/um-uninstall.php index 68a8b159..2f6b0a4e 100644 --- a/core/um-uninstall.php +++ b/core/um-uninstall.php @@ -14,9 +14,9 @@ class UM_Uninstall { foreach( wp_load_alloptions() as $k => $v ) { - if ( substr( $k, 0, 3 ) == 'um_' || substr( $k, 0, 3 ) == 'UM_' ) { + if ( substr( $k, 0, 3 ) == 'um_' ) { - if ( $k == 'um_core_forms' || $k == 'um_core_pages' || $k == 'um_core_directories' ) { + if ( $k == 'um_core_pages' ) { $v = unserialize( $v ); foreach( $v as $post_id ) { wp_delete_post( $post_id, 1 ); @@ -29,10 +29,14 @@ class UM_Uninstall { } + $forms = get_posts( array( 'post_type' => 'um_form', 'numberposts' => -1 ) ); + foreach( $forms as $form ) {wp_delete_post( $form->ID, 1 );} + + $directories = get_posts( array( 'post_type' => 'um_directory', 'numberposts' => -1 ) ); + foreach( $directories as $directory ) {wp_delete_post( $directory->ID, 1 );} + $roles = get_posts( array( 'post_type' => 'um_role', 'numberposts' => -1 ) ); - foreach( $roles as $role ) { - wp_delete_post( $role->ID, 1 ); - } + foreach( $roles as $role ) {wp_delete_post( $role->ID, 1 );} if ( is_plugin_active( um_plugin ) ) { deactivate_plugins( um_plugin ); diff --git a/templates/members.php b/templates/members.php index 4b0188d7..4241620b 100644 --- a/templates/members.php +++ b/templates/members.php @@ -2,8 +2,6 @@
- - diff --git a/templates/modal/um_upload_image.php b/templates/modal/um_upload_image.php deleted file mode 100644 index daa75ed8..00000000 --- a/templates/modal/um_upload_image.php +++ /dev/null @@ -1,3 +0,0 @@ - \ No newline at end of file diff --git a/templates/modal/um_upload_single.php b/templates/modal/um_upload_single.php new file mode 100644 index 00000000..83e0abfa --- /dev/null +++ b/templates/modal/um_upload_single.php @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/um-config.php b/um-config.php index 6e071011..30a4815a 100644 --- a/um-config.php +++ b/um-config.php @@ -738,10 +738,30 @@ $this->sections[] = array( $this->sections[] = array( - 'icon' => 'um-icon-files', + 'icon' => 'um-icon-photo-2', 'title' => __( 'Image & File Uploads'), 'fields' => array( + array( + 'id' => 'photo_thumb_sizes', + 'type' => 'multi_text', + 'title' => __( 'Profile Photo Thumbnail Sizes' ), + 'desc' => __( 'Here you can define which thumbnail sizes will be created for each profile photo upload.' ), + 'default' => array( 40, 80, str_replace('px','',um_get_option('profile_photosize') ) ), + 'validate' => 'numeric', + 'add_text' => __('Add New Size'), + ), + + array( + 'id' => 'cover_thumb_sizes', + 'type' => 'multi_text', + 'title' => __( 'Cover Photo Thumbnail Sizes' ), + 'desc' => __( 'Here you can define which thumbnail sizes will be created for each cover photo upload.' ), + 'default' => array( 300, 600 ), + 'validate' => 'numeric', + 'add_text' => __('Add New Size'), + ), + array( 'id' => 'image_compression', 'type' => 'text', @@ -750,6 +770,15 @@ $this->sections[] = array( 'default' => 60, 'validate' => 'numeric', ), + + array( + 'id' => 'image_max_width', + 'type' => 'text', + 'title' => __( 'Image Upload Maximum Width' ), + 'desc' => __( 'Any image upload above this width will be resized to this limit automatically.' ), + 'default' => 1000, + 'validate' => 'numeric', + ), ) @@ -1163,6 +1192,14 @@ $this->sections[] = array( 'transparent' => false, ), + array( + 'id' => 'profile_show_bio', + 'type' => 'switch', + 'title' => __( 'Show user description in header' ), + 'default' => um_get_metadefault('profile_show_bio'), + 'desc' => 'Switch on/off the user description on profile header', + ), + ) ); diff --git a/um-init.php b/um-init.php index 0dd2818d..6e707f44 100644 --- a/um-init.php +++ b/um-init.php @@ -36,6 +36,7 @@ class UM_API { require_once um_path . 'core/um-fields.php'; require_once um_path . 'core/um-form.php'; require_once um_path . 'core/um-user.php'; + require_once um_path . 'core/um-profile.php'; require_once um_path . 'core/um-query.php'; require_once um_path . 'core/um-datetime.php'; require_once um_path . 'core/um-chart.php'; @@ -44,6 +45,7 @@ class UM_API { require_once um_path . 'core/um-taxonomies.php'; require_once um_path . 'core/um-validation.php'; require_once um_path . 'core/um-navmenu.php'; + require_once um_path . 'core/um-menu.php'; require_once um_path . 'core/um-access.php'; require_once um_path . 'core/um-permalinks.php'; require_once um_path . 'core/um-mail.php'; @@ -51,6 +53,8 @@ class UM_API { require_once um_path . 'core/um-logout.php'; require_once um_path . 'core/um-modal.php'; + require_once um_path . 'core/lib/mobiledetect/Mobile_Detect.php'; + require_once um_path . 'core/um-actions-form.php'; require_once um_path . 'core/um-actions-wpadmin.php'; require_once um_path . 'core/um-actions-core.php'; @@ -93,6 +97,7 @@ class UM_API { $this->register = new UM_Register(); $this->fields = new UM_Fields(); $this->user = new UM_User(); + $this->profile = new UM_Profile(); $this->datetime = new UM_DateTime(); $this->chart = new UM_Chart(); $this->builtin = new UM_Builtin(); @@ -101,12 +106,14 @@ class UM_API { $this->taxonomies = new UM_Taxonomies(); $this->validation = new UM_Validation(); $this->query = new UM_Query(); + $this->menu = new UM_Menu(); $this->access = new UM_Access(); $this->permalinks = new UM_Permalinks(); $this->mail = new UM_Mail(); $this->members = new UM_Members(); $this->logout = new UM_Logout(); $this->modal = new UM_Modal(); + $this->mobile = new Mobile_Detect; $this->options = get_option('um_options');