├── bower.json ├── feedback_me.jquery.json ├── examples ├── example_page.php └── example_send_feedback.php ├── MIT-LICENSE.txt ├── ChangeLog.markdown ├── README.md ├── css └── jquery.feedback_me.css └── js └── jquery.feedback_me.js /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Daniel Reznick", 3 | "name": "feedback_me", 4 | "description": "This jQuery plug-in allows user to easily add an animatable UI widget with a feedback form which slides from the side of the screen.", 5 | "version": "0.5.8", 6 | "homepage": "http://feedback-me.appspot.com/", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/vedmack/feedback_me" 10 | }, 11 | "main": [ 12 | "js/jquery.feedback_me.js", 13 | "css/jquery.feedback_me.css" 14 | ], 15 | "dependencies": { 16 | "jquery": ">=1.4.0" 17 | }, 18 | "keywords": [ 19 | "jquery", 20 | "feedback", 21 | "plugin", 22 | "slide", 23 | "form" 24 | ], 25 | "ignore": [ 26 | "feedback_me.jquery.json" 27 | ] 28 | } -------------------------------------------------------------------------------- /feedback_me.jquery.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "feedback_me", 3 | "title": "jQuery Feedback Me Plugin", 4 | "description": "This jQuery plug-in allows user to easily add an animatable UI widget with a feedback form which slides from the side of the screen.", 5 | "keywords": [ 6 | "jquery", 7 | "feedback", 8 | "plugin", 9 | "slide", 10 | "form" 11 | ], 12 | "version": "0.5.8", 13 | "author": { 14 | "name": "Daniel Reznick", 15 | "url": "https://github.com/vedmack" 16 | }, 17 | "licenses": [ 18 | { 19 | "type": "MIT", 20 | "url": "http://www.opensource.org/licenses/mit-license.php" 21 | } 22 | ], 23 | "bugs": "https://github.com/vedmack/feedback_me/issues", 24 | "homepage": "http://feedback-me.appspot.com/", 25 | "docs": "https://github.com/vedmack/feedback_me/blob/master/README.md", 26 | "download": "https://github.com/vedmack/feedback_me/archive/master.zip", 27 | "dependencies": { 28 | "jquery": ">=1.4" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /examples/example_page.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /MIT-LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Daniel Reznick, vedmack@gmail.com 2 | All rights reserved. 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining 5 | a copy of this software and associated documentation files (the 6 | "Software"), to deal in the Software without restriction, including 7 | without limitation the rights to use, copy, modify, merge, publish, 8 | distribute, sublicense, and/or sell copies of the Software, and to 9 | permit persons to whom the Software is furnished to do so, subject to 10 | the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 19 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /ChangeLog.markdown: -------------------------------------------------------------------------------- 1 | # jQuery Feedback Me Plugin ChangeLog 2 | 3 | ## 0.5.9 (still beta) 4 | 5 | * Closed issues https://github.com/vedmack/feedback_me/issues/40 6 | 7 | 8 | ## 0.5.8 9 | 10 | * Added custom template for the delayed_options, custom_html_success and custom_html_fail are part of the delayed_options, https://github.com/vedmack/feedback_me/issues/33 11 | * Added extend for the global ajax beforeSend, https://github.com/vedmack/feedback_me/issues/34 12 | 13 | 14 | ## 0.5.7 15 | 16 | * IE8 Support 17 | * Improved the Multiple feedbacks mode 18 | * Bugs fix 19 | 20 | 21 | ## 0.5.6 22 | 23 | * Customizable pattern attribute: "name_pattern" for name input (https://github.com/vedmack/feedback_me/issues/27) 24 | * Allow jQuery.noConflict (https://github.com/vedmack/feedback_me/issues/25) 25 | * Minor fix (https://github.com/vedmack/feedback_me/issues/26) 26 | 27 | ## 0.5.5 28 | 29 | * Added highly customizable notification response for success/fail feedback sending (https://github.com/vedmack/feedback_me/issues/21) 30 | * Bug fix (https://github.com/vedmack/feedback_me/issues/24) 31 | 32 | 33 | ## 0.5.2 34 | 35 | * Added support for multiple feedbacks on page (https://github.com/vedmack/feedback_me/issues/19) 36 | * As part of the multiple feedback feature implementation ALL ids were replaced with classes (e.g #feedback_content is now .feedback_content) 37 | * Bug fix 38 | 39 | 40 | ## 0.4.8 41 | 42 | * Added "custom_html" option to allow the use of inline html code that will be embeded inside feedback_me widget (thanks to miguelfontanes) 43 | * Added "show_form" option to allow the external iframe/inline html code to appear with the original form or replace it 44 | * CSS improvements colors / rounded coreners / trigger text location / etc... 45 | * API change , when using "iframe_url" you should fedback me form explicitly: set "show_form" to false (untill now the form was hidden implicitly by default) , same goes for "custom_html" 46 | 47 | 48 | ## 0.4.6 49 | 50 | * Remove jQuery UI dependencies (the only additional required library for the plugin is jQuery) https://github.com/vedmack/feedback_me/issues/14 51 | * Bug fix clear input fields upon sending feedback, https://github.com/vedmack/feedback_me/issues/15 52 | 53 | 54 | ## 0.4.4 55 | 56 | * Fixed jquery version check for using on instead of delegate (IE specific issue) 57 | 58 | 59 | ## 0.4.3 60 | 61 | * Using CSS3 Transition for sliding out the feedback widget - jquery UI ased as a fallback for older browsers (since this version jquery UI is not a must to include). 62 | 63 | 64 | ## 0.4.0 65 | 66 | * Added "iframe_url" option to allow the use of any html file that you want, html file will be embeded inside feedback_me widget. 67 | 68 | 69 | ## 0.3.8 70 | 71 | * Bug fix Prevent form submit when there is no use in required inputs, https://github.com/vedmack/feedback_me/issues/8 72 | 73 | 74 | ## 0.3.7 75 | 76 | * Added "custom_params" option to send additional data to the server (can be used for sending: csrf token / logged in user_name / etc`) 77 | 78 | 79 | ## 0.3.4 80 | 81 | * Added option to set the position of the feedback widget (4 possible locations) : left-top / left-bottom / right-top / right-bottom 82 | 83 | 84 | ## 0.3.2 85 | 86 | * Email input is now using HTML5 type + added simple email validation 87 | 88 | 89 | ## 0.3.1 90 | 91 | * Fixed IE8 style issues 92 | 93 | 94 | ## 0.3.0 95 | 96 | * Added optional radio button list input (5 radio buttons), can be used to rank something or any other purpose, all labels of the radios are customizable (string array) and it can be set to required too + its title is also customizable 97 | * Fixed several IE8 js issues 98 | 99 | 100 | ## 0.2.7 101 | 102 | * Added optional required attribute (HTML5) for all input fields with homegrown validation 103 | * Added optional asterisk next to label of required input fields 104 | 105 | 106 | ## 0.2.5 107 | 108 | * Added customizable placeholder (HTML5) for all input fields 109 | * Better font and appearance 110 | 111 | 112 | ## 0.2.1 113 | 114 | * Changed close_on_click_outisde option to be true by default 115 | * Bug fix 116 | 117 | 118 | ## 0.2.0 119 | 120 | * Added close_on_click_outisde option (default is false) 121 | * Several code enhancements 122 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | jQuery Feedback Me Plugin 2 | =========== 3 | 4 | Description: 5 | ===== 6 | 7 | This jQuery plug-in allows user to easily add an animatable UI widget with a feedback form which slides from the side of the screen. 8 | 9 | 10 | Contact/Social: 11 | ===== 12 | If you want to ask a question use my [google group](https://groups.google.com/forum/#!forum/daniels_code) 13 | 14 | If you like my plugin, you can show your appreciation by following me in [Twitter](https://twitter.com/danielreznick) / [GitHub](https://github.com/vedmack). 15 | 16 | 17 | Features: 18 | ===== 19 | 20 | - Using CSS3 Transition for sliding out the feedback widget (jquery used as a fallback for older browsers) 21 | - Multiple feedbacks on page (different locations) 22 | - Growl alike and super customizable dialog response for success/fail feedback sending 23 | - Highly customizable notification response for success/fail feedback sending 24 | - Bootstrap support 25 | - jQuery UI themes support 26 | - 4 different possible locations to place the widget 27 | - Using AJAX post to send data to server ('name', 'message' and 'email' parameters will be send to your servlet/php file etc...) 28 | - Ability to send additional custom params to server (csrf token / logged in user_name / etc`) 29 | - Ability to embed any external html file inside the feedback_me widget for maximum customization 30 | - Ability to embed inline html code inside the feedback_me widget for more customizations 31 | - RTL support (except in 0.5.2 version) 32 | - All labels are customizable 33 | - Customizable placeholder (HTML5) for all input fields 34 | - Optional required attribute (HTML5) for all input fields with homegrown validation 35 | - Optional asterisk next to label of required input fields 36 | - Optional pattern attribute (HTML5) for name input field with homegrown validation 37 | 38 | 39 | 40 | Examples: 41 | ===== 42 | 43 | [Clean example](http://feedback-me.appspot.com/example_clean.html) 44 | 45 | [Clean complex example](http://feedback-me.appspot.com/example_clean_complex.html) 46 | 47 | [jQuery UI theme aware example](http://feedback-me.appspot.com/example_jqueryUI.html) 48 | 49 | [Bootstrap example](http://feedback-me.appspot.com/example_bootstrap.html) 50 | 51 | [External IFrame Example](http://feedback-me.appspot.com/example_external_iframe.html) 52 | 53 | [Custom Html Example](http://feedback-me.appspot.com/example_custom_html.html) 54 | 55 | [Multiple Feedbacks Example](http://feedback-me.appspot.com/example_clean_multiple.html) 56 | 57 | Usage: 58 | ===== 59 | 60 | ```javascript 61 | $(document).ready(function(){ 62 | fm_options = { 63 | jQueryUI : true, 64 | position : "left-bottom", 65 | name_placeholder:"Name please", 66 | trigger_label : "Click me", 67 | message_required : true, 68 | show_asterisk_for_required : true, 69 | feedback_url : "send_feedback" 70 | }; 71 | 72 | fm.init(fm_options); 73 | }); 74 | ``` 75 | 76 | All available parameters + default settings (detailed explanation inside jquery.feedback_me.js) : 77 | 78 | ```javascript 79 | var default_options = { 80 | feedback_url : "", 81 | position : "left-top", 82 | jQueryUI : false, 83 | bootstrap : false, 84 | show_email : false, 85 | show_radio_button_list : false, 86 | close_on_click_outisde: true, 87 | name_label : "Name", 88 | email_label : "Email", 89 | message_label : "Message", 90 | radio_button_list_labels : ["1", "2", "3", "4", "5"], 91 | radio_button_list_title : "How would you rate my site?", 92 | name_placeholder : "", 93 | email_placeholder : "", 94 | message_placeholder : "", 95 | name_required : false, 96 | email_required : false, 97 | message_required : false, 98 | radio_button_list_required : false, 99 | show_asterisk_for_required : false, 100 | submit_label : "Send", 101 | title_label : "Feedback", 102 | trigger_label : "Feedback", 103 | custom_params : {}, 104 | iframe_url : undefined, 105 | show_form: true, 106 | custom_html: "", 107 | delayed_close : true, 108 | delayed_options : { 109 | delay_success_milliseconds : 2000, 110 | delay_fail_milliseconds : 2000, 111 | sending : "Sending...", 112 | send_fail : "Sending failed.", 113 | send_success : "Feedack sent.", 114 | fail_color : undefined, 115 | success_color : undefined, 116 | custom_html_success: undefined, 117 | custom_html_fail: undefined 118 | } 119 | }; 120 | ``` 121 | 122 | 123 | License: 124 | ===== 125 | 126 | Copyright (c) 2015 Daniel Reznick, released under the MIT license 127 | 128 | -------------------------------------------------------------------------------- /examples/example_send_feedback.php: -------------------------------------------------------------------------------- 1 | 20 | Optionally, either storing to a database or emailing can be disabled by setting the corresponding global variable - 'feedback_me_db' or 'feedback_me_mail' - to FALSE. 21 | ************************************************/ 22 | 23 | /*** 24 | error_reporting (-1); 25 | ini_set ('display_errors', 'On'); 26 | ***/ 27 | 28 | ini_set ('output_buffering', 'Off'); 29 | while (@ob_end_flush()); 30 | 31 | date_default_timezone_set ('UTC'); 32 | 33 | ini_set ('php.internal_encoding', 'UTF-8'); 34 | mb_internal_encoding ('UTF-8'); 35 | 36 | if (file_exists ('../private/fme_config.php')) 37 | { 38 | require_once ('../private/fme_config.php'); 39 | } else { Result ('error'); } 40 | 41 | if (file_exists ('swift/lib/swift_required.php')) 42 | { 43 | require_once ('swift/lib/swift_required.php'); 44 | } else { Result ('error'); } 45 | 46 | /***********************************************/ 47 | function Result ($sStatus) 48 | /***********************************************/ 49 | { 50 | /*** 51 | The success() function of jquery.feedback_me.js does not check 52 | if (data['error']) { } else { }. This is the reason we do not 53 | use the dataType:'json' response below, but instead use a 54 | response that will forcefully trigger the error() function if 55 | necessary. 56 | 57 | $arResponse[$sStatus] = TRUE; 58 | header ('Content-type: application/json'); 59 | print (json_encode ($arResponse)); 60 | ***/ 61 | 62 | switch ($sStatus) 63 | { 64 | case 'success': 65 | http_response_code (200); /*** 200 = OK ***/ 66 | break; 67 | case 'error': 68 | default: 69 | http_response_code (404); /*** 404 = Not Found ***/ 70 | break; 71 | } 72 | } 73 | /***********************************************/ 74 | function FeedbackDB ($arFeedback) 75 | /***********************************************/ 76 | { 77 | /*** Database. ***/ 78 | $GLOBALS['link'] = mysqli_init(); 79 | if ($GLOBALS['link'] == FALSE) { return (FALSE); } 80 | if (!@mysqli_real_connect ($GLOBALS['link'], $GLOBALS['db_host'], 81 | $GLOBALS['db_user'], $GLOBALS['db_pass'], $GLOBALS['db_dtbs'])) 82 | { return (FALSE); } 83 | mysqli_set_charset ($GLOBALS['link'], 'utf8'); 84 | 85 | /*** Create table. ***/ 86 | $query_table = "CREATE TABLE IF NOT EXISTS `feedback_me` ( 87 | `feedback_id` INT(10) UNIQUE NOT NULL AUTO_INCREMENT, 88 | `feedback_name` VARCHAR(255) NOT NULL, 89 | `feedback_email` VARCHAR(255) NOT NULL, 90 | `feedback_message` VARCHAR(255) NOT NULL, 91 | `feedback_website` VARCHAR(255) NOT NULL, 92 | `feedback_ip` VARCHAR(45) NOT NULL, 93 | `feedback_date` DATETIME NOT NULL, 94 | PRIMARY KEY (`feedback_id`));"; 95 | $result_table = mysqli_query ($GLOBALS['link'], $query_table); 96 | 97 | /*** Insert feedback. ***/ 98 | $query_feedback = "INSERT INTO `feedback_me` VALUES (NULL, '" . 99 | mysqli_real_escape_string ($GLOBALS['link'], 100 | $arFeedback['name']) . "', '" . 101 | mysqli_real_escape_string ($GLOBALS['link'], 102 | $arFeedback['email']) . "', '" . 103 | mysqli_real_escape_string ($GLOBALS['link'], 104 | $arFeedback['message']) . "', '" . 105 | $arFeedback['website'] . "', '" . 106 | $arFeedback['ip'] . "', '" . 107 | $arFeedback['datetime'] . "');"; 108 | $result_feedback = mysqli_query ($GLOBALS['link'], $query_feedback); 109 | 110 | if (mysqli_affected_rows ($GLOBALS['link']) == 1) 111 | { 112 | $bResult = TRUE; 113 | } else { 114 | $bResult = FALSE; 115 | } 116 | mysqli_close ($GLOBALS['link']); 117 | 118 | return ($bResult); 119 | } 120 | /***********************************************/ 121 | function FeedbackMail ($arFeedback) 122 | /***********************************************/ 123 | { 124 | /*** Subject. ***/ 125 | $sSubject = '[ ' . $arFeedback['website'] . ' ] Feedback'; 126 | 127 | /*** Message. ***/ 128 | $sMessage = ''; 129 | if (!empty ($arFeedback['name'])) 130 | { $sMessage .= 'Name: ' . $arFeedback['name'] . '
'; } 131 | if (!empty ($arFeedback['email'])) 132 | { $sMessage .= 'Email: ' . $arFeedback['email'] . '
'; } 133 | $sMessage .= 'IP: ' . $arFeedback['ip'] . '
'; 134 | $sMessage .= nl2br ($arFeedback['message']); 135 | 136 | $transport = Swift_SmtpTransport::newInstance( 137 | $GLOBALS['smtp_host'], 138 | $GLOBALS['smtp_port']) 139 | ->setUsername($GLOBALS['smtp_user']) 140 | ->setPassword($GLOBALS['smtp_pass']) 141 | ; 142 | $mailer = Swift_Mailer::newInstance($transport); 143 | 144 | $message = Swift_Message::newInstance() 145 | ->setSubject($sSubject) 146 | ->setFrom(array($GLOBALS['smtp_user'] => 'Feedback Me')) 147 | ->setTo($GLOBALS['mail_to']) 148 | ->setBody( 149 | '' . 150 | '' . 151 | '' . 152 | $sMessage . 153 | '' . 154 | '', 155 | 'text/html' 156 | ); 157 | ; 158 | 159 | try { 160 | $bResult = $mailer->send($message); 161 | } catch (Exception $e) { 162 | /*** 163 | file_put_contents ('../private/error_log.txt', 164 | $e->getMessage(), FILE_APPEND); 165 | ***/ 166 | $bResult = FALSE; 167 | } 168 | 169 | return ($bResult); 170 | } 171 | /***********************************************/ 172 | function GetIP () 173 | /***********************************************/ 174 | { 175 | $arServer = array ( 176 | 'HTTP_CLIENT_IP', 177 | 'HTTP_X_FORWARDED_FOR', 178 | 'HTTP_X_FORWARDED', 179 | 'HTTP_X_CLUSTER_CLIENT_IP', 180 | 'HTTP_FORWARDED_FOR', 181 | 'HTTP_FORWARDED', 182 | 'REMOTE_ADDR' 183 | ); 184 | foreach ($arServer as $sServer) 185 | { 186 | if (array_key_exists ($sServer, $_SERVER) === TRUE) 187 | { 188 | foreach (explode (',', $_SERVER[$sServer]) as $sIP) 189 | { 190 | if (filter_var ($sIP, FILTER_VALIDATE_IP) !== FALSE) 191 | { return ($sIP); } 192 | } 193 | } 194 | } 195 | return ('unknown'); 196 | } 197 | /***********************************************/ 198 | 199 | if (strtoupper ($_SERVER['REQUEST_METHOD']) === 'POST') 200 | { 201 | if ((isset ($_POST['name'])) && 202 | (isset ($_POST['email'])) && 203 | (isset ($_POST['message'])) && 204 | (!empty ($_POST['message']))) 205 | { 206 | $arFeedback = array(); 207 | $arFeedback['name'] = htmlspecialchars ($_POST['name'], ENT_QUOTES); 208 | $arFeedback['email'] = htmlspecialchars ($_POST['email'], ENT_QUOTES); 209 | $arFeedback['message'] = htmlspecialchars ($_POST['message'], ENT_QUOTES); 210 | $arFeedback['website'] = $GLOBALS['feedback_me_website_name']; 211 | $arFeedback['ip'] = GetIP(); 212 | $arFeedback['datetime'] = date ('Y-m-d H:i:s'); 213 | 214 | $bResultDB = TRUE; 215 | if ($GLOBALS['feedback_me_db'] == TRUE) 216 | { $bResultDB = FeedbackDB ($arFeedback); } 217 | 218 | $bResultMail = TRUE; 219 | if ($GLOBALS['feedback_me_mail'] == TRUE) 220 | { $bResultMail = FeedbackMail ($arFeedback); } 221 | 222 | if (($bResultDB != FALSE) && ($bResultMail != FALSE)) 223 | { 224 | Result ('success'); 225 | } else { 226 | Result ('error'); 227 | } 228 | } else { Result ('error'); } 229 | } else { Result ('error'); } 230 | ?> 231 | -------------------------------------------------------------------------------- /css/jquery.feedback_me.css: -------------------------------------------------------------------------------- 1 | .feedback_content, .feedback_trigger { 2 | font-size: 16px; 3 | font-family: 'Helvetica Neue', Helvetica, Tahoma, Arial, sans-serif; 4 | 5 | -webkit-transition: all 500ms ease-in-out; 6 | -moz-transition: all 500ms ease-in-out; 7 | -o-transition: all 500ms ease-in-out; 8 | transition: all 500ms ease-in-out; 9 | } 10 | 11 | .feedback_content ul { 12 | list-style: none; 13 | padding-left: 11px; 14 | } 15 | 16 | .feedback_trigger{ 17 | cursor: pointer; 18 | -webkit-border-top-left-radius: 0px; 19 | -moz-border-radius-topleft: 0px; 20 | border-top-left-radius: 0px; 21 | -webkit-border-bottom-left-radius: 0px; 22 | -moz-border-radius-bottomleft: 0px; 23 | border-bottom-left-radius: 0px; 24 | z-index: 2001; 25 | } 26 | 27 | .feedback_trigger.left-top, .feedback_trigger.left-bottom , .feedback_trigger.right-top, .feedback_trigger.right-bottom { 28 | width: 40px; 29 | height: 100px; 30 | } 31 | 32 | .feedback_trigger.bottom-left{ 33 | width: 100px; 34 | height: 40px; 35 | } 36 | 37 | .feedback_content { 38 | width: 380px; 39 | height: 300px; 40 | z-index: 2001; 41 | } 42 | 43 | .feedback_content.email_present { 44 | height: 360px; 45 | } 46 | 47 | .feedback_content ul li { 48 | margin-right: 20px; 49 | margin-bottom: 20px; 50 | } 51 | 52 | .feedback_content label { 53 | display: inline-block; 54 | } 55 | 56 | .feedback_name { 57 | display: block; 58 | width: 340px; 59 | } 60 | 61 | .feedback_email { 62 | display: block; 63 | width: 340px; 64 | } 65 | 66 | .feedback_message { 67 | display: block; 68 | width: 340px; 69 | resize: none; 70 | } 71 | 72 | .feedback_submit { 73 | float: right; 74 | } 75 | 76 | .feedback_trigger_text { 77 | white-space: nowrap; 78 | position: absolute; 79 | top: 40px; 80 | letter-spacing: 2px; 81 | font-size: 17px; 82 | } 83 | 84 | .left-top .feedback_trigger_text , .left-bottom .feedback_trigger_text , .right-top .feedback_trigger_text , .right-bottom .feedback_trigger_text { 85 | white-space: nowrap; 86 | position: absolute; 87 | top: 40px; 88 | letter-spacing: 2px; 89 | font-size: 17px; 90 | -webkit-transform: rotate(-90deg); 91 | -moz-transform: rotate(-90deg); 92 | -ms-transform: rotate(-90deg); 93 | -o-transform: rotate(-90deg); 94 | filter:none; 95 | } 96 | 97 | .fm_clean { 98 | background-color: #DDDDDD; 99 | } 100 | 101 | .fm_clean.feedback_trigger:hover { 102 | background-color: #CCCCCC; 103 | } 104 | 105 | .fm_clean button{ 106 | padding: 5px 10px; 107 | display: inline; 108 | background: #777; 109 | border: none; 110 | color: #fff; 111 | cursor: pointer; 112 | font-weight: bold; 113 | border-radius: 5px; 114 | -moz-border-radius: 5px; 115 | -webkit-border-radius: 5px; 116 | text-shadow: 1px 1px #666; 117 | } 118 | 119 | .fm_clean button:hover { 120 | background-color: #9C9A9A; 121 | } 122 | 123 | .fm_clean .feedback_trigger_text { 124 | right: -25px; 125 | } 126 | 127 | .fm_clean .feedback_title { 128 | padding-top: 5px; 129 | } 130 | 131 | .fm_clean .feedback_title span { 132 | margin-left: 10px; 133 | } 134 | 135 | .feedback_content.fm_clean.feedback_content_closed.left-top, .feedback_content.fm_clean.feedback_content_closed.left-bottom{ 136 | margin-left: -380px; 137 | } 138 | 139 | .feedback_content.fm_clean.left-top, .feedback_content.fm_clean.left-bottom{ 140 | margin-left: 0px; 141 | -webkit-border-bottom-right-radius: 6px; 142 | -moz-border-bottom-right-radius: 6px; 143 | border-bottom-right-radius: 6px; 144 | -webkit-border-top-right-radius: 6px; 145 | -moz-border-top-right-radius: 6px; 146 | border-top-right-radius: 6px; 147 | } 148 | 149 | .feedback_trigger.fm_clean.feedback_trigger_closed.left-top, .feedback_trigger.fm_clean.feedback_trigger_closed.left-bottom{ 150 | margin-left: 0px; 151 | } 152 | 153 | .feedback_trigger.fm_clean.left-top, .feedback_trigger.fm_clean.left-bottom{ 154 | margin-left: 380px; 155 | -webkit-border-bottom-right-radius: 6px; 156 | -moz-border-bottom-right-radius: 6px; 157 | border-bottom-right-radius: 6px; 158 | -webkit-border-top-right-radius: 6px; 159 | -moz-border-top-right-radius: 6px; 160 | border-top-right-radius: 6px; 161 | } 162 | 163 | .feedback_content.fm_jquery.feedback_content_closed.left-top, .feedback_content.fm_jquery.feedback_content_closed.left-bottom { 164 | margin-left: -382px; 165 | } 166 | 167 | .feedback_content.fm_jquery.left-top, .feedback_content.fm_jquery.left-bottom { 168 | margin-left: 0px; 169 | } 170 | 171 | .feedback_trigger.fm_jquery.feedback_trigger_closed.left-top, .feedback_trigger.fm_jquery.feedback_trigger_closed.left-bottom { 172 | margin-left: 0px; 173 | } 174 | 175 | .feedback_trigger.fm_jquery.left-top, .feedback_trigger.fm_jquery.left-bottom { 176 | margin-left: 382px; 177 | -webkit-border-bottom-right-radius: 6px; 178 | -moz-border-bottom-right-radius: 6px; 179 | border-bottom-right-radius: 6px; 180 | -webkit-border-top-right-radius: 6px; 181 | -moz-border-top-right-radius: 6px; 182 | border-top-right-radius: 6px; 183 | -webkit-border-bottom-left-radius: 0; 184 | -moz-border-bottom-left-radius: 0; 185 | border-bottom-left-radius: 0; 186 | -webkit-border-top-left-radius: 0; 187 | -moz-border-top-left-radius: 0; 188 | border-top-left-radius: 0; 189 | } 190 | 191 | .fm_jquery .feedback_trigger_text { 192 | right: -20px; 193 | } 194 | 195 | .fm_jquery.feedback_content { 196 | padding: 0px; 197 | width: 380px; 198 | height: 340px; 199 | } 200 | 201 | .fm_jquery.feedback_content.email_present { 202 | height: 390px; 203 | } 204 | 205 | 206 | .fm_jquery.feedback_trigger{ 207 | padding: 0px; 208 | border: 0px; 209 | } 210 | 211 | .feedback_content.fm_bootstrap.feedback_content_closed.left-top, .feedback_content.fm_bootstrap.feedback_content_closed.left-bottom { 212 | margin-left: -380px; 213 | } 214 | 215 | .feedback_content.fm_bootstrap.left-top, .feedback_content.fm_bootstrap.left-bottom { 216 | margin-left: 0px; 217 | } 218 | 219 | .feedback_trigger.fm_bootstrap.feedback_trigger_closed.left-top, .feedback_trigger.fm_bootstrap.feedback_trigger_closed.left-bottom { 220 | margin-left: 0px; 221 | } 222 | 223 | .feedback_trigger.fm_bootstrap.left-top, .feedback_trigger.fm_bootstrap.left-bottom { 224 | margin-left: 380px; 225 | } 226 | 227 | 228 | .fm_bootstrap .feedback_trigger_text { 229 | right: -25px; 230 | } 231 | 232 | .fm_bootstrap.hero-unit { 233 | padding:0px; 234 | } 235 | 236 | .fm_bootstrap.hero-unit ul { 237 | margin-left:15px; 238 | } 239 | 240 | .fm_bootstrap .feedback_title { 241 | margin-bottom: 10px; 242 | } 243 | 244 | .fm_bootstrap .feedback_title span { 245 | margin-left: 15px; 246 | } 247 | 248 | .fm_bootstrap .feedback_trigger_text { 249 | top: 35px; 250 | } 251 | 252 | .fm_bootstrap.feedback_content ul { 253 | padding: 0px; 254 | } 255 | 256 | .fm_bootstrap.feedback_content { 257 | height: 340px; 258 | } 259 | 260 | .fm_bootstrap.feedback_content.email_present { 261 | height: 430px; 262 | } 263 | 264 | .fm_bootstrap.feedback_trigger:hover{ 265 | background-color: #AAAAAA; 266 | } 267 | 268 | .required_asterisk { 269 | color: red; 270 | } 271 | 272 | .feedback_content.radio_button_list_present { 273 | height: 380px; 274 | } 275 | 276 | .feedback_content.email_present.radio_button_list_present { 277 | height: 440px; 278 | } 279 | 280 | .radio_button_wrapper{ 281 | display:inline-block; 282 | text-align:center; 283 | margin-right:35px; 284 | } 285 | .feedback_content .radio_button_wrapper label { 286 | display:block; 287 | } 288 | 289 | .radio_button_list_title_wrapper{ 290 | margin-bottom: 10px; 291 | } 292 | 293 | .fm_jquery.feedback_content.radio_button_list_present { 294 | height: 430px; 295 | } 296 | 297 | .fm_bootstrap.feedback_content.radio_button_list_present { 298 | height: 470px; 299 | } 300 | 301 | .fm_jquery.feedback_content.email_present.radio_button_list_present { 302 | height: 500px; 303 | } 304 | 305 | .fm_bootstrap.feedback_content.email_present.radio_button_list_present { 306 | height: 550px; 307 | } 308 | 309 | @media \0screen { 310 | .fm_clean .feedback_trigger_text, .fm_jquery .feedback_trigger_text, .fm_bootstrap .feedback_trigger_text { 311 | right:-70px; 312 | width:100px; 313 | margin-top:-45px; 314 | filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.00000000, M12=1.00000000, M21=-1.00000000, M22=0.00000000,sizingMethod='auto expand'); 315 | } 316 | .fm_bootstrap .feedback_trigger_text { 317 | right:-65px; 318 | margin-top:-40px; 319 | } 320 | } 321 | 322 | .feedback_trigger.left-top { 323 | position: fixed; 324 | top: 100px; 325 | left: 0; 326 | } 327 | 328 | .feedback_content.left-top { 329 | position: fixed; 330 | top: 50px; 331 | left: 0; 332 | } 333 | 334 | .feedback_trigger.left-bottom { 335 | position: fixed; 336 | bottom: 100px; 337 | left: 0; 338 | } 339 | 340 | .feedback_content.left-bottom { 341 | position: fixed; 342 | bottom: 50px; 343 | left: 0; 344 | } 345 | 346 | .feedback_trigger.right-top { 347 | position: fixed; 348 | top: 100px; 349 | right: 0; 350 | } 351 | 352 | .feedback_content.right-top { 353 | position: fixed; 354 | top: 50px; 355 | right: 0; 356 | } 357 | 358 | .feedback_trigger.right-bottom { 359 | position: fixed; 360 | bottom: 100px; 361 | right: 0; 362 | } 363 | 364 | .feedback_content.right-bottom { 365 | position: fixed; 366 | bottom: 50px; 367 | right: 0; 368 | } 369 | 370 | .feedback_content.fm_clean.feedback_content_closed.right-top, .feedback_content.fm_clean.feedback_content_closed.right-bottom{ 371 | margin-right: -380px; 372 | } 373 | 374 | .feedback_content.fm_clean.right-top, .feedback_content.fm_clean.right-bottom{ 375 | margin-right: 0px; 376 | -webkit-border-bottom-left-radius: 6px; 377 | -moz-border-bottom-left-radius: 6px; 378 | border-bottom-left-radius: 6px; 379 | -webkit-border-top-left-radius: 6px; 380 | -moz-border-top-left-radius: 6px; 381 | border-top-left-radius: 6px; 382 | } 383 | 384 | .feedback_content.fm_jquery.feedback_content_closed.right-top, .feedback_content.fm_jquery.feedback_content_closed.right-bottom { 385 | margin-right: -382px; 386 | } 387 | 388 | .feedback_content.fm_jquery.right-top, .feedback_content.fm_jquery.right-bottom { 389 | margin-right: 0px; 390 | } 391 | 392 | .feedback_content.fm_bootstrap.feedback_content_closed.right-top, .feedback_content.fm_bootstrap.feedback_content_closed.right-bottom { 393 | margin-right: -380px; 394 | } 395 | 396 | .feedback_content.fm_bootstrap.right-top, .feedback_content.fm_bootstrap.right-bottom { 397 | margin-right: 0px; 398 | 399 | -webkit-border-bottom-left-radius: 6px; 400 | -moz-border-bottom-left-radius: 6px; 401 | border-bottom-left-radius: 6px; 402 | 403 | -webkit-border-top-left-radius: 6px; 404 | -moz-border-top-left-radius: 6px; 405 | border-top-left-radius: 6px; 406 | 407 | -webkit-border-bottom-right-radius: 0; 408 | -moz-border-bottom-right-radius: 0; 409 | border-bottom-right-radius: 0; 410 | 411 | -webkit-border-top-right-radius: 0; 412 | -moz-border-top-right-radius: 0; 413 | border-top-right-radius: 0; 414 | } 415 | 416 | 417 | .feedback_trigger.fm_clean.feedback_trigger_closed.right-top, .feedback_trigger.fm_clean.feedback_trigger_closed.right-bottom{ 418 | margin-right: 0px; 419 | } 420 | 421 | .feedback_trigger.fm_clean.right-top, .feedback_trigger.fm_clean.right-bottom{ 422 | margin-right: 380px; 423 | -webkit-border-bottom-left-radius: 6px; 424 | -moz-border-bottom-left-radius: 6px; 425 | border-bottom-left-radius: 6px; 426 | -webkit-border-top-left-radius: 6px; 427 | -moz-border-top-left-radius: 6px; 428 | border-top-left-radius: 6px; 429 | } 430 | 431 | .feedback_trigger.fm_jquery.feedback_trigger_closed.right-top, .feedback_trigger.fm_jquery.feedback_trigger_closed.right-bottom { 432 | margin-right: 0px; 433 | } 434 | 435 | .feedback_trigger.fm_jquery.right-top, .feedback_trigger.fm_jquery.right-bottom { 436 | margin-right: 382px; 437 | -webkit-border-bottom-right-radius: 0; 438 | -moz-border-bottom-right-radius: 0; 439 | border-bottom-right-radius: 0; 440 | -webkit-border-top-right-radius: 0; 441 | -moz-border-top-right-radius: 0; 442 | border-top-right-radius: 0; 443 | -webkit-border-bottom-left-radius: 6px; 444 | -moz-border-bottom-left-radius: 6px; 445 | border-bottom-left-radius: 6px; 446 | -webkit-border-top-left-radius: 6px; 447 | -moz-border-top-left-radius: 6px; 448 | border-top-left-radius: 6px; 449 | } 450 | 451 | .feedback_trigger.fm_bootstrap.feedback_trigger_closed.right-top, .feedback_trigger.fm_bootstrap.feedback_trigger_closed.right-bottom { 452 | margin-right: 0px; 453 | } 454 | 455 | .feedback_trigger.fm_bootstrap.right-top, .feedback_trigger.fm_bootstrap.right-bottom { 456 | margin-right: 380px; 457 | 458 | -webkit-border-bottom-left-radius: 6px; 459 | -moz-border-bottom-left-radius: 6px; 460 | border-bottom-left-radius: 6px; 461 | 462 | -webkit-border-top-left-radius: 6px; 463 | -moz-border-top-left-radius: 6px; 464 | border-top-left-radius: 6px; 465 | 466 | -webkit-border-bottom-right-radius: 0; 467 | -moz-border-bottom-right-radius: 0; 468 | border-bottom-right-radius: 0; 469 | 470 | -webkit-border-top-right-radius: 0; 471 | -moz-border-top-right-radius: 0; 472 | border-top-right-radius: 0; 473 | } 474 | 475 | .feedback_me_frame { 476 | border: none; 477 | overflow: auto; 478 | height: 90%; 479 | width: 98%; 480 | } 481 | 482 | .feedback-delayed-dlg { 483 | position: fixed; 484 | width: 250px; 485 | height: 100px; 486 | top: 50%; 487 | left: 50%; 488 | vertical-align: middle; 489 | text-align: center; 490 | margin-left: -125px; 491 | margin-top: -50px; 492 | -webkit-border-radius: 6px; 493 | -moz-border-radius: 6px; 494 | border-radius: 6px; 495 | } 496 | 497 | .feedback-delayed-dlg.success { 498 | background-color: #2ECC40; 499 | } 500 | 501 | .feedback-delayed-dlg.fail { 502 | background-color: #FF4136; 503 | } 504 | 505 | 506 | .feedback-dlg-close { 507 | position: absolute; 508 | right: 0; 509 | padding: 1px; 510 | background: #FFFFFF; 511 | cursor: pointer; 512 | } 513 | 514 | .feedback-success-fail-message-inner { 515 | position: fixed; 516 | width: 200px; 517 | height: 50px; 518 | background-color: #FFFFFF; 519 | top: 50%; 520 | left: 50%; 521 | vertical-align: middle; 522 | text-align: center; 523 | margin-left: -100px; 524 | margin-top: -25px; 525 | -webkit-border-radius: 6px; 526 | -moz-border-radius: 6px; 527 | border-radius: 6px; 528 | line-height:50px; 529 | } 530 | 531 | .feedback-success-fail-message-inner > span{ 532 | display: inline-block; 533 | vertical-align: middle; 534 | line-height: 1em; 535 | } 536 | 537 | .feedback-success-message, .feedback-fail-message { 538 | display: inline-block; 539 | width: 200px; 540 | height: 50px; 541 | } 542 | -------------------------------------------------------------------------------- /js/jquery.feedback_me.js: -------------------------------------------------------------------------------- 1 | /*global $, jQuery*/ 2 | /*jslint plusplus: true*/ 3 | 4 | /*! 5 | * jQuery Feedback Me Plugin 6 | * 7 | * File: jquery.feedback_me.js 8 | * Version: 0.5.9.beta.1 (grab latest stable from https://github.com/vedmack/feedback_me/releases) 9 | * 10 | * Author: Daniel Reznick 11 | * Info: https://github.com/vedmack/feedback_me 12 | * Contact: vedmack@gmail.com 13 | * Twitter: @danielreznick 14 | * Q&A: https://groups.google.com/forum/#!forum/daniels_code 15 | * 16 | * Copyright (c) 2014 Daniel Reznick, all rights reserved. released under the MIT license 17 | * 18 | * This source file is distributed in the hope that it will be useful, but 19 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 20 | * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details. 21 | */ 22 | /* 23 | * Parameters: 24 | * 25 | * 26 | * ------------- 27 | 28 | * feedback_url 29 | Required: true 30 | Type: String 31 | Description: URL of your servlet/php etc ('name', 'message' and 'email' parameters will be send to your servlet/php etc...) 32 | 33 | * position 34 | Required: false 35 | Type: String 36 | Default value: left-top 37 | Possible values: left-top / left-bottom / right-top / right-bottom 38 | Description: Set the position where the feedback widget will be located 39 | * jQueryUI 40 | Required: false 41 | Type: boolean 42 | Default value: false 43 | Description: Tell the plugin to use jQuery UI theme 44 | 45 | * bootstrap 46 | Required: false 47 | Type: boolean 48 | Default value: false 49 | Description: Tell the plugin to use twitter bootstrap 50 | 51 | * show_email 52 | Required: false 53 | Type: boolean 54 | Default value: false 55 | Description: Tell the plugin to display email input field 56 | 57 | * show_radio_button_list 58 | Required: false 59 | Type: boolean 60 | Default value: false 61 | Description: Tell the plugin to set of 5 radio buttons 62 | 63 | * name_label 64 | Required: false 65 | Type: String 66 | Default value: "Name" 67 | Description: Label for name input 68 | 69 | * email_label 70 | Required: false 71 | Type: String 72 | Default value: "Email" 73 | Description: Label for email input 74 | 75 | * message_label 76 | Required: false 77 | Type: String 78 | Default value: "Message" 79 | Description: Label for message input 80 | 81 | * radio_button_list_labels 82 | Required: false 83 | Type: Array of 5 strings 84 | Default value: ["1","2","3","4","5"] 85 | Description: Labels for radio button list 86 | 87 | * radio_button_list_title 88 | Required: false 89 | Type: String 90 | Default value: "How would you rate my site?" 91 | Description: Label that will appear above the list of radio button 92 | 93 | * submit_label 94 | Required: false 95 | Type: String 96 | Default value: "Send" 97 | Description: Label for submit input 98 | 99 | * title_label 100 | Required: false 101 | Type: String 102 | Default value: "Feedback" 103 | Description: Label for title text 104 | 105 | * trigger_label 106 | Required: false 107 | Type: String 108 | Default value: "Feedback" 109 | Description: Label for open/close (trigger) button 110 | 111 | * name_placeholder 112 | Required: false 113 | Type: String 114 | Default value: "" 115 | Description: Watermark for name input 116 | 117 | * email_placeholder 118 | Required: false 119 | Type: String 120 | Default value: "" 121 | Description: Watermark for email input 122 | 123 | * message_placeholder 124 | Required: false 125 | Type: String 126 | Default value: "" 127 | Description: Watermark for message input 128 | 129 | * name_pattern 130 | Required: false 131 | Type: String 132 | Default value: "" 133 | Description: Set name input pattern, you must escape your '\' chars (\ ---> \\) 134 | 135 | * name_required 136 | Required: false 137 | Type: boolean 138 | Default value: false 139 | Description: Makes input required 140 | 141 | * email_required 142 | Required: false 143 | Type: boolean 144 | Default value: false 145 | Description: Makes input required 146 | 147 | * message_required 148 | Required: false 149 | Type: boolean 150 | Default value: false 151 | Description: Makes input required 152 | 153 | * radio_button_list_required 154 | Required: false 155 | Type: boolean 156 | Default value: false 157 | Description: Makes radio inputs required 158 | 159 | * show_asterisk_for_required 160 | Required: false 161 | Type: boolean 162 | Default value: false 163 | Description: Add an asterisk to the label of the required inputs 164 | 165 | * close_on_click_outside 166 | Required: false 167 | Type: boolean 168 | Default value: true 169 | Description: Will cause the feedback dialog to be closed on clicking anywhere outside the dialog 170 | 171 | * custom_params 172 | Required: false 173 | Type: object 174 | Default value: {} 175 | Description: Use it if you want to send additional data to the server (can be used for sending: csrf token / logged in user_name / etc`) 176 | * iframe_url 177 | Required: false 178 | Type: String 179 | Default value: undefined 180 | Description: Allows you to use any html file that you want, it will be placed inside feedback_me widget, (note that in order to close the feedback_me widget 181 | just call the following command: parent.fm.triggerAction(event, "left-top"); don't forget to pass the "event" from you onclick call to the triggerAction function 182 | and also the position of your feedback widget left-top / left-bottom / right-top / right-bottom) 183 | * show_form 184 | Required: false 185 | Type: boolean 186 | Default value: true 187 | Description: Allows you to hide the form in the widget (and only show HTML code or iframe) 188 | * custom_html 189 | Required: false 190 | Type: String 191 | Default value: "" 192 | Description: Allows you to use any inline html code that you want, it will be placed inside feedback_me widget 193 | * delayed_close 194 | Required: false 195 | Type: boolean 196 | Default value: true 197 | Description: Enable feedback dialog upon feedback sending, a small dialog will be displayed with appropriate message in the middle 198 | of the screen and then fade out (read more about the delayed_options property) 199 | * delayed_options 200 | Required: false 201 | Type: object 202 | Default value: 203 | { 204 | delay_success_milliseconds : 2000, 205 | delay_fail_milliseconds : 2000, 206 | sending : "Sending...", //This text will appear on the "send" button while sending 207 | send_fail : "Sending failed.", //This text will appear on the fail dialog 208 | send_success : "Feedback sent.", //This text will appear on the success dialog 209 | fail_color : undefined, 210 | success_color : undefined, 211 | custom_html_success: undefined, //Allow to customize delayed success feedback with custom html code, note that the html will be wrapped with div with the following classes feedback-delayed-custom-dlg success 212 | custom_html_fail: undefined, //Allow to customize delayed fail feedback with custom html code, note that the html will be wrapped with div with the following classes feedback-delayed-custom-dlg fail 213 | } 214 | Description: Allow to customize the feedback dialog upon feedback sending 215 | * 216 | * 217 | */ 218 | var fm = (function ($) { 219 | 220 | 'use strict'; 221 | 222 | var fm_options_arr = {}, 223 | supportsTransitions = false; 224 | 225 | function eventTargetFixUp(pEvent) { 226 | if (pEvent.target === undefined) { 227 | pEvent.target = pEvent.srcElement; 228 | } 229 | return pEvent; 230 | } 231 | 232 | function getFmOptions(event, position) { 233 | var className, 234 | selector; 235 | event = eventTargetFixUp(event); 236 | if ($(event.target).closest(".feedback_trigger").length === 1) { 237 | className = $(event.target).closest(".feedback_trigger")[0].className; 238 | } else if ($(event.target).closest(".feedback_content").length === 1) { 239 | className = $(event.target).closest(".feedback_content")[0].className; 240 | } else { 241 | if (position === undefined) { 242 | position = 'left-top'; 243 | } 244 | className = position; 245 | } 246 | 247 | if (className.indexOf('left-top') !== -1) { 248 | selector = 'left-top'; 249 | } else if (className.indexOf('left-bottom') !== -1) { 250 | selector = 'left-bottom'; 251 | } else if (className.indexOf('right-top') !== -1) { 252 | selector = 'right-top'; 253 | } else if (className.indexOf('right-bottom') !== -1) { 254 | selector = 'right-bottom'; 255 | } 256 | return fm_options_arr[selector]; 257 | } 258 | 259 | function triggerAction(event, position) { 260 | 261 | var animation_show = {}, 262 | animation_hide = {}, 263 | $fm_trigger, 264 | $fm_content; 265 | 266 | event = eventTargetFixUp(event); 267 | animation_show.marginLeft = "+=380px"; 268 | animation_hide.marginLeft = "-=380px"; 269 | 270 | if (fm.getFmOptions(event, position).position.indexOf("right-") !== -1) { 271 | animation_show.marginRight = "+=380px"; 272 | animation_hide.marginRight = "-=380px"; 273 | } 274 | 275 | $fm_trigger = $(event.target).closest(".feedback_trigger"); 276 | if ($fm_trigger.length === 1) { 277 | $fm_content = $fm_trigger.next(); 278 | } else { 279 | $fm_content = $(event.target).closest(".feedback_content"); 280 | $fm_trigger = $fm_content.prev(); 281 | } 282 | if ($fm_content.length === 0 || $fm_trigger.length === 0) { 283 | if (position === undefined) { 284 | position = 'left-top'; 285 | } 286 | $fm_content = $('.' + position).closest(".feedback_content"); 287 | $fm_trigger = $fm_content.prev(); 288 | } 289 | 290 | if ($fm_trigger.hasClass("feedback_trigger_closed")) { 291 | if (supportsTransitions === true) { 292 | $fm_trigger.removeClass("feedback_trigger_closed"); 293 | $fm_content.removeClass("feedback_content_closed"); 294 | } else { 295 | $fm_trigger.add($fm_content).animate( 296 | animation_show, 297 | 150, 298 | function () { 299 | $fm_trigger.removeClass("feedback_trigger_closed"); 300 | $fm_content.removeClass("feedback_content_closed"); 301 | } 302 | ); 303 | } 304 | } else { 305 | //first add the closed class so double (which will trigger closeFeedback function) click wont try to hide the form twice 306 | $fm_trigger.addClass("feedback_trigger_closed"); 307 | $fm_content.addClass("feedback_content_closed"); 308 | if (supportsTransitions === false) { 309 | $fm_trigger.add($fm_content).animate( 310 | animation_hide, 311 | 150 312 | ); 313 | } 314 | } 315 | } 316 | 317 | function closeFeedback(event) { 318 | event = eventTargetFixUp(event); 319 | if (($(".feedback_content").length === 1 && $(".feedback_content").hasClass("feedback_content_closed")) || 320 | $(event.target).closest('.feedback_content').length === 1) { 321 | return; 322 | } 323 | 324 | var animation_hide = {}, 325 | option, 326 | $fm_trigger, 327 | $fm_content; 328 | 329 | for (option in fm_options_arr) { 330 | if (fm_options_arr.hasOwnProperty(option)) { 331 | 332 | $fm_content = $('.' + option).closest(".feedback_content"); 333 | $fm_trigger = $fm_content.prev(); 334 | 335 | if (!$fm_trigger.hasClass("feedback_trigger_closed")) { 336 | if (supportsTransitions === true) { 337 | $fm_trigger.addClass("feedback_trigger_closed"); 338 | $fm_content.addClass("feedback_content_closed"); 339 | } else { 340 | if (option.indexOf("left-") !== -1) { 341 | animation_hide.marginLeft = "-=380px"; 342 | } else { 343 | animation_hide.marginRight = "-=380px"; 344 | } 345 | if (!$fm_trigger.hasClass("feedback_kurama")) { 346 | $fm_trigger.addClass("feedback_kurama"); 347 | $fm_trigger.add($fm_content).animate( 348 | animation_hide, 349 | 150, 350 | function () { 351 | var $fm_trigger, 352 | $fm_content; 353 | $fm_trigger = $(this); 354 | $fm_content = $fm_trigger.next(); 355 | $fm_trigger.removeClass("feedback_kurama"); 356 | $fm_trigger.addClass("feedback_trigger_closed"); 357 | $fm_content.addClass("feedback_content_closed"); 358 | } 359 | ); 360 | } 361 | } 362 | } 363 | } 364 | } 365 | } 366 | 367 | function emailValid(str) { 368 | var lastAtPos = str.lastIndexOf('@'); 369 | return (lastAtPos < (str.length - 1) && lastAtPos > 0 && str.indexOf('@@') === -1 && str.length > 2); 370 | } 371 | 372 | function validateFeedbackForm(event, position) { 373 | event = eventTargetFixUp(event); 374 | var $fm_content = $(event.target).closest(".feedback_content"), 375 | fm_options = getFmOptions(event, position); 376 | if ((fm_options.name_required === true && $fm_content.find(".feedback_name").val() === "") || 377 | ((fm_options.email_required === true && $fm_content.find(".feedback_email").val() === "") || (fm_options.email_required === true && emailValid($fm_content.find(".feedback_email").val()) === false)) || 378 | (fm_options.message_required === true && $fm_content.find(".feedback_message").val() === "") || 379 | (fm_options.radio_button_list_required === true && $fm_content.find("input[name=feedback_radio]:checked").val() === undefined)) { 380 | return false; 381 | } 382 | return true; 383 | 384 | } 385 | 386 | function checkPatternFieldsOk(event, position) { 387 | var $patternFields = $("." + position + " [pattern]"), 388 | form_valid = true, 389 | i; 390 | 391 | if ($patternFields.length > 0) { 392 | for (i = 0; i < $patternFields.length; i++) { 393 | form_valid = !$patternFields[i].validity.patternMismatch; 394 | if (form_valid === false) { 395 | break; 396 | } 397 | } 398 | } 399 | return form_valid; 400 | } 401 | 402 | function checkRequiredFieldsOk(event, position) { 403 | var $reqFields = $("." + position + " [required]"), 404 | form_valid = true; 405 | 406 | if ($reqFields.length > 0) { 407 | form_valid = validateFeedbackForm(event, position); 408 | } 409 | return form_valid; 410 | } 411 | 412 | function applyCloseOnClickOutside() { 413 | var jqVersion = $().jquery.split("."); 414 | jqVersion[0] = +jqVersion[0]; 415 | jqVersion[1] = +jqVersion[1]; 416 | if (jqVersion[0] > 1 || (jqVersion[0] === 1 && jqVersion[1] >= 7)) { 417 | $(document).on("click", document, function (event) { 418 | closeFeedback(event); 419 | }); 420 | } else { 421 | $(document).delegate("body", document, function (event) { 422 | closeFeedback(event); 423 | }); 424 | } 425 | } 426 | 427 | function appendFeedbackToBody(fm_options) { 428 | var form_html = "", 429 | iframe_html = "", 430 | jQueryUIClasses1 = "", 431 | jQueryUIClasses2 = "", 432 | jQueryUIClasses3 = "", 433 | jQueryUIClasses4 = "", 434 | email_html = "", 435 | email_feedback_content_class = "", 436 | radio_button_list_html = "", 437 | radio_button_list_class = "", 438 | fm_class = " fm_clean ", 439 | jquery_class = "", 440 | bootstrap_class = "", 441 | bootstrap_btn = "", 442 | bootstrap_hero_unit = "", 443 | 444 | name_pattern = fm_options.name_pattern === "" ? "" : "pattern=\"" + fm_options.name_pattern + "\"", 445 | 446 | name_required = fm_options.name_required ? "required" : "", 447 | email_required = fm_options.email_required ? "required" : "", 448 | message_required = fm_options.message_required ? "required" : "", 449 | radio_button_list_required = fm_options.radio_button_list_required ? "required" : "", 450 | 451 | name_asterisk = fm_options.name_required && fm_options.show_asterisk_for_required ? "*" : "", 452 | email_asterisk = fm_options.email_required && fm_options.show_asterisk_for_required ? "*" : "", 453 | message_asterisk = fm_options.message_required && fm_options.show_asterisk_for_required ? "*" : "", 454 | radio_button_list_asterisk = fm_options.radio_button_list_required && fm_options.show_asterisk_for_required ? "*" : ""; 455 | 456 | if (fm_options.bootstrap === true) { 457 | bootstrap_class = " fm_bootstrap "; 458 | bootstrap_btn = " btn btn-primary "; 459 | bootstrap_hero_unit = " hero-unit jumbotron "; 460 | 461 | fm_class = ""; 462 | jquery_class = ""; 463 | } 464 | 465 | if (fm_options.jQueryUI === true) { 466 | jquery_class = " fm_jquery "; 467 | jQueryUIClasses1 = " ui-widget-header ui-corner-all ui-helper-clearfix "; 468 | jQueryUIClasses2 = " ui-dialog ui-widget ui-widget-content ui-corner-all "; 469 | jQueryUIClasses3 = " ui-dialog-titlebar "; 470 | jQueryUIClasses4 = " ui-dialog-title "; 471 | 472 | fm_class = ""; 473 | bootstrap_class = ""; 474 | bootstrap_hero_unit = ""; 475 | bootstrap_btn = ""; 476 | 477 | } 478 | 479 | if (fm_options.show_radio_button_list === true) { 480 | radio_button_list_html = "
  • " + fm_options.radio_button_list_title + radio_button_list_asterisk + "
    "; 481 | radio_button_list_html += "
    "; 482 | radio_button_list_html += " "; 483 | radio_button_list_html += "
  • "; 502 | 503 | radio_button_list_class = " radio_button_list_present"; 504 | } 505 | 506 | if (fm_options.show_email === true) { 507 | email_html = '
  • ' + email_asterisk + '
  • '; 508 | email_feedback_content_class = " email_present"; 509 | } 510 | 511 | if (fm_options.show_form === true) { 512 | form_html = '
    ' 513 | + '' 524 | + '
    '; 525 | } 526 | if (fm_options.iframe_url !== undefined) { 527 | iframe_html = ''; 528 | } 529 | 530 | $('body').append('
    ' 531 | + '' + fm_options.trigger_label 532 | + '
    '); 533 | 534 | $('body').append('
    ' 535 | + '
    ' 536 | + '' + fm_options.title_label + '' 537 | + '
    ' 538 | + form_html 539 | + iframe_html 540 | + fm_options.custom_html 541 | + '
    '); 542 | 543 | if (fm_options.jQueryUI === true) { 544 | $('.feedback_submit').button({ 545 | icons: { 546 | primary: 'ui-icon-mail-closed' 547 | } 548 | }); 549 | } 550 | 551 | if (fm_options.close_on_click_outside === true) { 552 | applyCloseOnClickOutside(); 553 | } 554 | 555 | //prevent form submit (needed for validation) 556 | $('.feedback_me_form').submit(function (event) { 557 | event.preventDefault(); 558 | }); 559 | 560 | } 561 | 562 | function stopPropagation(evt) { 563 | if (evt.stopPropagation !== undefined) { 564 | evt.stopPropagation(); 565 | } else { 566 | evt.cancelBubble = true; 567 | } 568 | } 569 | 570 | function slideBack(fm_options, $fm_trigger, $fm_content) { 571 | var animation_hide = {}; 572 | animation_hide.marginLeft = "-=380px"; 573 | if (fm_options.position.indexOf("right-") !== -1) { 574 | animation_hide.marginRight = "-=380px"; 575 | } 576 | 577 | if (supportsTransitions === true) { 578 | $fm_trigger.addClass("feedback_trigger_closed"); 579 | $fm_content.addClass("feedback_content_closed"); 580 | } else { 581 | $fm_trigger.add($fm_content).animate( 582 | animation_hide, 583 | 150, 584 | function () { 585 | $fm_trigger.addClass("feedback_trigger_closed"); 586 | } 587 | ); 588 | } 589 | } 590 | function clearInputs(event) { 591 | event = eventTargetFixUp(event); 592 | var $fm_content = $(event.target).closest(".feedback_content"); 593 | 594 | $fm_content.find(".feedback_name").val(""); 595 | $fm_content.find(".feedback_message").val(""); 596 | $fm_content.find(".feedback_email").val(""); 597 | $fm_content.find(".feedback_me_form input[name=feedback_radio]").prop('checked', false); 598 | } 599 | 600 | function sendFeedback(event, position) { 601 | var checkValid = checkRequiredFieldsOk(event, position), 602 | checkPattern = checkPatternFieldsOk(event, position), 603 | dataArray, 604 | $fm_trigger, 605 | $fm_content, 606 | fm_options = getFmOptions(event, position); 607 | 608 | event = eventTargetFixUp(event); 609 | if (checkValid === false || checkPattern === false) { 610 | stopPropagation(event); 611 | return; 612 | } 613 | 614 | $fm_content = $(event.target).closest(".feedback_content"); 615 | $fm_trigger = $(event.target).closest(".feedback_content").prev(); 616 | 617 | if (fm_options.delayed_close === true) { 618 | $fm_content.find('.feedback_submit').text(fm_options.delayed_options.sending); 619 | } 620 | 621 | dataArray = { 622 | name: $fm_content.find(".feedback_name").val(), 623 | message: $fm_content.find(".feedback_message").val(), 624 | email: $fm_content.find(".feedback_email").val(), 625 | radio_list_value: $fm_content.find(".feedback_me_form input[name=feedback_radio]:checked").val() 626 | }; 627 | 628 | dataArray = $.extend(fm_options.custom_params, dataArray); 629 | 630 | $.ajax({ 631 | type: 'POST', 632 | url: fm_options.feedback_url, 633 | data: dataArray, 634 | beforeSend: function (xhr, settings) { 635 | if ($.ajaxSettings.hasOwnProperty('beforeSend')) { 636 | $.ajaxSettings.beforeSend(xhr, settings); 637 | } 638 | if (fm_options.delayed_close === false) { 639 | slideBack(fm_options, $fm_trigger, $fm_content); 640 | } 641 | }, 642 | success: function (data) { 643 | var st = ""; 644 | fm.clearInputs(event); 645 | if (fm_options.delayed_close === true) { 646 | if (fm_options.delayed_options.custom_html_success === undefined) { 647 | if (fm_options.delayed_options.success_color !== undefined) { 648 | st = ' style="background-color:' + fm_options.delayed_options.success_color + '" '; 649 | } 650 | $fm_content.find('.feedback_submit').text(fm_options.submit_label); 651 | slideBack(fm_options, $fm_trigger, $fm_content); 652 | $("body").append('
    X' + 653 | '
    '); 654 | setTimeout(function () {$(".feedback-delayed-dlg").fadeOut(function () { $(this).remove(); }); }, fm_options.delayed_options.delay_success_milliseconds); 655 | } else { 656 | $fm_content.find('.feedback_submit').text(fm_options.submit_label); 657 | slideBack(fm_options, $fm_trigger, $fm_content); 658 | $("body").append('
    ' + fm_options.delayed_options.custom_html_success + '
    '); 659 | setTimeout(function () {$(".feedback-delayed-custom-dlg").fadeOut(function () { $(this).remove(); }); }, fm_options.delayed_options.delay_success_milliseconds); 660 | } 661 | } 662 | }, 663 | error: function (ob, errStr) { 664 | var st = ""; 665 | if (fm_options.delayed_close === true) { 666 | if (fm_options.delayed_options.custom_html_fail === undefined) { 667 | if (fm_options.delayed_options.fail_color !== undefined) { 668 | st = ' style="background-color:' + fm_options.delayed_options.fail_color + '" '; 669 | } 670 | $fm_content.find('.feedback_submit').text(fm_options.submit_label); 671 | $("body").append('
    X' + 672 | '
    '); 673 | setTimeout(function () {$(".feedback-delayed-dlg").fadeOut(function () { $(this).remove(); }); }, fm_options.delayed_options.delay_fail_milliseconds); 674 | } else { 675 | $fm_content.find('.feedback_submit').text(fm_options.submit_label); 676 | $("body").append('
    ' + fm_options.delayed_options.custom_html_fail + '
    '); 677 | setTimeout(function () {$(".feedback-delayed-custom-dlg").fadeOut(function () { $(this).remove(); }); }, fm_options.delayed_options.delay_fail_milliseconds); 678 | } 679 | } else { 680 | console.log("Failed to send feedback (please double check your feedback_url parameter)"); 681 | } 682 | } 683 | }); 684 | } 685 | 686 | function closeFeedbackDelayedDlg() { 687 | $(".feedback-delayed-dlg").fadeOut(); 688 | } 689 | 690 | function detectTransitionSupport() { 691 | var be = document.body || document.documentElement, 692 | style = be.style, 693 | p = 'transition', 694 | vendors, 695 | i; 696 | if (typeof style[p] === 'string') { 697 | supportsTransitions = true; 698 | return; 699 | } 700 | 701 | vendors = ['Moz', 'Webkit', 'Khtml', 'O', 'ms']; 702 | p = p.charAt(0).toUpperCase() + p.substr(1); 703 | for (i = 0; i < vendors.length; i++) { 704 | if (typeof style[vendors[i] + p] === 'string') { 705 | supportsTransitions = true; 706 | return; 707 | } 708 | } 709 | supportsTransitions = false; 710 | return; 711 | } 712 | 713 | function init(options) { 714 | 715 | var default_options = { 716 | feedback_url : "", 717 | position : "left-top", 718 | jQueryUI : false, 719 | bootstrap : false, 720 | show_email : false, 721 | show_radio_button_list : false, 722 | close_on_click_outside: true, 723 | name_label : "Name", 724 | email_label : "Email", 725 | message_label : "Message", 726 | radio_button_list_labels : ["1", "2", "3", "4", "5"], 727 | radio_button_list_title : "How would you rate my site?", 728 | name_placeholder : "", 729 | email_placeholder : "", 730 | message_placeholder : "", 731 | name_pattern : "", 732 | name_required : false, 733 | email_required : false, 734 | message_required : false, 735 | radio_button_list_required : false, 736 | show_asterisk_for_required : false, 737 | submit_label : "Send", 738 | title_label : "Feedback", 739 | trigger_label : "Feedback", 740 | custom_params : {}, 741 | iframe_url : undefined, 742 | show_form: true, 743 | custom_html : "", 744 | delayed_close : true, 745 | delayed_options : { 746 | delay_success_milliseconds : 2000, 747 | delay_fail_milliseconds : 2000, 748 | sending : "Sending...", 749 | send_fail : "Sending failed.", 750 | send_success : "Feedback sent.", 751 | fail_color : undefined, 752 | success_color : undefined, 753 | custom_html_success: undefined, 754 | custom_html_fail: undefined 755 | } 756 | }, 757 | tmp_options, 758 | tmp_delayed_options; 759 | 760 | tmp_delayed_options = $.extend(default_options.delayed_options, options.delayed_options); 761 | 762 | tmp_options = $.extend(default_options, options); 763 | tmp_options.delayed_options = tmp_delayed_options; 764 | 765 | fm_options_arr[tmp_options.position] = tmp_options; 766 | 767 | appendFeedbackToBody(tmp_options); 768 | 769 | detectTransitionSupport(tmp_options); 770 | } 771 | 772 | return { 773 | init : init, 774 | sendFeedback : sendFeedback, 775 | getFmOptions : getFmOptions, 776 | triggerAction : triggerAction, 777 | stopPropagation : stopPropagation, 778 | clearInputs : clearInputs, 779 | closeFeedbackDelayedDlg : closeFeedbackDelayedDlg 780 | }; 781 | 782 | }(jQuery)); 783 | --------------------------------------------------------------------------------