Smart Floating / Sticky Buttons – Call, Sharing, Chat Widgets & More – Buttonizer - Version 1.0.9

Version Description

A new cool update! We added for our new users a tour (of course, if you are already using Buttonizer, feel free to take the tour!). If someone has some feedback, about the tour, contact us through the plugin! :)

We've added something magical! For each button it's now possible to use one of the following actions: - Website URL: For opening a webpage - Phone number: Direct action - E-mail: Opens the desktop client from the user with your mail address filled in already

New this release: - Information tour for our new users - Button actions easier

Changed: - Re-ordered the settings page a bit

Fixed: - When enabling 'show on hover' with only one button, the label won't be visible anymore - Some caching plugins liked to cache our javascript, we've added a fix to that - Other bugs

Download this release

Release Info

Developer buttonizer
Plugin Icon wp plugin Smart Floating / Sticky Buttons – Call, Sharing, Chat Widgets & More – Buttonizer
Version 1.0.9
Comparing to
See all releases

Code changes from version 1.0.8 to 1.0.9

buttonizer.php CHANGED
@@ -3,7 +3,7 @@
3
  Plugin Name: Buttonizer - Smart Floating Action Button
4
  Plugin URI: https://buttonizer.pro
5
  Description: The Buttonizer is a new way to give a boost to your number of interactions, actions and conversions from your website visitor by adding one or multiple Customizable Smart Floating Button in the corner of your website.
6
- Version: 1.0.8
7
  Author: Buttonizer
8
  Author URI: https://buttonizer.pro
9
  License: GPL2
@@ -28,11 +28,13 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
28
  Copyright 2017 Buttonizer
29
  */
30
 
 
 
31
  define('BUTTONIZER_NAME', 'buttonizer');
32
  define('BUTTONIZER_DIR', dirname(__FILE__));
33
  define('BUTTONIZER_SLUG', basename(BUTTONIZER_DIR));
34
  define('BUTTONIZER_PLUGIN_DIR', __FILE__ );
35
- define('BUTTONIZER_VERSION','1.0.8');
36
 
37
  # No script kiddies
38
  defined( 'ABSPATH' ) or die('No script kiddies please!');
@@ -114,16 +116,21 @@ else
114
  /* LAST FEW FUNCTIONS */
115
  if(!function_exists("buttonizer_custom_connect_message_on_update")) {
116
  function buttonizer_custom_connect_message_on_update(
117
- $message,
118
- $user_first_name,
119
- $plugin_title,
120
- $user_login,
121
- $site_link,
122
- $freemius_link
123
- ) {
124
  return sprintf(
125
- __fs( 'hey-x' ) . '<br>' .
126
- __( 'Hey %2$s! We want to ask you to help us to improve Buttonizer! If you opt-in, some data about your usage of %2$s will be sent to %5$s. If you skip this, that\'s no problem! %2$s will still be improved!', 'buttonizer' ),
 
 
 
 
 
127
  $user_first_name,
128
  '<b>' . $plugin_title . '</b>',
129
  '<b>' . $user_login . '</b>',
3
  Plugin Name: Buttonizer - Smart Floating Action Button
4
  Plugin URI: https://buttonizer.pro
5
  Description: The Buttonizer is a new way to give a boost to your number of interactions, actions and conversions from your website visitor by adding one or multiple Customizable Smart Floating Button in the corner of your website.
6
+ Version: 1.0.9
7
  Author: Buttonizer
8
  Author URI: https://buttonizer.pro
9
  License: GPL2
28
  Copyright 2017 Buttonizer
29
  */
30
 
31
+ // error_reporting(E_ALL);
32
+
33
  define('BUTTONIZER_NAME', 'buttonizer');
34
  define('BUTTONIZER_DIR', dirname(__FILE__));
35
  define('BUTTONIZER_SLUG', basename(BUTTONIZER_DIR));
36
  define('BUTTONIZER_PLUGIN_DIR', __FILE__ );
37
+ define('BUTTONIZER_VERSION','1.0.9');
38
 
39
  # No script kiddies
40
  defined( 'ABSPATH' ) or die('No script kiddies please!');
116
  /* LAST FEW FUNCTIONS */
117
  if(!function_exists("buttonizer_custom_connect_message_on_update")) {
118
  function buttonizer_custom_connect_message_on_update(
119
+ $message,
120
+ $user_first_name,
121
+ $plugin_title,
122
+ $user_login,
123
+ $site_link,
124
+ $freemius_link
125
+ ) {
126
  return sprintf(
127
+ __( 'Hey %1$s' ) . ',<br>' .
128
+ '<br />' .
129
+ __( 'Click on Allow & Continue to start Buttonizing your website :)! Create Floating Action Buttons & Floating Menu\'s. Decide on a number of click actions like start chatting with Whatsapp, click-to-call, open a URL and more.') . '<br />' .
130
+ __('Never miss an important update -- opt-in to our security and feature updates notifications​.') . '<br />' .
131
+ '<br />' .
132
+ __('​See you on the other side.')
133
+ ,
134
  $user_first_name,
135
  '<b>' . $plugin_title . '</b>',
136
  '<b>' . $user_login . '</b>',
classes/admin/Buttons.php CHANGED
@@ -46,9 +46,9 @@ class Buttons
46
  private function loadButtons()
47
  {
48
  // Get buttons
49
- $aButtons = ( isset( $this->aSavedData['buttonorder'] ) && is_array( $this->aSavedData['buttonorder'] ) ? $this->aSavedData['buttonorder'] : array(
50
  0 => -1,
51
- ) );
52
  if ( $aButtons[0] != '-1' ) {
53
  $aButtons[-1] = '-1';
54
  }
@@ -71,55 +71,45 @@ class Buttons
71
  <li>
72
  <div class="button-row" id="btn_row_<?php
73
  echo $iButtonId ;
74
- ?>
75
- " button-id="<?php
76
  echo $iButtonId ;
77
- ?>
78
- "<?php
 
79
  if ( $iButtonId == -1 ) {
80
  echo ' style="display: none"' ;
81
  }
82
- ?>
83
- >
84
  <input type="checkbox" name="buttonizer_buttons[buttonorder][]" value="<?php
85
  echo $iButtonId ;
86
- ?>
87
- " checked="checked" style="display: none;" />
88
 
89
  <div class="row-info drag-handle">
90
  <div class="mover"></div>
91
  <span><i class="fa <?php
92
  echo $this->buttonOutputIcon( $iButtonId ) ;
93
- ?>
94
- button-icon"></i> <span class="row-title"><?php
95
  echo ( $iButtonId == -1 ? 'New button' : $sButtonTitle ) ;
96
- ?>
97
- </span> <i class="fa fa-pencil pencil-edit"></i></span>
98
 
99
  <a href="javascript:buttonizer.removeRow(<?php
100
  echo $iButtonId ;
101
- ?>
102
- )" class="delete-button">Delete <i class="fa fa-trash-o"></i></a>
103
 
104
  <a href="javascript:buttonizer.toggleMobile(<?php
105
  echo $iButtonId ;
106
- ?>
107
- )" class="mobiledesktop mobile-button <?php
108
- echo ( $onMobile == '' ? 'selected' : '' ) ;
109
- ?>
110
- "><i class="fa fa-mobile"></i></a>
111
  <a href="javascript:buttonizer.toggleDesktop(<?php
112
  echo $iButtonId ;
113
- ?>
114
- )" class="mobiledesktop desktop-button <?php
115
- echo ( $onDesktop == '' ? 'selected' : '' ) ;
116
- ?>
117
- "><i class="fa fa-desktop"></i></a>
118
 
119
  <a href="javascript:void(0)" class="is-live-button" style="<?php
120
- echo ( $onMobile != '' || $onDesktop != '' ? '' : 'display: none;' ) ;
121
- ?>
122
- "><i class="fa fa-circle"></i> Live</a>
123
 
124
  <button type="submit" class="must-save-button savebutton " style="display: none;"><i class="fa fa-floppy-o"></i> Save changes</button>
125
  </div>
@@ -138,11 +128,9 @@ class Buttons
138
  </label>
139
  <input type="text" name="buttonizer_buttons[button_<?php
140
  echo $iButtonId ;
141
- ?>
142
- _title]" value="<?php
143
  echo $sButtonTitle ;
144
- ?>
145
- " class="button_title" />
146
  </td>
147
  <td class="button-pdr">
148
  <label for="button_icon" class="label-top">
@@ -186,28 +174,30 @@ _title]" value="<?php
186
  <td></td>
187
  </tr>
188
 
189
- <tr class="settings-row">
190
- <td>
191
- <i class="fa fa-globe setting-icon"></i> URL or phone-number
192
- <span class="info-class"><i class="fa fa-info-circle"></i> <span>This is the click-action of your button. You can input a full url (http://myawesomewebsite.com), a URI (/contact), an anchor-link (#anchor) or a phone number. Note that if you input a phone number that you select the option ‘Is phone number’. This will be a direct click-to-call action. Please don’t add a full url without the http:// (like: www.myawesomewebsite.com). </span></span>
193
- </td>
194
- <td><?php
 
 
 
 
 
 
195
  echo $this->buttonGetUrl( $iButtonId ) ;
196
  ?>
197
- </td>
198
- </tr>
199
 
200
- <tr class="settings-row">
201
- <td>
202
- <i class="fa fa-phone setting-icon"></i> Is phone number
203
- </td>
204
- <td><?php
205
- echo $this->buttonGetIsPhoneNumber( $iButtonId ) ;
206
- ?>
207
- <label for="button_<?php
208
- echo $iButtonId ;
209
- ?>
210
- _is_phonenumber"> Yes</label></td>
211
  </tr>
212
 
213
  <tr class="settings-row setting-new-tab">
@@ -216,22 +206,19 @@ _is_phonenumber"> Yes</label></td>
216
  </td>
217
  <td><?php
218
  echo $this->buttonGetNewTab( $iButtonId ) ;
219
- ?>
220
- <label for="button_<?php
221
  echo $iButtonId ;
222
- ?>
223
- _url_newtab"> Yes</label></td>
224
  </tr>
225
 
226
  <tr class="settings-row">
227
  <td>
228
  <i class="fa fa-paint-brush setting-icon"></i> Button colors
229
- <span class="info-class"><i class="fa fa-info-circle"></i> <span>... </span></span>
230
  </td>
231
  <td><?php
232
  echo $this->buttonColors( $iButtonId ) ;
233
- ?>
234
- </td>
235
  </tr>
236
 
237
  <tr>
@@ -239,30 +226,26 @@ _url_newtab"> Yes</label></td>
239
  <td></td>
240
  </tr>
241
 
242
- <tr class="settings-row">
243
  <td>
244
  <i class="fa fa-mobile setting-icon"></i> Show on mobile
245
  </td>
246
  <td><?php
247
  echo $this->buttonGetShowPhone( $iButtonId ) ;
248
- ?>
249
- <label for="button_<?php
250
  echo $iButtonId ;
251
- ?>
252
- _show_on_phone"> Yes</label></td>
253
  </tr>
254
 
255
- <tr class="settings-row">
256
  <td>
257
  <i class="fa fa-desktop setting-icon"></i> Show on desktop
258
  </td>
259
  <td><?php
260
  echo $this->buttonGetShowDesktop( $iButtonId ) ;
261
- ?>
262
- <label for="button_<?php
263
  echo $iButtonId ;
264
- ?>
265
- _show_on_desktop"> Yes</label></td>
266
  </tr>
267
 
268
  <tr class="settings-row">
@@ -278,11 +261,9 @@ _show_on_desktop"> Yes</label></td>
278
  </td>
279
  <td><?php
280
  echo $this->buttonGetHideLabel( $iButtonId ) ;
281
- ?>
282
- <label for="button_<?php
283
  echo $iButtonId ;
284
- ?>
285
- _hide_label"> Yes</label></td>
286
  </tr>
287
 
288
  <tr>
@@ -318,7 +299,7 @@ _hide_label"> Yes</label></td>
318
  * Form functions
319
  */
320
  // Get the text of the button
321
- public function buttonGetText( $iButtonId = 0 )
322
  {
323
  $fieldName = 'button_' . $iButtonId . '_text';
324
  $fieldValue = ( isset( $this->aSavedData[$fieldName] ) ? $this->aSavedData[$fieldName] : 'New button' );
@@ -326,26 +307,26 @@ _hide_label"> Yes</label></td>
326
  }
327
 
328
  // Get button icon
329
- public function buttonGetIcon( $iButtonId = 0 )
330
  {
331
- return $this->oIconManager->generator( array(
332
  'icon' => ( isset( $this->aSavedData['button_' . $iButtonId . '_icon'] ) ? $this->aSavedData['button_' . $iButtonId . '_icon'] : '' ),
333
  'icon_fieldname' => 'buttonizer_buttons[button_' . $iButtonId . '_icon]',
334
  'image' => ( isset( $this->aSavedData['button_' . $iButtonId . '_image'] ) ? $this->aSavedData['button_' . $iButtonId . '_image'] : '' ),
335
  'image_fieldname' => 'buttonizer_buttons[button_' . $iButtonId . '_image]',
336
  'choose_type' => true,
337
- ) );
338
  }
339
 
340
  // Get button icon
341
- public function buttonOutputIcon( $iButtonId = 0 )
342
  {
343
  $fieldName = 'button_' . $iButtonId . '_icon';
344
  return ( isset( $this->aSavedData[$fieldName] ) ? $this->aSavedData[$fieldName] : 'fa-info' );
345
  }
346
 
347
  // Only show this button on phone?
348
- public function buttonGetShowPhone( $iButtonId = 0 )
349
  {
350
  $fieldName = 'button_' . $iButtonId . '_show_on_phone';
351
  $is_selected = ( isset( $this->aSavedData[$fieldName] ) ? $this->aSavedData[$fieldName] : '' );
@@ -353,11 +334,11 @@ _hide_label"> Yes</label></td>
353
  }
354
 
355
  // buttonGetShowOnPages
356
- public function buttonGetShowOnPages( $iButtonId = 0 )
357
  {
358
  $fieldName = 'button_' . $iButtonId . '_show_on_pages';
359
  $is_selected = ( isset( $this->aSavedData[$fieldName] ) ? $this->aSavedData[$fieldName] : '' );
360
- $pageOrders = ( isset( $this->aPageCategories['categorieOrder'] ) ? $this->aPageCategories['categorieOrder'] : array() );
361
  /*
362
  * Generate <select>
363
  */
@@ -372,7 +353,7 @@ _hide_label"> Yes</label></td>
372
  }
373
 
374
  // Only show this button on desktop?
375
- public function buttonGetShowDesktop( $iButtonId = 0 )
376
  {
377
  $fieldName = 'button_' . $iButtonId . '_show_on_desktop';
378
  $is_selected = ( isset( $this->aSavedData[$fieldName] ) ? $this->aSavedData[$fieldName] : '' );
@@ -380,7 +361,7 @@ _hide_label"> Yes</label></td>
380
  }
381
 
382
  // Only show when company is open?
383
- public function buttonGetShowWenOpen( $iButtonId = 0 )
384
  {
385
  $fieldName = 'button_' . $iButtonId . '_show_when_opened';
386
  $is_selected = ( isset( $this->aSavedData[$fieldName] ) ? $this->aSavedData[$fieldName] : '' );
@@ -388,44 +369,56 @@ _hide_label"> Yes</label></td>
388
  }
389
 
390
  // Only show when company is open?
391
- public function buttonGetShowNotWenOpen( $iButtonId = 0 )
392
  {
393
  $fieldName = 'button_' . $iButtonId . '_show_not_when_opened';
394
  $is_selected = ( isset( $this->aSavedData[$fieldName] ) ? $this->aSavedData[$fieldName] : '' );
395
  return '<input type="checkbox" name="buttonizer_buttons[' . $fieldName . ']" id="' . $fieldName . '" value="1" class="button_showwhenopened" ' . (( $is_selected == '1' ? 'checked="checked"' : '' )) . ' />';
396
  }
397
 
398
- public function buttonGetHideLabel( $iButtonId = 0 )
399
  {
400
  $fieldName = 'button_' . $iButtonId . '_hide_label';
401
  $is_selected = ( isset( $this->aSavedData[$fieldName] ) ? $this->aSavedData[$fieldName] : '' );
402
  return '<input type="checkbox" name="buttonizer_buttons[' . $fieldName . ']" id="' . $fieldName . '" value="1" class="button_hidelabel" ' . (( $is_selected == '1' ? 'checked="checked"' : '' )) . ' />';
403
  }
404
 
405
- // Is this a phone number?
406
- public function buttonGetIsPhoneNumber( $iButtonId = 0 )
407
  {
408
- $fieldName = 'button_' . $iButtonId . '_is_phonenumber';
409
- $is_selected = ( isset( $this->aSavedData[$fieldName] ) ? $this->aSavedData[$fieldName] : '' );
410
- return '<input type="checkbox" name="buttonizer_buttons[' . $fieldName . ']" id="' . $fieldName . '" value="1" class="button_isphonenumber" ' . (( $is_selected == '1' ? 'checked="checked"' : '' )) . ' />';
 
 
 
 
 
 
 
 
 
 
 
 
 
411
  }
412
 
413
- public function buttonGetUrl( $iButtonId = 0 )
414
  {
415
  $fieldName = 'button_' . $iButtonId . '_url';
416
  $fieldValue = ( isset( $this->aSavedData[$fieldName] ) ? $this->aSavedData[$fieldName] : '' );
417
- return '<input type="text" name="buttonizer_buttons[' . $fieldName . ']" id="' . $fieldName . '" value="' . $fieldValue . '" class="button_isurl" placeholder="URL/Phone number" />';
418
  }
419
 
420
  // Only show this button on phone?
421
- public function buttonGetNewTab( $iButtonId = 0 )
422
  {
423
  $fieldName = 'button_' . $iButtonId . '_url_newtab';
424
  $is_selected = ( isset( $this->aSavedData[$fieldName] ) ? $this->aSavedData[$fieldName] : '' );
425
  return '<input type="checkbox" name="buttonizer_buttons[' . $fieldName . ']" id="' . $fieldName . '" value="1" class="button_isnewtab" ' . (( $is_selected == '1' ? 'checked="checked"' : '' )) . ' />';
426
  }
427
 
428
- public function buttonColors( $iButtonId = 0 )
429
  {
430
  // Default button colors
431
  $sDefaultColor1 = $this->aSystemSettings['button_unpushed'];
46
  private function loadButtons()
47
  {
48
  // Get buttons
49
+ $aButtons = ( isset( $this->aSavedData['buttonorder'] ) && is_array( $this->aSavedData['buttonorder'] ) ? $this->aSavedData['buttonorder'] : [
50
  0 => -1,
51
+ ] );
52
  if ( $aButtons[0] != '-1' ) {
53
  $aButtons[-1] = '-1';
54
  }
71
  <li>
72
  <div class="button-row" id="btn_row_<?php
73
  echo $iButtonId ;
74
+ ?>" button-id="<?php
 
75
  echo $iButtonId ;
76
+ ?>" data-button-action="<?php
77
+ echo ( isset( $this->aSavedData['button_' . $iButtonId . '_type'] ) ? $this->aSavedData['button_' . $iButtonId . '_type'] : 'url' ) ;
78
+ ?>" <?php
79
  if ( $iButtonId == -1 ) {
80
  echo ' style="display: none"' ;
81
  }
82
+ ?>>
 
83
  <input type="checkbox" name="buttonizer_buttons[buttonorder][]" value="<?php
84
  echo $iButtonId ;
85
+ ?>" checked="checked" style="display: none;" />
 
86
 
87
  <div class="row-info drag-handle">
88
  <div class="mover"></div>
89
  <span><i class="fa <?php
90
  echo $this->buttonOutputIcon( $iButtonId ) ;
91
+ ?> button-icon"></i> <span class="row-title"><?php
 
92
  echo ( $iButtonId == -1 ? 'New button' : $sButtonTitle ) ;
93
+ ?></span> <i class="fa fa-pencil pencil-edit"></i></span>
 
94
 
95
  <a href="javascript:buttonizer.removeRow(<?php
96
  echo $iButtonId ;
97
+ ?>)" class="delete-button">Delete <i class="fa fa-trash-o"></i></a>
 
98
 
99
  <a href="javascript:buttonizer.toggleMobile(<?php
100
  echo $iButtonId ;
101
+ ?>)" class="mobiledesktop mobile-button <?php
102
+ echo ( $onMobile == "" ? 'selected' : '' ) ;
103
+ ?>"><i class="fa fa-mobile"></i></a>
 
 
104
  <a href="javascript:buttonizer.toggleDesktop(<?php
105
  echo $iButtonId ;
106
+ ?>)" class="mobiledesktop desktop-button <?php
107
+ echo ( $onDesktop == "" ? 'selected' : '' ) ;
108
+ ?>"><i class="fa fa-desktop"></i></a>
 
 
109
 
110
  <a href="javascript:void(0)" class="is-live-button" style="<?php
111
+ echo ( $onMobile != "" || $onDesktop != "" ? '' : 'display: none;' ) ;
112
+ ?>"><i class="fa fa-circle"></i> Live</a>
 
113
 
114
  <button type="submit" class="must-save-button savebutton " style="display: none;"><i class="fa fa-floppy-o"></i> Save changes</button>
115
  </div>
128
  </label>
129
  <input type="text" name="buttonizer_buttons[button_<?php
130
  echo $iButtonId ;
131
+ ?>_title]" value="<?php
 
132
  echo $sButtonTitle ;
133
+ ?>" class="button_title" />
 
134
  </td>
135
  <td class="button-pdr">
136
  <label for="button_icon" class="label-top">
174
  <td></td>
175
  </tr>
176
 
177
+ <tr class="settings-row button-action">
178
+ <td colspan="2">
179
+ <i class="fa fa-globe setting-icon"></i> Button action
180
+ <span class="info-class"><i class="fa fa-info-circle"></i> <span>This is the click-action of your button. When you choose for a phone number, it will be a direct click-to-call action.</span></span>
181
+
182
+ <table width="100%" class="settings-row-type">
183
+ <tr>
184
+ <td width="200"><?php
185
+ echo $this->buttonGetType( $iButtonId ) ;
186
+ ?></td>
187
+ <td>
188
+ <?php
189
  echo $this->buttonGetUrl( $iButtonId ) ;
190
  ?>
 
 
191
 
192
+ <div class="input_error" style="display: none;"></div>
193
+
194
+ <div class="extra_info" data-button-type="phone">
195
+
196
+ </div>
197
+ </td>
198
+ </tr>
199
+ </table>
200
+ </td>
 
 
201
  </tr>
202
 
203
  <tr class="settings-row setting-new-tab">
206
  </td>
207
  <td><?php
208
  echo $this->buttonGetNewTab( $iButtonId ) ;
209
+ ?><label for="button_<?php
 
210
  echo $iButtonId ;
211
+ ?>_url_newtab"> Yes</label></td>
 
212
  </tr>
213
 
214
  <tr class="settings-row">
215
  <td>
216
  <i class="fa fa-paint-brush setting-icon"></i> Button colors
217
+ <span class="info-class"><i class="fa fa-info-circle"></i> <span>You can change the colors of this button, like the default background, the background on hover or the icon color. You can undo them too. Click the palet for changing the colors.</span></span>
218
  </td>
219
  <td><?php
220
  echo $this->buttonColors( $iButtonId ) ;
221
+ ?></td>
 
222
  </tr>
223
 
224
  <tr>
226
  <td></td>
227
  </tr>
228
 
229
+ <tr class="settings-row show-mobile-btn">
230
  <td>
231
  <i class="fa fa-mobile setting-icon"></i> Show on mobile
232
  </td>
233
  <td><?php
234
  echo $this->buttonGetShowPhone( $iButtonId ) ;
235
+ ?><label for="button_<?php
 
236
  echo $iButtonId ;
237
+ ?>_show_on_phone"> Yes</label></td>
 
238
  </tr>
239
 
240
+ <tr class="settings-row show-desktop-btn">
241
  <td>
242
  <i class="fa fa-desktop setting-icon"></i> Show on desktop
243
  </td>
244
  <td><?php
245
  echo $this->buttonGetShowDesktop( $iButtonId ) ;
246
+ ?><label for="button_<?php
 
247
  echo $iButtonId ;
248
+ ?>_show_on_desktop"> Yes</label></td>
 
249
  </tr>
250
 
251
  <tr class="settings-row">
261
  </td>
262
  <td><?php
263
  echo $this->buttonGetHideLabel( $iButtonId ) ;
264
+ ?><label for="button_<?php
 
265
  echo $iButtonId ;
266
+ ?>_hide_label"> Yes</label></td>
 
267
  </tr>
268
 
269
  <tr>
299
  * Form functions
300
  */
301
  // Get the text of the button
302
+ function buttonGetText( $iButtonId = 0 )
303
  {
304
  $fieldName = 'button_' . $iButtonId . '_text';
305
  $fieldValue = ( isset( $this->aSavedData[$fieldName] ) ? $this->aSavedData[$fieldName] : 'New button' );
307
  }
308
 
309
  // Get button icon
310
+ function buttonGetIcon( $iButtonId = 0 )
311
  {
312
+ return $this->oIconManager->generator( [
313
  'icon' => ( isset( $this->aSavedData['button_' . $iButtonId . '_icon'] ) ? $this->aSavedData['button_' . $iButtonId . '_icon'] : '' ),
314
  'icon_fieldname' => 'buttonizer_buttons[button_' . $iButtonId . '_icon]',
315
  'image' => ( isset( $this->aSavedData['button_' . $iButtonId . '_image'] ) ? $this->aSavedData['button_' . $iButtonId . '_image'] : '' ),
316
  'image_fieldname' => 'buttonizer_buttons[button_' . $iButtonId . '_image]',
317
  'choose_type' => true,
318
+ ] );
319
  }
320
 
321
  // Get button icon
322
+ function buttonOutputIcon( $iButtonId = 0 )
323
  {
324
  $fieldName = 'button_' . $iButtonId . '_icon';
325
  return ( isset( $this->aSavedData[$fieldName] ) ? $this->aSavedData[$fieldName] : 'fa-info' );
326
  }
327
 
328
  // Only show this button on phone?
329
+ function buttonGetShowPhone( $iButtonId = 0 )
330
  {
331
  $fieldName = 'button_' . $iButtonId . '_show_on_phone';
332
  $is_selected = ( isset( $this->aSavedData[$fieldName] ) ? $this->aSavedData[$fieldName] : '' );
334
  }
335
 
336
  // buttonGetShowOnPages
337
+ function buttonGetShowOnPages( $iButtonId = 0 )
338
  {
339
  $fieldName = 'button_' . $iButtonId . '_show_on_pages';
340
  $is_selected = ( isset( $this->aSavedData[$fieldName] ) ? $this->aSavedData[$fieldName] : '' );
341
+ $pageOrders = ( isset( $this->aPageCategories['categorieOrder'] ) ? $this->aPageCategories['categorieOrder'] : [] );
342
  /*
343
  * Generate <select>
344
  */
353
  }
354
 
355
  // Only show this button on desktop?
356
+ function buttonGetShowDesktop( $iButtonId = 0 )
357
  {
358
  $fieldName = 'button_' . $iButtonId . '_show_on_desktop';
359
  $is_selected = ( isset( $this->aSavedData[$fieldName] ) ? $this->aSavedData[$fieldName] : '' );
361
  }
362
 
363
  // Only show when company is open?
364
+ function buttonGetShowWenOpen( $iButtonId = 0 )
365
  {
366
  $fieldName = 'button_' . $iButtonId . '_show_when_opened';
367
  $is_selected = ( isset( $this->aSavedData[$fieldName] ) ? $this->aSavedData[$fieldName] : '' );
369
  }
370
 
371
  // Only show when company is open?
372
+ function buttonGetShowNotWenOpen( $iButtonId = 0 )
373
  {
374
  $fieldName = 'button_' . $iButtonId . '_show_not_when_opened';
375
  $is_selected = ( isset( $this->aSavedData[$fieldName] ) ? $this->aSavedData[$fieldName] : '' );
376
  return '<input type="checkbox" name="buttonizer_buttons[' . $fieldName . ']" id="' . $fieldName . '" value="1" class="button_showwhenopened" ' . (( $is_selected == '1' ? 'checked="checked"' : '' )) . ' />';
377
  }
378
 
379
+ function buttonGetHideLabel( $iButtonId = 0 )
380
  {
381
  $fieldName = 'button_' . $iButtonId . '_hide_label';
382
  $is_selected = ( isset( $this->aSavedData[$fieldName] ) ? $this->aSavedData[$fieldName] : '' );
383
  return '<input type="checkbox" name="buttonizer_buttons[' . $fieldName . ']" id="' . $fieldName . '" value="1" class="button_hidelabel" ' . (( $is_selected == '1' ? 'checked="checked"' : '' )) . ' />';
384
  }
385
 
386
+ function buttonGetType( $iButtonId = 0 )
 
387
  {
388
+ $aTypes = [
389
+ 'url' => 'Website URL',
390
+ 'phone' => 'Phone number',
391
+ 'mail' => 'E-mail',
392
+ ];
393
+ $aTypes = array_merge( $aTypes, [
394
+ 'disabled_whatsapp' => 'Open whatsapp (phone number) - PRO ONLY',
395
+ 'disabled_javascript' => 'Javascript function - PRO ONLY',
396
+ ] );
397
+ $fieldName = 'button_' . $iButtonId . '_action';
398
+ $is_selected = ( isset( $this->aSavedData[$fieldName] ) ? $this->aSavedData[$fieldName] : 'url' );
399
+ $html = '<select name="buttonizer_buttons[' . $fieldName . ']" id="button_type" class="button_action">';
400
+ foreach ( $aTypes as $sType => $sTitle ) {
401
+ $html .= '<option value="' . str_replace( 'disabled_', '', $sType ) . '"' . (( $sType == $is_selected ? 'selected="selected"' : '' )) . ' ' . (( strpos( $sType, 'disabled_' ) !== false > 0 ? 'disabled' : '' )) . '>' . $sTitle . '</option>';
402
+ }
403
+ return $html . '</select>';
404
  }
405
 
406
+ function buttonGetUrl( $iButtonId = 0 )
407
  {
408
  $fieldName = 'button_' . $iButtonId . '_url';
409
  $fieldValue = ( isset( $this->aSavedData[$fieldName] ) ? $this->aSavedData[$fieldName] : '' );
410
+ return '<input type="text" name="buttonizer_buttons[' . $fieldName . ']" id="' . $fieldName . '" value="' . $fieldValue . '" class="button_input" placeholder="URL/Phone number" />';
411
  }
412
 
413
  // Only show this button on phone?
414
+ function buttonGetNewTab( $iButtonId = 0 )
415
  {
416
  $fieldName = 'button_' . $iButtonId . '_url_newtab';
417
  $is_selected = ( isset( $this->aSavedData[$fieldName] ) ? $this->aSavedData[$fieldName] : '' );
418
  return '<input type="checkbox" name="buttonizer_buttons[' . $fieldName . ']" id="' . $fieldName . '" value="1" class="button_isnewtab" ' . (( $is_selected == '1' ? 'checked="checked"' : '' )) . ' />';
419
  }
420
 
421
+ function buttonColors( $iButtonId = 0 )
422
  {
423
  // Default button colors
424
  $sDefaultColor1 = $this->aSystemSettings['button_unpushed'];
classes/admin/General.php CHANGED
@@ -34,8 +34,8 @@ class General
34
  $this->aFontAwesome = $aFontAwesome;
35
  $this->oIconManager = $oIconManager;
36
  $this->loadData();
37
- $this->registerPlacingSettings();
38
  $this->registerDesignSettings();
 
39
  $this->registerAdvancedSettings();
40
  $this->registerSocialSettings();
41
  $this->registerOther();
@@ -78,10 +78,10 @@ class General
78
  <h2>Placing &amp; animations</h2>
79
  <p>You can change here the button position and the button animation.</p>
80
  </td></tr>';
81
- $this->aSubPages['placing'] .= $this->createFormField( 'Right', array( &$this, 'field_position_right' ) );
82
  $this->aSubPages['placing'] .= $this->createFormField( 'Bottom', array( &$this, 'field_position_bottom' ) );
83
- $this->aSubPages['placing'] .= $this->createFormField( 'Button animation', array( &$this, 'field_button_animation' ) );
84
- $this->aSubPages['placing'] .= $this->createFormField( 'Attention animation', array( &$this, 'field_attention_animation' ) );
85
  $this->aSubPages['placing'] .= '</tbody></table>';
86
  }
87
 
@@ -97,11 +97,15 @@ class General
97
  <h2>Design</h2>
98
  <p>Style your button, make it blinky as you wish. You can change here the main color, main label and set the label hover types.</p>
99
  </td></tr>';
 
 
100
  $this->aSubPages['design'] .= $this->createFormField( 'Color button unpushed', array( &$this, 'field_button_unpushed' ) );
101
  $this->aSubPages['design'] .= $this->createFormField( 'Color button pushed', array( &$this, 'field_button_pushed' ) );
102
  $this->aSubPages['design'] .= $this->createFormField( 'Icon color', array( &$this, 'field_icon_color' ) );
103
- $this->aSubPages['design'] .= $this->createFormField( 'Main icon<br /><br /><small>Used only when more then one button and there is a menu.</small>', array( &$this, 'field_icon_icon' ) );
104
- $this->aSubPages['design'] .= $this->createFormField( 'Main label<br /><br /><small>Used only when more then one button and there is a menu.</small>', array( &$this, 'field_icon_label' ) );
 
 
105
  $this->aSubPages['design'] .= $this->createFormField( 'Only show labels on hover<br /><br /><small>Only show the label when you hover the button.</small>', array( &$this, 'field_label_show_on_hover' ) );
106
  $this->aSubPages['design'] .= '</tbody></table>';
107
  }
@@ -228,9 +232,9 @@ class General
228
  $this->aSubPages['other'] .= '</tbody></table>';
229
  }
230
 
231
- private function createFormField( $sLabel, $aFunction )
232
  {
233
- return '<tr><th scope="row">' . $sLabel . '</th><td class="r-sit">' . $aFunction() . '</td></tr>';
234
  }
235
 
236
  private function createFieldset( $sLabel )
@@ -368,21 +372,21 @@ class General
368
  {
369
  $share_facebook = ( isset( $this->aSavedData['share_facebook'] ) ? $this->aSavedData['share_facebook'] : '' );
370
  $share_facebook_text = ( isset( $this->aSavedData['share_facebook_text'] ) ? $this->aSavedData['share_facebook_text'] : 'Share this on Facebook' );
371
- return '<span class="after-input"><input type="checkbox" name="buttonizer_general_settings[share_facebook]" value="1" ' . (( $share_facebook == '1' ? 'checked="checked"' : '' )) . ' /></span> <input type="text" name="buttonizer_general_settings[share_facebook_text]" value="' . $share_facebook_text . '" placeholder="Share this on Facebook" />';
372
  }
373
 
374
  function field_share_linkedin()
375
  {
376
  $share_linkedin = ( isset( $this->aSavedData['share_linkedin'] ) ? $this->aSavedData['share_linkedin'] : '' );
377
  $share_linkedin_text = ( isset( $this->aSavedData['share_linkedin_text'] ) ? $this->aSavedData['share_linkedin_text'] : 'Share this on LinkedIn' );
378
- return '<span class="after-input"><input type="checkbox" name="buttonizer_general_settings[share_linkedin]" value="1" ' . (( $share_linkedin == '1' ? 'checked="checked"' : '' )) . ' /></span> <input type="text" name="buttonizer_general_settings[share_linkedin_text]" value="' . $share_linkedin_text . '" placeholder="Share this on LinkedIn" />';
379
  }
380
 
381
  function field_share_twitter()
382
  {
383
  $share_twitter = ( isset( $this->aSavedData['share_twitter'] ) ? $this->aSavedData['share_twitter'] : '' );
384
  $share_twitter_text = ( isset( $this->aSavedData['share_twitter_text'] ) ? $this->aSavedData['share_twitter_text'] : 'Share this on Twitter' );
385
- return '<span class="after-input"><input type="checkbox" name="buttonizer_general_settings[share_twitter]" value="1" ' . (( $share_twitter == '1' ? 'checked="checked"' : '' )) . ' /></span> <input type="text" name="buttonizer_general_settings[share_twitter_text]" value="' . $share_twitter_text . '" placeholder="Share this on Twitter" />';
386
  }
387
 
388
  }
34
  $this->aFontAwesome = $aFontAwesome;
35
  $this->oIconManager = $oIconManager;
36
  $this->loadData();
 
37
  $this->registerDesignSettings();
38
+ $this->registerPlacingSettings();
39
  $this->registerAdvancedSettings();
40
  $this->registerSocialSettings();
41
  $this->registerOther();
78
  <h2>Placing &amp; animations</h2>
79
  <p>You can change here the button position and the button animation.</p>
80
  </td></tr>';
81
+ $this->aSubPages['placing'] .= $this->createFormField( 'Right', array( &$this, 'field_position_right' ), 'intro-position' );
82
  $this->aSubPages['placing'] .= $this->createFormField( 'Bottom', array( &$this, 'field_position_bottom' ) );
83
+ $this->aSubPages['placing'] .= $this->createFormField( 'Button animation', array( &$this, 'field_button_animation' ), 'intro-animaton' );
84
+ $this->aSubPages['placing'] .= $this->createFormField( 'Attention animation', array( &$this, 'field_attention_animation' ), 'intro-attention' );
85
  $this->aSubPages['placing'] .= '</tbody></table>';
86
  }
87
 
97
  <h2>Design</h2>
98
  <p>Style your button, make it blinky as you wish. You can change here the main color, main label and set the label hover types.</p>
99
  </td></tr>';
100
+ $this->aSubPages['design'] .= '</tbody></table>';
101
+ $this->aSubPages['design'] .= '<table class="form-table intro-styling"><tbody>';
102
  $this->aSubPages['design'] .= $this->createFormField( 'Color button unpushed', array( &$this, 'field_button_unpushed' ) );
103
  $this->aSubPages['design'] .= $this->createFormField( 'Color button pushed', array( &$this, 'field_button_pushed' ) );
104
  $this->aSubPages['design'] .= $this->createFormField( 'Icon color', array( &$this, 'field_icon_color' ) );
105
+ $this->aSubPages['design'] .= '</tbody></table>';
106
+ $this->aSubPages['design'] .= '<table class="form-table" style="margin-top: 0;"><tbody>';
107
+ $this->aSubPages['design'] .= $this->createFormField( 'Main icon<br /><br /><small>This icon will apear in a button when you have multiple floating action buttons on one page. When you click on the icon the other floating action button will \'pop\' open​.</small>', array( &$this, 'field_icon_icon' ), 'intro-icon' );
108
+ $this->aSubPages['design'] .= $this->createFormField( 'Main label<br /><br /><small>This label will apear next to the \'main button\' when you have multiple floating action buttons on one page.</small>', array( &$this, 'field_icon_label' ), 'intro-label' );
109
  $this->aSubPages['design'] .= $this->createFormField( 'Only show labels on hover<br /><br /><small>Only show the label when you hover the button.</small>', array( &$this, 'field_label_show_on_hover' ) );
110
  $this->aSubPages['design'] .= '</tbody></table>';
111
  }
232
  $this->aSubPages['other'] .= '</tbody></table>';
233
  }
234
 
235
+ private function createFormField( $sLabel, $aFunction, $sClass = '' )
236
  {
237
+ return '<tr class="' . $sClass . '"><th scope="row">' . $sLabel . '</th><td class="r-sit">' . $aFunction() . '</td></tr>';
238
  }
239
 
240
  private function createFieldset( $sLabel )
372
  {
373
  $share_facebook = ( isset( $this->aSavedData['share_facebook'] ) ? $this->aSavedData['share_facebook'] : '' );
374
  $share_facebook_text = ( isset( $this->aSavedData['share_facebook_text'] ) ? $this->aSavedData['share_facebook_text'] : 'Share this on Facebook' );
375
+ return '<span class="before-input"><input type="checkbox" name="buttonizer_general_settings[share_facebook]" value="1" ' . (( $share_facebook == '1' ? 'checked="checked"' : '' )) . ' /></span> <input type="text" name="buttonizer_general_settings[share_facebook_text]" value="' . $share_facebook_text . '" class="before-input-padding" placeholder="Share this on Facebook" />';
376
  }
377
 
378
  function field_share_linkedin()
379
  {
380
  $share_linkedin = ( isset( $this->aSavedData['share_linkedin'] ) ? $this->aSavedData['share_linkedin'] : '' );
381
  $share_linkedin_text = ( isset( $this->aSavedData['share_linkedin_text'] ) ? $this->aSavedData['share_linkedin_text'] : 'Share this on LinkedIn' );
382
+ return '<span class="before-input"><input type="checkbox" name="buttonizer_general_settings[share_linkedin]" value="1" ' . (( $share_linkedin == '1' ? 'checked="checked"' : '' )) . ' /></span> <input type="text" name="buttonizer_general_settings[share_linkedin_text]" value="' . $share_linkedin_text . '" class="before-input-padding" placeholder="Share this on LinkedIn" />';
383
  }
384
 
385
  function field_share_twitter()
386
  {
387
  $share_twitter = ( isset( $this->aSavedData['share_twitter'] ) ? $this->aSavedData['share_twitter'] : '' );
388
  $share_twitter_text = ( isset( $this->aSavedData['share_twitter_text'] ) ? $this->aSavedData['share_twitter_text'] : 'Share this on Twitter' );
389
+ return '<span class="before-input"><input type="checkbox" name="buttonizer_general_settings[share_twitter]" value="1" ' . (( $share_twitter == '1' ? 'checked="checked"' : '' )) . ' /></span> <input type="text" name="buttonizer_general_settings[share_twitter_text]" value="' . $share_twitter_text . '" class="before-input-padding" placeholder="Share this on Twitter" />';
390
  }
391
 
392
  }
classes/admin/InitializeAdmin.php CHANGED
@@ -86,11 +86,13 @@ class InitializeAdmin {
86
  wp_enqueue_script('sortable', plugins_url('/js/sortable.min.js?v=' . md5(BUTTONIZER_VERSION), BUTTONIZER_PLUGIN_DIR));
87
  wp_enqueue_script('dashboard-script', plugins_url('/js/dashboard.js?v=' . md5(BUTTONIZER_VERSION), BUTTONIZER_PLUGIN_DIR));
88
  wp_enqueue_script('opening-script', plugins_url('/js/company-openingtimes.js?v=' . md5(BUTTONIZER_VERSION), BUTTONIZER_PLUGIN_DIR));
89
- wp_enqueue_script('advanced-settings-script', plugins_url('/js/advanced-settings.js?v=' . md5(BUTTONIZER_VERSION), BUTTONIZER_PLUGIN_DIR));
 
90
 
91
  wp_enqueue_style('font-awesome','//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css');
92
 
93
- wp_enqueue_style('dialog-boxes', plugins_url('/freemius/assets/css/admin/dialog-boxes.css', BUTTONIZER_PLUGIN_DIR));
 
94
 
95
  }
96
 
@@ -195,6 +197,8 @@ class InitializeAdmin {
195
  echo '<a href="'. ButtonizerLicense()->get_account_url() .'" class="nav-tab" style="float: right; border: 0; background: none; outline: none !important;">My Account</a>';
196
  }
197
 
 
 
198
  echo '</h1>';
199
  }
200
 
86
  wp_enqueue_script('sortable', plugins_url('/js/sortable.min.js?v=' . md5(BUTTONIZER_VERSION), BUTTONIZER_PLUGIN_DIR));
87
  wp_enqueue_script('dashboard-script', plugins_url('/js/dashboard.js?v=' . md5(BUTTONIZER_VERSION), BUTTONIZER_PLUGIN_DIR));
88
  wp_enqueue_script('opening-script', plugins_url('/js/company-openingtimes.js?v=' . md5(BUTTONIZER_VERSION), BUTTONIZER_PLUGIN_DIR));
89
+ wp_enqueue_script('advanced-settings-script', plugins_url('/js/advanced-settings.js?v=' . md5(BUTTONIZER_VERSION), BUTTONIZER_PLUGIN_DIR));
90
+ wp_enqueue_script('intro', plugins_url('/js/intro.js?v=' . md5(BUTTONIZER_VERSION), BUTTONIZER_PLUGIN_DIR));
91
 
92
  wp_enqueue_style('font-awesome','//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css');
93
 
94
+ wp_enqueue_style('dialog-boxes', plugins_url('/freemius/assets/css/admin/dialog-boxes.css', BUTTONIZER_PLUGIN_DIR));
95
+ wp_enqueue_style('intro', plugins_url('/css/introjs.css?v=' . md5(BUTTONIZER_VERSION), BUTTONIZER_PLUGIN_DIR));
96
 
97
  }
98
 
197
  echo '<a href="'. ButtonizerLicense()->get_account_url() .'" class="nav-tab" style="float: right; border: 0; background: none; outline: none !important;">My Account</a>';
198
  }
199
 
200
+ echo '<a href="?page=Buttonizer&tab=buttonizer_general_settings#buttonizer-tour" class="nav-tab" style="float: right; border: 0; background: none; outline: none !important;" id="take-the-tour">Take the tour</a>';
201
+
202
  echo '</h1>';
203
  }
204
 
classes/default/license.php CHANGED
@@ -41,8 +41,9 @@ class License
41
  'has_addons' => false,
42
  'has_paid_plans' => true,
43
  'menu' => array(
44
- 'slug' => 'Buttonizer',
45
- 'support' => false,
 
46
  ),
47
  'is_live' => true,
48
  ) );
41
  'has_addons' => false,
42
  'has_paid_plans' => true,
43
  'menu' => array(
44
+ 'first-path' => 'admin.php?page=Buttonizer&welcome-splash=true',
45
+ 'slug' => 'Buttonizer',
46
+ 'support' => false,
47
  ),
48
  'is_live' => true,
49
  ) );
classes/default/main.php CHANGED
@@ -149,14 +149,21 @@ class Button
149
  $buttonText = ( isset( $this->aButtons['button_' . $bNmbr . '_text'] ) ? $this->aButtons['button_' . $bNmbr . '_text'] : 'Button ' . $bNmbr );
150
  $buttonTitle = ( isset( $this->aButtons['button_' . $bNmbr . '_title'] ) ? $this->aButtons['button_' . $bNmbr . '_title'] : 'Button ' . $bNmbr );
151
  $buttonIsPhone = ( isset( $this->aButtons['button_' . $bNmbr . '_is_phonenumber'] ) ? $this->aButtons['button_' . $bNmbr . '_is_phonenumber'] : '' );
152
- $link = ( isset( $this->aButtons['button_' . $bNmbr . '_url'] ) ? $this->aButtons['button_' . $bNmbr . '_url'] : '' );
153
  $linkNewTab = ( isset( $this->aButtons['button_' . $bNmbr . '_url_newtab'] ) ? 'target="_blank"' : '' );
154
  $hideLabel = ( isset( $this->aButtons['button_' . $bNmbr . '_hide_label'] ) ? $this->aButtons['button_' . $bNmbr . '_hide_label'] : '' );
 
 
155
 
156
- if ( $link != "#" && $link != "" && strpos( 'javascript:', $link ) !== false ) {
157
- $linkInfo = parse_url( $link );
158
- if ( !$buttonIsPhone && empty($linkInfo['scheme']) && substr( $link, 0, 1 ) != '/' ) {
159
- $link = "http://" . $link;
 
 
 
 
 
 
160
  }
161
  }
162
 
@@ -184,7 +191,7 @@ class Button
184
  color: ' . $this->aButtons['button_' . $bNmbr . '_colors_icon'] . ';
185
  } ';
186
  }
187
- return '<a href="' . (( $buttonIsPhone ? 'tel:' . $link : $link )) . '" class="' . $sButtonClasses . ' buttonizer_' . $bNmbr . '" ' . $linkNewTab . ' onclick="onButtonizerClickEvent(\'' . $buttonText . '\')">' . (( $buttonText != "" ? '<div class="text"' . (( $hideLabel == '1' ? 'style="display: none;"' : '' )) . '><div>' . $buttonText . '</div></div>' : '' )) . $sButtonIcon . '</a>';
188
  // Thanks, end
189
  }
190
 
@@ -352,13 +359,15 @@ class Button
352
  echo '<style>.buttonizer-button a:hover, .buttonizer-button a:focus{ background:' . $this->aSettings['button_pushed'] . '; } .buttonizer-button a { background:' . $this->aSettings['button_unpushed'] . '; } .buttonizer-button a i { color: ' . $this->aSettings['icon_color'] . '; } ' . $this->sButtonCss . '</style>
353
  <div class="buttonizer-button ' . (( $showOnScroll > 0 || $showAfterTimeout > 0 ? 'hide' : '' )) . ' ' . (( isset( $this->aSettings['buttons_label_show_on_hover'] ) && $this->aSettings['buttons_label_show_on_hover'] == '1' ? ' show_labels_on_hover' : '' )) . '" button-animation="' . $sButtonAnimation . '" attention-animation="' . $sAttentionAnimation . '" style=" right: ' . $this->aSettings['position_right'] . '%; bottom: ' . $this->aSettings['position_bottom'] . '%;" id="buttonizer-button"><div class="buttonizer_inner" id="buttonizer-sys">' . $output . '</div></div>' ;
354
  echo '
355
- <script type="text/javascript" src="' . plugins_url( '/js/buttonizer.js?v=' . md5( BUTTONIZER_VERSION ), BUTTONIZER_PLUGIN_DIR ) . '"></script>
356
  <script type="text/javascript">
357
- buttonizer.init({
358
- scrollBarTop: ' . $showOnScroll . ',
359
- showAfter: ' . $showAfterTimeout . ',
360
- ' . (( ButtonizerLicense()->is__premium_only() ? 'exitIntent: ' . $this->enableExitIntent() . ',' : '' )) . '
361
- ' . (( ButtonizerLicense()->is__premium_only() ? 'exitIntentText: "' . $this->enableExitIntentText() . '",' : '' )) . '
 
 
362
  });
363
  </script>' ;
364
  }
149
  $buttonText = ( isset( $this->aButtons['button_' . $bNmbr . '_text'] ) ? $this->aButtons['button_' . $bNmbr . '_text'] : 'Button ' . $bNmbr );
150
  $buttonTitle = ( isset( $this->aButtons['button_' . $bNmbr . '_title'] ) ? $this->aButtons['button_' . $bNmbr . '_title'] : 'Button ' . $bNmbr );
151
  $buttonIsPhone = ( isset( $this->aButtons['button_' . $bNmbr . '_is_phonenumber'] ) ? $this->aButtons['button_' . $bNmbr . '_is_phonenumber'] : '' );
 
152
  $linkNewTab = ( isset( $this->aButtons['button_' . $bNmbr . '_url_newtab'] ) ? 'target="_blank"' : '' );
153
  $hideLabel = ( isset( $this->aButtons['button_' . $bNmbr . '_hide_label'] ) ? $this->aButtons['button_' . $bNmbr . '_hide_label'] : '' );
154
+ $sButtonAction = ( isset( $this->aButtons['button_' . $bNmbr . '_action'] ) ? $this->aButtons['button_' . $bNmbr . '_action'] : '' );
155
+ $sButtonActionLink = ( isset( $this->aButtons['button_' . $bNmbr . '_url'] ) ? $this->aButtons['button_' . $bNmbr . '_url'] : '' );
156
 
157
+ if ( 'phone' == $sButtonAction || $buttonIsPhone ) {
158
+ $sButtonActionLink = 'tel:' . str_replace( [
159
+ ' ',
160
+ '+',
161
+ '?',
162
+ '#'
163
+ ], '', $sButtonActionLink );
164
+ } else {
165
+ if ( 'mail' == $sButtonAction ) {
166
+ $sButtonActionLink = 'mailto:' . $sButtonActionLink;
167
  }
168
  }
169
 
191
  color: ' . $this->aButtons['button_' . $bNmbr . '_colors_icon'] . ';
192
  } ';
193
  }
194
+ return '<a href="' . $sButtonActionLink . '" class="' . $sButtonClasses . ' buttonizer_' . $bNmbr . '" ' . $linkNewTab . ' onclick="onButtonizerClickEvent(\'' . $buttonText . '\')">' . (( $buttonText != "" ? '<div class="text"' . (( $hideLabel == '1' ? 'style="display: none;"' : '' )) . '><div>' . $buttonText . '</div></div>' : '' )) . $sButtonIcon . '</a>';
195
  // Thanks, end
196
  }
197
 
359
  echo '<style>.buttonizer-button a:hover, .buttonizer-button a:focus{ background:' . $this->aSettings['button_pushed'] . '; } .buttonizer-button a { background:' . $this->aSettings['button_unpushed'] . '; } .buttonizer-button a i { color: ' . $this->aSettings['icon_color'] . '; } ' . $this->sButtonCss . '</style>
360
  <div class="buttonizer-button ' . (( $showOnScroll > 0 || $showAfterTimeout > 0 ? 'hide' : '' )) . ' ' . (( isset( $this->aSettings['buttons_label_show_on_hover'] ) && $this->aSettings['buttons_label_show_on_hover'] == '1' ? ' show_labels_on_hover' : '' )) . '" button-animation="' . $sButtonAnimation . '" attention-animation="' . $sAttentionAnimation . '" style=" right: ' . $this->aSettings['position_right'] . '%; bottom: ' . $this->aSettings['position_bottom'] . '%;" id="buttonizer-button"><div class="buttonizer_inner" id="buttonizer-sys">' . $output . '</div></div>' ;
361
  echo '
362
+ <script defer type="text/javascript" src="' . plugins_url( '/js/buttonizer.js?v=' . md5( BUTTONIZER_VERSION ), BUTTONIZER_PLUGIN_DIR ) . '"></script>
363
  <script type="text/javascript">
364
+ document.addEventListener(\'DOMContentLoaded\', function(){
365
+ buttonizer.init({
366
+ scrollBarTop: ' . $showOnScroll . ',
367
+ showAfter: ' . $showAfterTimeout . ',
368
+ ' . (( ButtonizerLicense()->is__premium_only() ? 'exitIntent: ' . $this->enableExitIntent() . ',' : '' )) . '
369
+ ' . (( ButtonizerLicense()->is__premium_only() ? 'exitIntentText: "' . $this->enableExitIntentText() . '",' : '' )) . '
370
+ });
371
  });
372
  </script>' ;
373
  }
css/buttonizer.css CHANGED
@@ -74,7 +74,7 @@
74
  }
75
 
76
  .buttonizer-button .buttonizer_inner a.buttonizer_head i {
77
- font-size: 40px !important;
78
  font-style: normal;
79
  width: 56px !important;
80
  height: 56px !important;
@@ -348,12 +348,14 @@
348
  transition-delay: 300ms;
349
  }
350
 
351
- .buttonizer-button.show_labels_on_hover a > .text {
 
352
  opacity: 0;
353
  visibility: hidden;
354
  }
355
 
356
- .buttonizer-button.show_labels_on_hover a:hover > .text {
 
357
  opacity: 1;
358
  visibility: visible;
359
  }
74
  }
75
 
76
  .buttonizer-button .buttonizer_inner a.buttonizer_head i {
77
+ font-size: 28px !important;
78
  font-style: normal;
79
  width: 56px !important;
80
  height: 56px !important;
348
  transition-delay: 300ms;
349
  }
350
 
351
+ .buttonizer-button.show_labels_on_hover a > .text,
352
+ .buttonizer-button.show_labels_on_hover .buttonizer_head.onlyone > .text {
353
  opacity: 0;
354
  visibility: hidden;
355
  }
356
 
357
+ .buttonizer-button.show_labels_on_hover a:hover > .text,
358
+ .buttonizer-button.show_labels_on_hover .buttonizer_head.onlyone:hover > .text {
359
  opacity: 1;
360
  visibility: visible;
361
  }
css/introjs.css ADDED
@@ -0,0 +1,552 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .introjs-overlay {
2
+ position: absolute;
3
+ box-sizing: content-box;
4
+ z-index: 999999;
5
+ background-color: #000;
6
+ opacity: 0;
7
+ background: -moz-radial-gradient(center,ellipse cover,rgba(0,0,0,0.4) 0,rgba(0,0,0,0.9) 100%);
8
+ background: -webkit-gradient(radial,center center,0px,center center,100%,color-stop(0%,rgba(0,0,0,0.4)),color-stop(100%,rgba(0,0,0,0.9)));
9
+ background: -webkit-radial-gradient(center,ellipse cover,rgba(0,0,0,0.4) 0,rgba(0,0,0,0.9) 100%);
10
+ background: -o-radial-gradient(center,ellipse cover,rgba(0,0,0,0.4) 0,rgba(0,0,0,0.9) 100%);
11
+ background: -ms-radial-gradient(center,ellipse cover,rgba(0,0,0,0.4) 0,rgba(0,0,0,0.9) 100%);
12
+ background: radial-gradient(center,ellipse cover,rgba(0,0,0,0.4) 0,rgba(0,0,0,0.9) 100%);
13
+ filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr='#66000000',endColorstr='#e6000000',GradientType=1)";
14
+ -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)";
15
+ filter: alpha(opacity=50);
16
+ -webkit-transition: all 0.3s ease-out;
17
+ -moz-transition: all 0.3s ease-out;
18
+ -ms-transition: all 0.3s ease-out;
19
+ -o-transition: all 0.3s ease-out;
20
+ transition: all 0.3s ease-out;
21
+ }
22
+
23
+ .introjs-fixParent {
24
+ z-index: auto !important;
25
+ opacity: 1.0 !important;
26
+ -webkit-transform: none !important;
27
+ -moz-transform: none !important;
28
+ -ms-transform: none !important;
29
+ -o-transform: none !important;
30
+ transform: none !important;
31
+ }
32
+
33
+ .introjs-showElement,
34
+ tr.introjs-showElement > td,
35
+ tr.introjs-showElement > th {
36
+ z-index: 9999999 !important;
37
+ }
38
+
39
+ .introjs-disableInteraction {
40
+ z-index: 99999999 !important;
41
+ position: absolute;
42
+ background-color: white;
43
+ opacity: 0;
44
+ filter: alpha(opacity=0);
45
+ }
46
+
47
+ .introjs-relativePosition,
48
+ tr.introjs-showElement > td,
49
+ tr.introjs-showElement > th {
50
+ position: relative;
51
+ }
52
+
53
+ .introjs-helperLayer {
54
+ box-sizing: content-box;
55
+ position: absolute;
56
+ z-index: 9999998;
57
+ background-color: #FFF;
58
+ background-color: rgba(255,255,255,.9);
59
+ border: 1px solid #777;
60
+ border: 1px solid rgba(0,0,0,.5);
61
+ border-radius: 4px;
62
+ box-shadow: 0 2px 15px rgba(0,0,0,.4);
63
+ -webkit-transition: all 0.3s ease-out;
64
+ -moz-transition: all 0.3s ease-out;
65
+ -ms-transition: all 0.3s ease-out;
66
+ -o-transition: all 0.3s ease-out;
67
+ transition: all 0.3s ease-out;
68
+ }
69
+
70
+ .introjs-tooltipReferenceLayer {
71
+ box-sizing: content-box;
72
+ position: absolute;
73
+ visibility: hidden;
74
+ z-index: 10000000;
75
+ background-color: transparent;
76
+ -webkit-transition: all 0.3s ease-out;
77
+ -moz-transition: all 0.3s ease-out;
78
+ -ms-transition: all 0.3s ease-out;
79
+ -o-transition: all 0.3s ease-out;
80
+ transition: all 0.3s ease-out;
81
+ }
82
+
83
+ .introjs-helperLayer *,
84
+ .introjs-helperLayer *:before,
85
+ .introjs-helperLayer *:after {
86
+ -webkit-box-sizing: content-box;
87
+ -moz-box-sizing: content-box;
88
+ -ms-box-sizing: content-box;
89
+ -o-box-sizing: content-box;
90
+ box-sizing: content-box;
91
+ }
92
+
93
+ .introjs-helperNumberLayer {
94
+ box-sizing: content-box;
95
+ position: absolute;
96
+ visibility: visible;
97
+ top: -16px;
98
+ left: -16px;
99
+ z-index: 9999999999 !important;
100
+ padding: 2px;
101
+ font-family: Arial, verdana, tahoma;
102
+ font-size: 13px;
103
+ font-weight: bold;
104
+ color: white;
105
+ text-align: center;
106
+ text-shadow: 1px 1px 1px rgba(0,0,0,.3);
107
+ background: #ff3019; /* Old browsers */
108
+ background: -webkit-linear-gradient(top, #ff8319 0%, #cf5804 100%); /* Chrome10+,Safari5.1+ */
109
+ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #ff8319), color-stop(100%, #cf5804)); /* Chrome,Safari4+ */
110
+ background: -moz-linear-gradient(top, #ff8319 0%, #cf5804 100%); /* FF3.6+ */
111
+ background: -ms-linear-gradient(top, #ff8319 0%, #cf5804 100%); /* IE10+ */
112
+ background: -o-linear-gradient(top, #ff8319 0%, #cf5804 100%); /* Opera 11.10+ */
113
+ background: linear-gradient(to bottom, #ff8319 0%, #cf5804 100%); /* W3C */
114
+ width: 20px;
115
+ height:20px;
116
+ line-height: 20px;
117
+ border: 3px solid white;
118
+ border-radius: 50%;
119
+ filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff8319', endColorstr='#cf5804', GradientType=0)"; /* IE6-9 */
120
+ filter: "progid:DXImageTransform.Microsoft.Shadow(direction=135, strength=2, color=ff0000)"; /* IE10 text shadows */
121
+ box-shadow: 0 2px 5px rgba(0,0,0,.4);
122
+ }
123
+
124
+ .introjs-arrow {
125
+ border: 5px solid white;
126
+ content:'';
127
+ position: absolute;
128
+ }
129
+ .introjs-arrow.top {
130
+ top: -10px;
131
+ border-top-color:transparent;
132
+ border-right-color:transparent;
133
+ border-bottom-color:white;
134
+ border-left-color:transparent;
135
+ }
136
+ .introjs-arrow.top-right {
137
+ top: -10px;
138
+ right: 10px;
139
+ border-top-color:transparent;
140
+ border-right-color:transparent;
141
+ border-bottom-color:white;
142
+ border-left-color:transparent;
143
+ }
144
+ .introjs-arrow.top-middle {
145
+ top: -10px;
146
+ left: 50%;
147
+ margin-left: -5px;
148
+ border-top-color:transparent;
149
+ border-right-color:transparent;
150
+ border-bottom-color:white;
151
+ border-left-color:transparent;
152
+ }
153
+ .introjs-arrow.right {
154
+ right: -10px;
155
+ top: 10px;
156
+ border-top-color:transparent;
157
+ border-right-color:transparent;
158
+ border-bottom-color:transparent;
159
+ border-left-color:white;
160
+ }
161
+ .introjs-arrow.right-bottom {
162
+ bottom:10px;
163
+ right: -10px;
164
+ border-top-color:transparent;
165
+ border-right-color:transparent;
166
+ border-bottom-color:transparent;
167
+ border-left-color:white;
168
+ }
169
+ .introjs-arrow.bottom {
170
+ bottom: -10px;
171
+ border-top-color:white;
172
+ border-right-color:transparent;
173
+ border-bottom-color:transparent;
174
+ border-left-color:transparent;
175
+ }
176
+ .introjs-arrow.left {
177
+ left: -10px;
178
+ top: 10px;
179
+ border-top-color:transparent;
180
+ border-right-color:white;
181
+ border-bottom-color:transparent;
182
+ border-left-color:transparent;
183
+ }
184
+ .introjs-arrow.left-bottom {
185
+ left: -10px;
186
+ bottom:10px;
187
+ border-top-color:transparent;
188
+ border-right-color:white;
189
+ border-bottom-color:transparent;
190
+ border-left-color:transparent;
191
+ }
192
+
193
+ .introjs-tooltip {
194
+ box-sizing: content-box;
195
+ position: absolute;
196
+ visibility: visible;
197
+ padding: 10px;
198
+ background-color: white;
199
+ min-width: 200px;
200
+ max-width: 300px;
201
+ border-radius: 3px;
202
+ box-shadow: 0 1px 10px rgba(0,0,0,.4);
203
+ -webkit-transition: opacity 0.1s ease-out;
204
+ -moz-transition: opacity 0.1s ease-out;
205
+ -ms-transition: opacity 0.1s ease-out;
206
+ -o-transition: opacity 0.1s ease-out;
207
+ transition: opacity 0.1s ease-out;
208
+ }
209
+
210
+ .introjs-tooltipbuttons {
211
+ text-align: right;
212
+ white-space: nowrap;
213
+ margin-top: 20px;
214
+ }
215
+
216
+ /*
217
+ Buttons style by http://nicolasgallagher.com/lab/css3-github-buttons/
218
+ Changed by Afshin Mehrabani
219
+ */
220
+ .introjs-button {
221
+ box-sizing: content-box;
222
+ position: relative;
223
+ overflow: visible;
224
+ display: inline-block;
225
+ padding: 6px 17px;
226
+ margin: 0;
227
+ text-decoration: none;
228
+ font: 12px/normal sans-serif;
229
+ white-space: nowrap;
230
+ cursor: pointer;
231
+ outline: none;
232
+ -webkit-background-clip: padding;
233
+ -moz-background-clip: padding;
234
+ -o-background-clip: padding-box;
235
+ /*background-clip: padding-box;*/ /* commented out due to Opera 11.10 bug */
236
+ -webkit-border-radius: 0.2em;
237
+ -moz-border-radius: 0.2em;
238
+ border-radius: 0.2em;
239
+ /* IE hacks */
240
+ zoom: 1;
241
+ *display: inline;
242
+ margin-top: 10px;
243
+ color: #FFFFFF;
244
+ font-weight: 500;
245
+
246
+ background: #0085ba;
247
+ border: 1px solid #006799;
248
+ border-bottom-width: 2px;
249
+ border-color: #0073aa #006799 #006799;
250
+ -webkit-box-shadow: 0 1px 0 #006799;
251
+ box-shadow: 0 1px 0 #006799;
252
+ color: #fff;
253
+ text-decoration: none;
254
+ text-shadow: 0 -1px 1px #006799, 1px 0 1px #006799, 0 1px 1px #006799, -1px 0 1px #006799;
255
+ font-weight: 700 !important;
256
+ -webkit-touch-callout: none; /* iOS Safari */
257
+ -webkit-user-select: none; /* Safari */
258
+ -khtml-user-select: none; /* Konqueror HTML */
259
+ -moz-user-select: none; /* Firefox */
260
+ -ms-user-select: none; /* Internet Explorer/Edge */
261
+ user-select: none; /* Non-prefixed version, currently*/
262
+
263
+ }
264
+
265
+ .introjs-button:hover {
266
+ background: #008ec2;
267
+ border-color: #006799;
268
+ color: #fff;
269
+ }
270
+
271
+ .introjs-button:active {
272
+ background: #0073aa;
273
+ border-color: #006799;
274
+ -webkit-box-shadow: inset 0 2px 0 #006799;
275
+ box-shadow: inset 0 2px 0 #006799;
276
+ vertical-align: top;
277
+ }
278
+
279
+ .introjs-button:focus {
280
+ color: #FFFFFF;
281
+ }
282
+
283
+ /* overrides extra padding on button elements in Firefox */
284
+ .introjs-button::-moz-focus-inner {
285
+ padding: 0;
286
+ border: 0;
287
+ }
288
+
289
+ .introjs-skipbutton {
290
+ box-sizing: content-box;
291
+ margin-right: 5px;
292
+ }
293
+
294
+ .introjs-prevbutton {
295
+ -webkit-border-radius: 0.2em 0 0 0.2em;
296
+ -moz-border-radius: 0.2em 0 0 0.2em;
297
+ border-radius: 0.2em 0 0 0.2em;
298
+ border-right: none;
299
+ }
300
+
301
+ .introjs-prevbutton.introjs-fullbutton {
302
+ border: 1px solid #d4d4d4;
303
+ -webkit-border-radius: 0.2em;
304
+ -moz-border-radius: 0.2em;
305
+ border-radius: 0.2em;
306
+ }
307
+
308
+ .introjs-nextbutton {
309
+ -webkit-border-radius: 0 0.2em 0.2em 0;
310
+ -moz-border-radius: 0 0.2em 0.2em 0;
311
+ border-radius: 0 0.2em 0.2em 0;
312
+
313
+ background: #ee8014;
314
+ border-color: #aa6e00 #994700 #9f5b4b;
315
+ -webkit-box-shadow: 0 1px 0 #ffffff;
316
+ box-shadow: 0 1px 0 #ffffff;
317
+ color: #fff;
318
+ text-decoration: none;
319
+ text-shadow: 0 -1px 1px #995500, 1px 0 1px #994700, 0 1px 1px #993800, -1px 0 1px #995500;
320
+ padding: 6px 17px 7px;
321
+ }
322
+
323
+ .introjs-nextbutton:hover {
324
+ background: #cc750b;
325
+ border-color: #764301;
326
+ color: #fff;
327
+ }
328
+
329
+ .introjs-nextbutton:active {
330
+ background: #aa4e00;
331
+ border-color: #993100;
332
+ -webkit-box-shadow: inset 0 2px 0 #993800;
333
+ box-shadow: inset 0 2px 0 #993800;
334
+ vertical-align: top;
335
+ }
336
+
337
+ .introjs-nextbutton.introjs-fullbutton {
338
+ -webkit-border-radius: 0.2em;
339
+ -moz-border-radius: 0.2em;
340
+ border-radius: 0.2em;
341
+ }
342
+
343
+ .introjs-disabled, .introjs-disabled:hover, .introjs-disabled:focus {
344
+ opacity: 0.8;
345
+ }
346
+
347
+ .introjs-hidden {
348
+ display: none;
349
+ }
350
+
351
+ .introjs-bullets {
352
+ text-align: center;
353
+ }
354
+ .introjs-bullets ul {
355
+ box-sizing: content-box;
356
+ clear: both;
357
+ margin: 15px auto 0;
358
+ padding: 0;
359
+ display: inline-block;
360
+ }
361
+ .introjs-bullets ul li {
362
+ box-sizing: content-box;
363
+ list-style: none;
364
+ float: left;
365
+ margin: 0 2px;
366
+ }
367
+ .introjs-bullets ul li a {
368
+ box-sizing: content-box;
369
+ display: block;
370
+ width: 6px;
371
+ height: 6px;
372
+ background: #ccc;
373
+ border-radius: 10px;
374
+ -moz-border-radius: 10px;
375
+ -webkit-border-radius: 10px;
376
+ text-decoration: none;
377
+ cursor: pointer;
378
+ }
379
+ .introjs-bullets ul li a:hover {
380
+ background: #999;
381
+ }
382
+ .introjs-bullets ul li a.active {
383
+ background: #999;
384
+ }
385
+
386
+ .introjs-progress {
387
+ box-sizing: content-box;
388
+ overflow: hidden;
389
+ height: 10px;
390
+ margin: 10px 0 5px 0;
391
+ border-radius: 4px;
392
+ background-color: #ecf0f1
393
+ }
394
+ .introjs-progressbar {
395
+ box-sizing: content-box;
396
+ float: left;
397
+ width: 0%;
398
+ height: 100%;
399
+ font-size: 10px;
400
+ line-height: 10px;
401
+ text-align: center;
402
+ background-color: #08c;
403
+ }
404
+
405
+ .introjsFloatingElement {
406
+ position: absolute;
407
+ height: 0;
408
+ width: 0;
409
+ left: 50%;
410
+ top: 50%;
411
+ }
412
+
413
+ .introjs-fixedTooltip {
414
+ position: fixed;
415
+ }
416
+
417
+ .introjs-hint {
418
+ box-sizing: content-box;
419
+ position: absolute;
420
+ background: transparent;
421
+ width: 20px;
422
+ height: 15px;
423
+ cursor: pointer;
424
+ }
425
+ .introjs-hint:focus {
426
+ border: 0;
427
+ outline: 0;
428
+ }
429
+ .introjs-hidehint {
430
+ display: none;
431
+ }
432
+
433
+ .introjs-fixedhint {
434
+ position: fixed;
435
+ }
436
+
437
+ .introjs-hint:hover > .introjs-hint-pulse {
438
+ border: 5px solid rgba(60, 60, 60, 0.57);
439
+ }
440
+
441
+ .introjs-hint-pulse {
442
+ box-sizing: content-box;
443
+ width: 10px;
444
+ height: 10px;
445
+ border: 5px solid rgba(60, 60, 60, 0.27);
446
+ -webkit-border-radius: 30px;
447
+ -moz-border-radius: 30px;
448
+ border-radius: 30px;
449
+ background-color: rgba(136, 136, 136, 0.24);
450
+ z-index: 10;
451
+ position: absolute;
452
+ -webkit-transition: all 0.2s ease-out;
453
+ -moz-transition: all 0.2s ease-out;
454
+ -ms-transition: all 0.2s ease-out;
455
+ -o-transition: all 0.2s ease-out;
456
+ transition: all 0.2s ease-out;
457
+ }
458
+ .introjs-hint-no-anim .introjs-hint-dot {
459
+ -webkit-animation: none;
460
+ -moz-animation: none;
461
+ animation: none;
462
+ }
463
+ .introjs-hint-dot {
464
+ box-sizing: content-box;
465
+ border: 10px solid rgba(146, 146, 146, 0.36);
466
+ background: transparent;
467
+ -webkit-border-radius: 60px;
468
+ -moz-border-radius: 60px;
469
+ border-radius: 60px;
470
+ height: 50px;
471
+ width: 50px;
472
+ -webkit-animation: introjspulse 3s ease-out;
473
+ -moz-animation: introjspulse 3s ease-out;
474
+ animation: introjspulse 3s ease-out;
475
+ -webkit-animation-iteration-count: infinite;
476
+ -moz-animation-iteration-count: infinite;
477
+ animation-iteration-count: infinite;
478
+ position: absolute;
479
+ top: -25px;
480
+ left: -25px;
481
+ z-index: 1;
482
+ opacity: 0;
483
+ }
484
+
485
+ @-webkit-keyframes introjspulse {
486
+ 0% {
487
+ -webkit-transform: scale(0);
488
+ opacity: 0.0;
489
+ }
490
+ 25% {
491
+ -webkit-transform: scale(0);
492
+ opacity: 0.1;
493
+ }
494
+ 50% {
495
+ -webkit-transform: scale(0.1);
496
+ opacity: 0.3;
497
+ }
498
+ 75% {
499
+ -webkit-transform: scale(0.5);
500
+ opacity: 0.5;
501
+ }
502
+ 100% {
503
+ -webkit-transform: scale(1);
504
+ opacity: 0.0;
505
+ }
506
+ }
507
+
508
+ @-moz-keyframes introjspulse {
509
+ 0% {
510
+ -moz-transform: scale(0);
511
+ opacity: 0.0;
512
+ }
513
+ 25% {
514
+ -moz-transform: scale(0);
515
+ opacity: 0.1;
516
+ }
517
+ 50% {
518
+ -moz-transform: scale(0.1);
519
+ opacity: 0.3;
520
+ }
521
+ 75% {
522
+ -moz-transform: scale(0.5);
523
+ opacity: 0.5;
524
+ }
525
+ 100% {
526
+ -moz-transform: scale(1);
527
+ opacity: 0.0;
528
+ }
529
+ }
530
+
531
+ @keyframes introjspulse {
532
+ 0% {
533
+ transform: scale(0);
534
+ opacity: 0.0;
535
+ }
536
+ 25% {
537
+ transform: scale(0);
538
+ opacity: 0.1;
539
+ }
540
+ 50% {
541
+ transform: scale(0.1);
542
+ opacity: 0.3;
543
+ }
544
+ 75% {
545
+ transform: scale(0.5);
546
+ opacity: 0.5;
547
+ }
548
+ 100% {
549
+ transform: scale(1);
550
+ opacity: 0.0;
551
+ }
552
+ }
css/style.css CHANGED
@@ -46,6 +46,7 @@ h2 .button {
46
  color: #9d8a7e;
47
  padding: 3px 5px;
48
  margin-right: 4px;
 
49
  }
50
 
51
  .info-class >span {
@@ -65,14 +66,17 @@ h2 .button {
65
  -webkit-border-top-left-radius: 0;
66
  min-width: 200px;
67
  padding: 10px;
68
- background-color: rgba(0, 0, 0, 0.56);
69
- color: #FFFFFF;
70
  font-weight: 200;
 
 
71
  }
72
 
73
  .info-class:hover >i {
74
- background-color: rgba(0, 0, 0, 0.56);
75
- color: rgba(255, 255, 255, 0.5);
 
76
 
77
  border-top-left-radius: 3px;
78
  -moz-border-top-left-radius: 3px;
@@ -82,6 +86,9 @@ h2 .button {
82
  -moz-border-top-right-radius: 3px;
83
  -webkit-border-top-right-radius: 3px;
84
 
 
 
 
85
  }
86
 
87
  .info-class:hover >span {
@@ -307,6 +314,25 @@ h2 .button {
307
  background-color: blue;
308
  }
309
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
310
  .button-row .button-color-palet {
311
  border: 1px solid #ccc;
312
  -webkit-box-shadow: 0 1px 0 #ccc;
@@ -359,6 +385,11 @@ h2 .button {
359
  height: 24px;
360
  }
361
 
 
 
 
 
 
362
  .button-preview {
363
  border-radius: 50%;
364
  width: 56px;
@@ -450,7 +481,8 @@ h2 .button {
450
  padding-top: 15px;
451
  }
452
 
453
- .form-table .r-sit .after-input {
 
454
  position: absolute;
455
  right: 11px;
456
  top: 20px;
@@ -458,10 +490,20 @@ h2 .button {
458
  line-height: 35px;
459
  }
460
 
461
- .form-table .r-sit small.after-input {
 
 
 
 
 
 
462
  top: 17px;
463
  }
464
 
 
 
 
 
465
  .form-table fieldset {
466
  border: 1px solid #d7d7d7;
467
  background-color: #f7f7f7;
@@ -510,6 +552,15 @@ h2 .button {
510
  left: 200px;
511
  }
512
 
 
 
 
 
 
 
 
 
 
513
  /* Categories */
514
  .categories-load-more {
515
  display: block;
@@ -586,7 +637,7 @@ h2 .button {
586
  box-shadow: 0px 0px 10px rgba(183, 183, 183, 0.18);
587
  min-width: 250px;
588
  border: 1px solid #e7e7e7;
589
- z-index: 999;
590
  }
591
 
592
  .fontawesome-searcher .fontawesome-searcher-searchbar {
46
  color: #9d8a7e;
47
  padding: 3px 5px;
48
  margin-right: 4px;
49
+ border: 1px solid rgba(0, 0, 0, 0);
50
  }
51
 
52
  .info-class >span {
66
  -webkit-border-top-left-radius: 0;
67
  min-width: 200px;
68
  padding: 10px;
69
+ background-color: #FFFFFF;
70
+ color: #333333;
71
  font-weight: 200;
72
+ line-height: 20px;
73
+ border: 1px solid #d2d2d2;
74
  }
75
 
76
  .info-class:hover >i {
77
+ background-color: #FFFFFF;
78
+ color: #f08216;
79
+ border-color: #d2d2d2;
80
 
81
  border-top-left-radius: 3px;
82
  -moz-border-top-left-radius: 3px;
86
  -moz-border-top-right-radius: 3px;
87
  -webkit-border-top-right-radius: 3px;
88
 
89
+ position: relative;
90
+ z-index: 800;
91
+ border-bottom: 0;
92
  }
93
 
94
  .info-class:hover >span {
314
  background-color: blue;
315
  }
316
 
317
+ .button-row table.settings-row-type tr td {
318
+ vertical-align: top;
319
+ }
320
+
321
+ .button-row table.settings-row-type .input_error {
322
+ background-color: #ffcfcf;
323
+ color: #ff0000;
324
+ border: 1px solid #cc6a6a;
325
+ padding: 10px 20px;
326
+ margin-top: 8px;
327
+ border-radius: 2px;
328
+ -webkit-border-radius: 2px;
329
+ -moz-border-radius: 2px;
330
+ }
331
+
332
+ .button-row table.settings-row-type .input_error p {
333
+ margin: 0;
334
+ }
335
+
336
  .button-row .button-color-palet {
337
  border: 1px solid #ccc;
338
  -webkit-box-shadow: 0 1px 0 #ccc;
385
  height: 24px;
386
  }
387
 
388
+ .button-row .extra_info {
389
+ padding: 5px;
390
+ line-height: 20px;
391
+ }
392
+
393
  .button-preview {
394
  border-radius: 50%;
395
  width: 56px;
481
  padding-top: 15px;
482
  }
483
 
484
+ .form-table .r-sit .after-input,
485
+ .form-table .r-sit .before-input {
486
  position: absolute;
487
  right: 11px;
488
  top: 20px;
490
  line-height: 35px;
491
  }
492
 
493
+ .form-table .r-sit .before-input {
494
+ right: auto;
495
+ left: 11px;
496
+ }
497
+
498
+ .form-table .r-sit small.after-input,
499
+ .form-table .r-sit small.before-input {
500
  top: 17px;
501
  }
502
 
503
+ .form-table .r-sit .before-input-padding {
504
+ padding-left: 40px;
505
+ }
506
+
507
  .form-table fieldset {
508
  border: 1px solid #d7d7d7;
509
  background-color: #f7f7f7;
552
  left: 200px;
553
  }
554
 
555
+ .form-table .wp-picker-container {
556
+ position: relative;
557
+ }
558
+
559
+ .form-table .wp-picker-container.wp-picker-active .wp-picker-holder {
560
+ position: absolute;
561
+ z-index: 99999;
562
+ }
563
+
564
  /* Categories */
565
  .categories-load-more {
566
  display: block;
637
  box-shadow: 0px 0px 10px rgba(183, 183, 183, 0.18);
638
  min-width: 250px;
639
  border: 1px solid #e7e7e7;
640
+ z-index: 99999999;
641
  }
642
 
643
  .fontawesome-searcher .fontawesome-searcher-searchbar {
freemius/.gitignore ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ output/
2
+ assets/img/icon.*
3
+
4
+ pootle-page-builder.png
5
+ rating-widget.jpg
6
+ hello-dolly-pro.png
7
+ woocommerce-fiscalita-italiana.png
8
+ assets/img/content-aware-sidebars.png
9
+ assets/img/custom-post-type-ui.png
10
+ .idea
11
+ nextgen-gallery.png
12
+ node_modules
13
+ languages/_freemius-en.mo
14
+ languages/_freemius-en.po
15
+ foobox-image-lightbox.png
16
+ transifex-config.json
js/buttonizer.js CHANGED
@@ -26,7 +26,7 @@ var buttonizer = {
26
  this.settings.exitIntent = options.exitIntent;
27
  this.settings.exitIntentText = options.exitIntentText;
28
 
29
- document.addEventListener('DOMContentLoaded', this.ready);
30
  },
31
  ready: function() {
32
  if(document.getElementById("buttonizer-sys") != null) {
@@ -42,6 +42,10 @@ var buttonizer = {
42
  }, false);
43
 
44
  document.getElementById("buttonizer-sys").addEventListener("click", function() {
 
 
 
 
45
  if(buttonizer.settings.buttonizerOpened == true) {
46
  document.getElementById("buttonizer-sys").className = 'buttonizer_inner';
47
  buttonizer.settings.buttonizerOpened = false;
26
  this.settings.exitIntent = options.exitIntent;
27
  this.settings.exitIntentText = options.exitIntentText;
28
 
29
+ this.ready();
30
  },
31
  ready: function() {
32
  if(document.getElementById("buttonizer-sys") != null) {
42
  }, false);
43
 
44
  document.getElementById("buttonizer-sys").addEventListener("click", function() {
45
+ if(document.querySelector("#buttonizer-sys .buttonizer_head.onlyone") != null) {
46
+ return;
47
+ }
48
+
49
  if(buttonizer.settings.buttonizerOpened == true) {
50
  document.getElementById("buttonizer-sys").className = 'buttonizer_inner';
51
  buttonizer.settings.buttonizerOpened = false;
js/dashboard.js CHANGED
@@ -21,6 +21,7 @@ var buttonizer = {
21
  buttonizer.overwriteFavIcon();
22
  buttonizer.faInit();
23
  buttonizer.initButtons();
 
24
 
25
  // WP color picker
26
  jQuery('#button_unpushed').wpColorPicker();
@@ -74,8 +75,10 @@ var buttonizer = {
74
 
75
  // Hash
76
  if(document.location.hash != "") {
77
- var tabKey = document.location.hash;
78
- buttonizer.tabNavigate(tabKey.replace("#tab_", ""));
 
 
79
  }
80
 
81
  jQuery(".buttonizer-click-to-pro").each(function() {
@@ -335,6 +338,7 @@ var buttonizer = {
335
  scroll: true,
336
  scrollSensitivity: 30,
337
  scrollSpeed: 10,
 
338
 
339
  onSort: function (evt) {
340
  jQuery("#" + evt.clone.children[0].id + " .row-info").css({
@@ -470,6 +474,8 @@ var buttonizer = {
470
  // Image icon handler
471
  buttonizer.iconImageHandler(rowObj.find(".icon-or-image"));
472
  buttonizer.colorPaletHandler(rowObj.find(".button-color-palet"));
 
 
473
  }
474
  });
475
  },
@@ -650,6 +656,13 @@ var buttonizer = {
650
  jQuery("#btn_row_"+ buttonId + " input.pushed").attr("name", buttonizer.settings.wpButtonSaveKey + "[button_"+ buttonId +"_colors_pushed]");
651
  jQuery("#btn_row_"+ buttonId + " input.icon").attr("name", buttonizer.settings.wpButtonSaveKey + "[button_"+ buttonId +"_colors_icon]");
652
 
 
 
 
 
 
 
 
653
  // Show on phone
654
  jQuery("#btn_row_"+ buttonId + " .button_showonphone")
655
  .attr("name", buttonizer.settings.wpButtonSaveKey + "[button_"+ buttonId +"_show_on_phone]")
@@ -713,6 +726,176 @@ var buttonizer = {
713
  jQuery("#btn_row_"+ buttonId + " .mobiledesktop.desktop-button").attr("href", "javascript:buttonizer.toggleDesktop("+ buttonId +")");
714
  },
715
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
716
  createWindow: function(data) {
717
  this.settings.windowNumber++;
718
  var currentWindow = this.settings.windowNumber;
@@ -741,6 +924,10 @@ var buttonizer = {
741
  data.onConfirm = function() {};
742
  }
743
 
 
 
 
 
744
  if(!data.onClose) {
745
  data.onClose = function() {};
746
  }
@@ -773,6 +960,10 @@ var buttonizer = {
773
  data.onConfirm();
774
  }
775
 
 
 
 
 
776
  data.onClose();
777
  })
778
  },
@@ -879,6 +1070,305 @@ var buttonizer = {
879
  buttonizer.faSettings.currentSelect.val(icon).change();
880
  buttonizer.faSettings.currentObject.html("Icon: "+ icon +" &nbsp; <i class=\"fa "+ icon +"\"></i>");
881
  jQuery("#" + buttonizer.faSettings.currentObject.attr("id")).click();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
882
  }
883
  }
884
 
21
  buttonizer.overwriteFavIcon();
22
  buttonizer.faInit();
23
  buttonizer.initButtons();
24
+ buttonizer.initIntro();
25
 
26
  // WP color picker
27
  jQuery('#button_unpushed').wpColorPicker();
75
 
76
  // Hash
77
  if(document.location.hash != "") {
78
+ if(document.location.hash != "#buttonizer-tour") {
79
+ var tabKey = document.location.hash;
80
+ buttonizer.tabNavigate(tabKey.replace("#tab_", ""));
81
+ }
82
  }
83
 
84
  jQuery(".buttonizer-click-to-pro").each(function() {
338
  scroll: true,
339
  scrollSensitivity: 30,
340
  scrollSpeed: 10,
341
+ handle: ".drag-handle",
342
 
343
  onSort: function (evt) {
344
  jQuery("#" + evt.clone.children[0].id + " .row-info").css({
474
  // Image icon handler
475
  buttonizer.iconImageHandler(rowObj.find(".icon-or-image"));
476
  buttonizer.colorPaletHandler(rowObj.find(".button-color-palet"));
477
+
478
+ buttonizer.buttonTypeHandler(rowObj);
479
  }
480
  });
481
  },
656
  jQuery("#btn_row_"+ buttonId + " input.pushed").attr("name", buttonizer.settings.wpButtonSaveKey + "[button_"+ buttonId +"_colors_pushed]");
657
  jQuery("#btn_row_"+ buttonId + " input.icon").attr("name", buttonizer.settings.wpButtonSaveKey + "[button_"+ buttonId +"_colors_icon]");
658
 
659
+ // Button action
660
+ jQuery("#btn_row_"+ buttonId + " .button_action").attr("name", buttonizer.settings.wpButtonSaveKey + "[button_"+ buttonId +"_action]");
661
+
662
+ // Button value
663
+ jQuery("#btn_row_"+ buttonId + " .button_input").attr("name", buttonizer.settings.wpButtonSaveKey + "[button_"+ buttonId +"_url]");
664
+
665
+
666
  // Show on phone
667
  jQuery("#btn_row_"+ buttonId + " .button_showonphone")
668
  .attr("name", buttonizer.settings.wpButtonSaveKey + "[button_"+ buttonId +"_show_on_phone]")
726
  jQuery("#btn_row_"+ buttonId + " .mobiledesktop.desktop-button").attr("href", "javascript:buttonizer.toggleDesktop("+ buttonId +")");
727
  },
728
 
729
+ /* Button type handler */
730
+ buttonTypeHandler: function(rowObj) {
731
+ var currentButtonType = rowObj.find(".button_action");
732
+ var currentButtonInput = rowObj.find(".button_input");
733
+ var newTab = rowObj.find(".setting-new-tab");
734
+
735
+ var isInitializing = true;
736
+
737
+ var waitUntilTriggering = setTimeout(function() {});
738
+
739
+ // The button type
740
+ currentButtonType.change(function() {
741
+ isInitializing = true;
742
+ checkInput();
743
+ });
744
+
745
+ currentButtonInput.keyup(function() {
746
+ clearInterval(waitUntilTriggering);
747
+
748
+ waitUntilTriggering = setTimeout(function(){
749
+ isInitializing = true;
750
+ checkInput();
751
+ }, 250);
752
+ });
753
+
754
+ currentButtonInput.change(checkInput);
755
+
756
+ //
757
+ function checkInput() {
758
+ var inputError = false;
759
+ var currentAction = currentButtonType.val();
760
+ var currentValueText = currentButtonInput.val();
761
+
762
+ // if(!isInitializing && currentValueText == '') {
763
+ // isInitializing = false;
764
+ // rowObj.find(".input_error").html('Hello there... You left the input empty here... Do you want to fill him up? It\'s up to you.').show();
765
+ // return;
766
+ // }
767
+
768
+ if(currentButtonType.val() == 'url') {
769
+ newTab.show();
770
+ }else{
771
+ newTab.hide();
772
+ }
773
+
774
+ if(currentAction == 'url' && currentValueText == '#') {
775
+ inputError = true;
776
+ rowObj.find(".input_error").html('<p>You only have #... That doesn\'t open anything...</p>');
777
+ }else if(currentAction == 'url' && !buttonizer.buttonTypeInputController(currentValueText, 'url')) {
778
+ inputError = true;
779
+ rowObj.find(".input_error").html('<p>This looks like an invalid URL. The button may not work as expected.</p><p>&nbsp;</p><p>Do you miss <b>http://</b> or <b>https://</b>? A space somewhere on the wrong place?</p>');
780
+ }
781
+
782
+ if((currentAction == 'phone' || currentAction == 'whatsapp') && !/^(?=.*\d)[\d ]+$/.test(currentValueText)) {
783
+ inputError = true;
784
+ rowObj.find(".input_error").html('Invalid phone number. Please use only the number format. Omit any zeroes, brackets or dashes when adding the phone number in international format.');
785
+ }
786
+
787
+ if(currentAction == 'mail' && !buttonizer.buttonTypeInputController(currentValueText, 'mail')) {
788
+ inputError = true;
789
+ rowObj.find(".input_error").html('<p>This looks like an invalid mail address. Make sure the mail address looks like <b><i>user</i>@domain.com</b></p>');
790
+ }
791
+
792
+
793
+ /* Premium Code Stripped by Freemius */
794
+
795
+
796
+ if(!isInitializing && currentValueText == '') {
797
+ rowObj.find(".input_error").html('Uhm... You forgot something to fill in...').show();
798
+ return;
799
+ }
800
+
801
+ // Stay here
802
+ if(!isInitializing) {
803
+ if(currentAction != 'phone' && currentAction != 'whatsapp' && /^(?=.*\d)[\d ]+$/.test(currentValueText)) {
804
+ buttonizer.createWindow({
805
+ title: 'Uhm? Shall I call you?',
806
+ text:
807
+ '<p>It\'s looks like you are having a phone number as value. Do you want to let this button behave as a call button?</p>' +
808
+ '<p>If it\'s not a phone number, you can click \'No thanks\'. If you click \'Yes please\' I will change it for you.</p>',
809
+
810
+ confirmText: 'Yes please',
811
+ canCancel: true,
812
+ cancelText: 'No thanks',
813
+
814
+ onConfirm: function() {
815
+ currentButtonType.val('phone');
816
+ rowObj.find(".input_error").hide();
817
+ }
818
+ });
819
+ }
820
+
821
+ if(currentAction != 'url' && buttonizer.buttonTypeInputController(currentValueText, 'url')) {
822
+ buttonizer.createWindow({
823
+ title: 'Uhm? WWW.?',
824
+ text:
825
+ '<p>It\'s looks like you are having a website URL as value. Do you want to let this button behave as a link?</p>' +
826
+ '<p>If you just about to change that, you can click \'No thanks\'. If you click \'Yes please\' I will change it for you.</p>',
827
+
828
+ confirmText: 'Yes please',
829
+ canCancel: true,
830
+ cancelText: 'No thanks',
831
+
832
+ onConfirm: function() {
833
+ currentButtonType.val('url');
834
+ rowObj.find(".input_error").hide();
835
+ }
836
+ });
837
+ }
838
+
839
+ if(currentAction != 'mail' && buttonizer.buttonTypeInputController(currentValueText, 'mail')) {
840
+ buttonizer.createWindow({
841
+ title: 'Uhm? That\'s a mail address?',
842
+ text:
843
+ '<p>It\'s looks like you are having a mail address as value. Do you want to change the button action to open the mail program?</p>' +
844
+ '<p>If you just about to change that, you can click \'No thanks\'. If you click \'Yes please\' I will change it for you.</p>',
845
+
846
+ confirmText: 'Yes please',
847
+ canCancel: true,
848
+ cancelText: 'No thanks',
849
+
850
+ onConfirm: function() {
851
+ currentButtonType.val('mail');
852
+ currentButtonInput.val(currentValueText.replace("mailto:", ""));
853
+ rowObj.find(".input_error").hide();
854
+ }
855
+ });
856
+ }
857
+
858
+
859
+ /* Premium Code Stripped by Freemius */
860
+
861
+ }
862
+
863
+ if(inputError) {
864
+ rowObj.find(".input_error").show();
865
+ }else{
866
+ rowObj.find(".input_error").hide();
867
+ }
868
+
869
+ isInitializing = false;
870
+ };
871
+
872
+ // Start
873
+ checkInput();
874
+ },
875
+
876
+ buttonTypeInputController: function(inputValue, action) {
877
+ var websiteUrlPattern = /(http|https):\/\/(\w+:{0,1}\w*)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%!\-\/]))?/;
878
+ var mailUrlPattern = /^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i;
879
+
880
+ // URL
881
+ if(action == 'url') {
882
+ if(!websiteUrlPattern.test(inputValue) && !websiteUrlPattern.substring(0, 1) == '#') {
883
+ return false;
884
+ } else {
885
+ return true;
886
+ }
887
+ }else
888
+
889
+ // E-mail
890
+ if(action == 'mail') {
891
+ if(!mailUrlPattern.test(inputValue) && inputValue.indexOf("mailto:") == -1) {
892
+ return false;
893
+ } else {
894
+ return true;
895
+ }
896
+ }
897
+ },
898
+
899
  createWindow: function(data) {
900
  this.settings.windowNumber++;
901
  var currentWindow = this.settings.windowNumber;
924
  data.onConfirm = function() {};
925
  }
926
 
927
+ if(!data.onCancel) {
928
+ data.onCancel = function() {};
929
+ }
930
+
931
  if(!data.onClose) {
932
  data.onClose = function() {};
933
  }
960
  data.onConfirm();
961
  }
962
 
963
+ if(jQuery(this).hasClass("cancel")) {
964
+ data.onCancel();
965
+ }
966
+
967
  data.onClose();
968
  })
969
  },
1070
  buttonizer.faSettings.currentSelect.val(icon).change();
1071
  buttonizer.faSettings.currentObject.html("Icon: "+ icon +" &nbsp; <i class=\"fa "+ icon +"\"></i>");
1072
  jQuery("#" + buttonizer.faSettings.currentObject.attr("id")).click();
1073
+ },
1074
+
1075
+ /* intro */
1076
+ initIntro: function() {
1077
+ var openedDesign = false;
1078
+
1079
+ if(RegExp("welcome-splash", "gi").test(document.location.href)) {
1080
+ buttonizer.createWindow({
1081
+ title: 'Welcome to Buttonizer',
1082
+ text:
1083
+ '<img src="'+ jQuery(".buttonizer-logo img").attr("src").replace("logo.png", "plugin-icon.png") +'" width="100" align="left" style="margin-right: 20px; margin-bottom: 50px;" />' +
1084
+ '<p>We are pleasured to welcome you to Buttonizer!</p>' +
1085
+ '<p>We\'ve created a tour for our new users, would you like to take the tour? We will make your first Buttonizer button there and show some things how it works!</p>' +
1086
+ '<p>Would you like to take the tour?</p>',
1087
+
1088
+ confirmText: 'Yes please <i class="fa fa-chevron-right" style="margin-left: 10px; vertical-align: middle;" aria-hidden="true"></i>',
1089
+ canCancel: true,
1090
+ cancelText: 'No thanks, I know how it works',
1091
+
1092
+ onConfirm: function() {
1093
+ document.location.href = '?page=Buttonizer&tab=buttonizer_general_settings#buttonizer-tour';
1094
+ },
1095
+
1096
+ onCancel: function() {
1097
+ document.location.href = '?page=Buttonizer';
1098
+ }
1099
+ });
1100
+ }
1101
+
1102
+ // Check if we need to send the user to the next page
1103
+ if(RegExp("buttonizer_tour_ready=toStep2", "gi").test(document.cookie)) {
1104
+ var d = new Date();
1105
+ d.setTime(d.getTime() - 10);
1106
+ document.cookie = "buttonizer_tour_ready=toStep2;expires="+ d.toUTCString() + ";path=/";
1107
+
1108
+ document.location.href = './admin.php?page=Buttonizer&tab=buttonizer_buttons#buttonizer-tour';
1109
+ return;
1110
+ }
1111
+
1112
+ jQuery("#take-the-tour").click(function() {
1113
+ setTimeout(function() {
1114
+ if (RegExp("buttonizer_general_settings", "gi").test(window.location.href)) {
1115
+ buttonizer.initIntro();
1116
+ }
1117
+ }, 250);
1118
+ });
1119
+
1120
+ if (RegExp("buttonizer-tour", "gi").test(window.location.href)) {
1121
+
1122
+ // Page one: General settings
1123
+ if(document.location.href.indexOf('buttonizer_general_settings') != -1) {
1124
+ var buttonizer_tour_1 = buttonizer.startTour('design');
1125
+ buttonizer.tabNavigate('design');
1126
+
1127
+ buttonizer_tour_1.onbeforechange(function(targetElement) {
1128
+ if (buttonizer_tour_1._currentStep == 5 && buttonizer_tour_1._direction == "forward") {
1129
+ buttonizer.tabNavigate('placing');
1130
+ openedDesign = true;
1131
+ }else if(buttonizer_tour_1._currentStep == 4 && buttonizer_tour_1._direction == "backward") {
1132
+ buttonizer.tabNavigate('design');
1133
+
1134
+ openedDesign = false;
1135
+ }
1136
+ }).oncomplete(function() {
1137
+ document.querySelector('.button.savebutton').click();
1138
+ }).setOptions({
1139
+ doneLabel: 'Save &rarr;'
1140
+ }).start();
1141
+
1142
+ jQuery("#tab_container_placing").click(function() {
1143
+ buttonizer_tour_1.nextStep();
1144
+ });
1145
+
1146
+ jQuery('.button.savebutton').click(function() {
1147
+ var d = new Date();
1148
+ d.setTime(d.getTime() + (2*24*60*60*1000));
1149
+ document.cookie = "buttonizer_tour_ready=toStep2;expires="+ d.toUTCString() + ";path=/";
1150
+ });
1151
+ }
1152
+
1153
+ if(document.location.href.indexOf('buttonizer_buttons') != -1) {
1154
+ var buttonizer_tour_2 = buttonizer.startTour('buttons');
1155
+ var buttonCreated = true;
1156
+
1157
+ buttonizer_tour_2.onchange(function(targetElement) {
1158
+ if(buttonizer_tour_2._currentStep == 4) {
1159
+ jQuery("#new-button").click();
1160
+ }
1161
+ }).setOptions({
1162
+ doneLabel: 'Next',
1163
+ hidePrev: true,
1164
+ hideNext: true,
1165
+ }).start();
1166
+
1167
+ // Create button
1168
+ var buttonCreated = false;
1169
+ jQuery("#new-button").click(function() {
1170
+ if(buttonCreated) {
1171
+ alert("Please finish the tour first.")
1172
+ return;
1173
+ }
1174
+ buttonCreated = true;
1175
+ buttonizer.addRow();
1176
+
1177
+ setTimeout(function() {
1178
+ var id = jQuery("#button-rows .button-row.opened").attr("id");
1179
+ buttonizer_tour_2.exit(true);
1180
+
1181
+ var buttonizer_tour_3 = buttonizer.startTour('edit-button', id);
1182
+
1183
+ buttonizer_tour_3.onchange(function(targetElement) {
1184
+ if(buttonizer_tour_3._currentStep == 1) {
1185
+ // buttonizer_tour_3.nextStep();
1186
+ }
1187
+ }).setOptions({
1188
+ doneLabel: 'Finish tour',
1189
+ hidePrev: true,
1190
+ hideNext: true,
1191
+ }).start();
1192
+ }, 400);
1193
+ });
1194
+ }
1195
+ }
1196
+ },
1197
+
1198
+ startTour: function(type, newButtonId) {
1199
+ var intro = introJs();
1200
+
1201
+ if(!newButtonId) {
1202
+ newButtonId = false;
1203
+ }
1204
+
1205
+ intro.setOption('tooltipPosition', 'top');
1206
+ intro.setOptions({
1207
+ showBullets: false,
1208
+ exitOnOverlayClick: false,
1209
+ skipLabel: 'Exit tour'
1210
+ });
1211
+
1212
+ if(type == 'design') {
1213
+ intro.setOptions({
1214
+ steps: [
1215
+ {
1216
+ intro: "Let's start the tour through Buttonizer. You can skip this every second."
1217
+ },
1218
+ {
1219
+ element: document.querySelector('.intro-styling'),
1220
+ intro: "Set the default colors of your Floating Action Button. We recommend to use the colors of your corporate identity.",
1221
+ position: 'top'
1222
+ },
1223
+ {
1224
+ element: document.querySelector('.intro-icon'),
1225
+ intro: "Select the main icon. This icon will appear when you have multiple floating action buttons on one page (So you can create a Floating Menu).<br><br>When you have PRO featues, you will be able to choose a custom image.",
1226
+ position: 'top'
1227
+ },
1228
+ {
1229
+ element: document.querySelector('.intro-label'),
1230
+ intro: "Optional: Add text If you want to have a label next to your button to explain the button action like 'Click here' or 'Contact options'.",
1231
+ position: 'top'
1232
+ },
1233
+ {
1234
+ element: document.querySelector('#tab_container_placing'),
1235
+ intro: "Click here to get to the position and animation page",
1236
+ position: 'right'
1237
+ },
1238
+ {
1239
+ element: document.querySelector('.intro-position'),
1240
+ intro: "D​ecide where you want to place your floating action buttons. 5% right means 5% from the right side of the screen. The default setting is bottom 5% and right 5%. It will place itself on the right corner of your screen (mobile & desktop).",
1241
+ position: 'top'
1242
+ },
1243
+ {
1244
+ element: document.querySelector('.intro-animaton'),
1245
+ intro: "The Button animation option is only relevant if you are going to add multiple floating action buttons on one page (create a floating menu). This options lets you choose how the different buttons will 'pop' open.",
1246
+ position: 'top'
1247
+ },
1248
+ {
1249
+ element: document.querySelector('.intro-attention'),
1250
+ intro: "If you want your floating action button to have extra attention from your website visitors you can add a attention animation. The button will make a specific 'move' each 10 seconds.",
1251
+ position: 'top'
1252
+ },
1253
+ {
1254
+ element: document.querySelector('.button.savebutton'),
1255
+ intro: "For now, we are finished here. Click the save button.",
1256
+ position: 'left'
1257
+ }
1258
+ ]});
1259
+ }else if(type == 'buttons') {
1260
+ intro.setOptions({
1261
+ steps: [
1262
+ {
1263
+ element: document.querySelector('#new-button'),
1264
+ intro: "Great! You are ready to make your first Floating Action Buttons. As an example we will make a Click-to-Call button. To start click on 'New Button'.",
1265
+ position: 'left'
1266
+ },
1267
+ {
1268
+ element: document.querySelector('#new-button'),
1269
+ intro: "Waiting on you for clicking the button at the right... It says '+ New button'",
1270
+ position: 'left'
1271
+ },
1272
+ {
1273
+ element: document.querySelector('#new-button'),
1274
+ intro: "Hello? Please click that button...",
1275
+ position: 'left'
1276
+ },
1277
+ {
1278
+ element: document.querySelector('#new-button'),
1279
+ intro: "Here, on the right... If you won't click it, I will click it >:-)",
1280
+ position: 'left'
1281
+ },
1282
+ {
1283
+ element: document.querySelector('#new-button'),
1284
+ intro: "Okay. Nothing to say.",
1285
+ position: 'bottom'
1286
+ },
1287
+ ]});
1288
+ }
1289
+ else if(type == 'edit-button') {
1290
+ intro.setOptions({
1291
+ steps: [
1292
+ {
1293
+ element: document.querySelector('#' + newButtonId + " .row-info"),
1294
+ intro: "This is the button-quick info bar.<br /><br />When you have multiple buttons, you can drag them to position them on the right place.",
1295
+ position: 'bottom',
1296
+ disableInteraction: true
1297
+ },
1298
+ {
1299
+ element: document.querySelector('#' + newButtonId + " .row-info .row-title"),
1300
+ intro: "This is the button title. Your webvisitors won't see this name, this is just for here. We will change the name in 2 steps.",
1301
+ position: 'right',
1302
+ disableInteraction: true
1303
+ },
1304
+ {
1305
+ element: document.querySelector('#' + newButtonId + " .row-info .desktop-button"),
1306
+ intro: "This is a quick-button to enable the button on desktop. When it's blue it's enabled. When grey it's disabled for desktop.",
1307
+ position: 'left',
1308
+ disableInteraction: true
1309
+ },
1310
+ {
1311
+ element: document.querySelector('#' + newButtonId + " .row-info .mobile-button"),
1312
+ intro: "Like we have one for desktop, this one is for mobile.",
1313
+ position: 'left',
1314
+ disableInteraction: true
1315
+ },
1316
+ {
1317
+ element: document.querySelector('#' + newButtonId + " .button_title"),
1318
+ intro: "Create a descend name for your Button like 'Call Buttons Sales department'. Your webvisitors won't see this name. It will also appear on Google Analytics on your 'event tracking' section so you know for each button how many times it is clicked on.",
1319
+ position: 'bottom'
1320
+ },
1321
+ {
1322
+ element: document.querySelector('#' + newButtonId + " .button-pdr"),
1323
+ intro: "Choose a appropriate icon or upload your own. Type 'phone' and select the phone icon to make a call button.",
1324
+ position: 'bottom'
1325
+ },
1326
+ {
1327
+ element: document.querySelector('#' + newButtonId + " .button_textfield"),
1328
+ intro: "Optional - Add text If you want to have a label next to your button to explain the button action like 'Call our sales department'. This label will be shown next to the button.",
1329
+ position: 'bottom'
1330
+ },
1331
+ {
1332
+ element: document.querySelector('#' + newButtonId + " .button-action"),
1333
+ intro: "Choose the appropriate click action. When you want to add a click-to-call button select the option phone-number. Type in the phone-number you want your visitors to call when clicking on the button.",
1334
+ position: 'bottom'
1335
+ },
1336
+ {
1337
+ element: document.querySelector('#' + newButtonId + " #button_category"),
1338
+ intro: "Premium function - By default the button is shown on every page. If you want to show your button on selected pages you can add 'page categories'. You have to have a premium account if you want to do this.",
1339
+ position: 'bottom'
1340
+ },
1341
+ {
1342
+ element: document.querySelector('#' + newButtonId + " .show-mobile-btn"),
1343
+ intro: "Decide if your button is shown on mobile, desktop or both by selecting the show on mobile and desktop functions. If you don't select at least one of both the button won't show on your website. In the case of a call button we recommend only selecting the option 'show on mobile'. ",
1344
+ position: 'top'
1345
+ },
1346
+ {
1347
+ element: document.querySelector('#' + newButtonId + " .show-desktop-btn"),
1348
+ intro: "Do you like to enable this button on desktop too?",
1349
+ position: 'bottom'
1350
+ },
1351
+ {
1352
+ element: document.querySelector('#' + newButtonId + " .must-save-button"),
1353
+ intro: "This is a quick Save button. It will save all buttons witch has changes. When the button has no changes, it says 'live'.",
1354
+ position: 'left',
1355
+ disableInteraction: true
1356
+ },
1357
+ {
1358
+ intro: "Congratzz! Your first Floating Action button is ready! Click on save and take a look at the result. Go to your website on your mobile phone and test your button.",
1359
+ },
1360
+ {
1361
+ intro: "Now it's time to get creative. Add another button on your website like a navigation button or just a url like your contact page or maybe even opening Facebook Messenger. By adding a second page you will see that your floating menu icon will appear and the animation you selected (In General Settings) when you click on the button.",
1362
+ },
1363
+ {
1364
+ element: document.querySelector('h2 .savebutton'),
1365
+ intro: "Do you have creative ideas or do you need support? Feel free to contact us! Push the save button to finish the tour.",
1366
+ position: 'left',
1367
+ },
1368
+ ]});
1369
+ }
1370
+
1371
+ return intro;
1372
  }
1373
  }
1374
 
js/intro.js ADDED
@@ -0,0 +1,2139 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Intro.js v2.7.0
3
+ * https://github.com/usablica/intro.js
4
+ *
5
+ * Copyright (C) 2017 Afshin Mehrabani (@afshinmeh)
6
+ */
7
+
8
+ (function (root, factory) {
9
+ if (typeof exports === 'object') {
10
+ // CommonJS
11
+ factory(exports);
12
+ } else if (typeof define === 'function' && define.amd) {
13
+ // AMD. Register as an anonymous module.
14
+ define(['exports'], factory);
15
+ } else {
16
+ // Browser globals
17
+ factory(root);
18
+ }
19
+ } (this, function (exports) {
20
+ //Default config/variables
21
+ var VERSION = '2.7.0';
22
+
23
+ /**
24
+ * IntroJs main class
25
+ *
26
+ * @class IntroJs
27
+ */
28
+ function IntroJs(obj) {
29
+ this._targetElement = obj;
30
+ this._introItems = [];
31
+
32
+ this._options = {
33
+ /* Next button label in tooltip box */
34
+ nextLabel: 'Next &rarr;',
35
+ /* Previous button label in tooltip box */
36
+ prevLabel: '&larr; Back',
37
+ /* Skip button label in tooltip box */
38
+ skipLabel: 'Skip',
39
+ /* Done button label in tooltip box */
40
+ doneLabel: 'Done',
41
+ /* Hide previous button in the first step? Otherwise, it will be disabled button. */
42
+ hidePrev: false,
43
+ /* Hide next button in the last step? Otherwise, it will be disabled button. */
44
+ hideNext: false,
45
+ /* Default tooltip box position */
46
+ tooltipPosition: 'bottom',
47
+ /* Next CSS class for tooltip boxes */
48
+ tooltipClass: '',
49
+ /* CSS class that is added to the helperLayer */
50
+ highlightClass: '',
51
+ /* Close introduction when pressing Escape button? */
52
+ exitOnEsc: true,
53
+ /* Close introduction when clicking on overlay layer? */
54
+ exitOnOverlayClick: true,
55
+ /* Show step numbers in introduction? */
56
+ showStepNumbers: true,
57
+ /* Let user use keyboard to navigate the tour? */
58
+ keyboardNavigation: true,
59
+ /* Show tour control buttons? */
60
+ showButtons: true,
61
+ /* Show tour bullets? */
62
+ showBullets: true,
63
+ /* Show tour progress? */
64
+ showProgress: false,
65
+ /* Scroll to highlighted element? */
66
+ scrollToElement: true,
67
+ /*
68
+ * Should we scroll the tooltip or target element?
69
+ *
70
+ * Options are: 'element' or 'tooltip'
71
+ */
72
+ scrollTo: 'element',
73
+ /* Padding to add after scrolling when element is not in the viewport (in pixels) */
74
+ scrollPadding: 30,
75
+ /* Set the overlay opacity */
76
+ overlayOpacity: 0.8,
77
+ /* Precedence of positions, when auto is enabled */
78
+ positionPrecedence: ["bottom", "top", "right", "left"],
79
+ /* Disable an interaction with element? */
80
+ disableInteraction: false,
81
+ /* Default hint position */
82
+ hintPosition: 'top-middle',
83
+ /* Hint button label */
84
+ hintButtonLabel: 'Got it',
85
+ /* Adding animation to hints? */
86
+ hintAnimation: true
87
+ };
88
+ }
89
+
90
+ /**
91
+ * Initiate a new introduction/guide from an element in the page
92
+ *
93
+ * @api private
94
+ * @method _introForElement
95
+ * @param {Object} targetElm
96
+ * @returns {Boolean} Success or not?
97
+ */
98
+ function _introForElement(targetElm) {
99
+ var introItems = [],
100
+ self = this;
101
+
102
+ if (this._options.steps) {
103
+ //use steps passed programmatically
104
+ for (var i = 0, stepsLength = this._options.steps.length; i < stepsLength; i++) {
105
+ var currentItem = _cloneObject(this._options.steps[i]);
106
+
107
+ //set the step
108
+ currentItem.step = introItems.length + 1;
109
+
110
+ //use querySelector function only when developer used CSS selector
111
+ if (typeof (currentItem.element) === 'string') {
112
+ //grab the element with given selector from the page
113
+ currentItem.element = document.querySelector(currentItem.element);
114
+ }
115
+
116
+ //intro without element
117
+ if (typeof (currentItem.element) === 'undefined' || currentItem.element == null) {
118
+ var floatingElementQuery = document.querySelector(".introjsFloatingElement");
119
+
120
+ if (floatingElementQuery == null) {
121
+ floatingElementQuery = document.createElement('div');
122
+ floatingElementQuery.className = 'introjsFloatingElement';
123
+
124
+ document.body.appendChild(floatingElementQuery);
125
+ }
126
+
127
+ currentItem.element = floatingElementQuery;
128
+ currentItem.position = 'floating';
129
+ }
130
+
131
+ currentItem.scrollTo = currentItem.scrollTo || this._options.scrollTo;
132
+
133
+ if (typeof (currentItem.disableInteraction) === 'undefined') {
134
+ currentItem.disableInteraction = this._options.disableInteraction;
135
+ }
136
+
137
+ if (currentItem.element != null) {
138
+ introItems.push(currentItem);
139
+ }
140
+ }
141
+
142
+ } else {
143
+ //use steps from data-* annotations
144
+ var allIntroSteps = targetElm.querySelectorAll('*[data-intro]');
145
+ //if there's no element to intro
146
+ if (allIntroSteps.length < 1) {
147
+ return false;
148
+ }
149
+
150
+ //first add intro items with data-step
151
+ for (var i = 0, elmsLength = allIntroSteps.length; i < elmsLength; i++) {
152
+ var currentElement = allIntroSteps[i];
153
+
154
+ // skip hidden elements
155
+ if (currentElement.style.display == 'none') {
156
+ continue;
157
+ }
158
+
159
+ var step = parseInt(currentElement.getAttribute('data-step'), 10);
160
+
161
+ var disableInteraction = this._options.disableInteraction;
162
+
163
+ if (typeof (currentElement.getAttribute('data-disable-interaction')) != 'undefined') {
164
+ disableInteraction = !!currentElement.getAttribute('data-disable-interaction');
165
+ }
166
+
167
+ if (step > 0) {
168
+ introItems[step - 1] = {
169
+ element: currentElement,
170
+ intro: currentElement.getAttribute('data-intro'),
171
+ step: parseInt(currentElement.getAttribute('data-step'), 10),
172
+ tooltipClass: currentElement.getAttribute('data-tooltipClass'),
173
+ highlightClass: currentElement.getAttribute('data-highlightClass'),
174
+ position: currentElement.getAttribute('data-position') || this._options.tooltipPosition,
175
+ scrollTo: currentElement.getAttribute('data-scrollTo') || this._options.scrollTo,
176
+ disableInteraction: disableInteraction
177
+ };
178
+ }
179
+ }
180
+
181
+ //next add intro items without data-step
182
+ //todo: we need a cleanup here, two loops are redundant
183
+ var nextStep = 0;
184
+ for (var i = 0, elmsLength = allIntroSteps.length; i < elmsLength; i++) {
185
+ var currentElement = allIntroSteps[i];
186
+
187
+ if (currentElement.getAttribute('data-step') == null) {
188
+
189
+ while (true) {
190
+ if (typeof introItems[nextStep] == 'undefined') {
191
+ break;
192
+ } else {
193
+ nextStep++;
194
+ }
195
+ }
196
+
197
+ var disableInteraction = this._options.disableInteraction;
198
+
199
+ if (typeof (currentElement.getAttribute('data-disable-interaction')) != 'undefined') {
200
+ disableInteraction = !!currentElement.getAttribute('data-disable-interaction');
201
+ }
202
+
203
+ introItems[nextStep] = {
204
+ element: currentElement,
205
+ intro: currentElement.getAttribute('data-intro'),
206
+ step: nextStep + 1,
207
+ tooltipClass: currentElement.getAttribute('data-tooltipClass'),
208
+ highlightClass: currentElement.getAttribute('data-highlightClass'),
209
+ position: currentElement.getAttribute('data-position') || this._options.tooltipPosition,
210
+ scrollTo: currentElement.getAttribute('data-scrollTo') || this._options.scrollTo,
211
+ disableInteraction: disableInteraction
212
+ };
213
+ }
214
+ }
215
+ }
216
+
217
+ //removing undefined/null elements
218
+ var tempIntroItems = [];
219
+ for (var z = 0; z < introItems.length; z++) {
220
+ introItems[z] && tempIntroItems.push(introItems[z]); // copy non-empty values to the end of the array
221
+ }
222
+
223
+ introItems = tempIntroItems;
224
+
225
+ //Ok, sort all items with given steps
226
+ introItems.sort(function (a, b) {
227
+ return a.step - b.step;
228
+ });
229
+
230
+ //set it to the introJs object
231
+ self._introItems = introItems;
232
+
233
+ //add overlay layer to the page
234
+ if(_addOverlayLayer.call(self, targetElm)) {
235
+ //then, start the show
236
+ _nextStep.call(self);
237
+
238
+ var skipButton = targetElm.querySelector('.introjs-skipbutton'),
239
+ nextStepButton = targetElm.querySelector('.introjs-nextbutton');
240
+
241
+ self._onKeyDown = function(e) {
242
+ if (e.keyCode === 27 && self._options.exitOnEsc == true) {
243
+ //escape key pressed, exit the intro
244
+ //check if exit callback is defined
245
+ _exitIntro.call(self, targetElm);
246
+ } else if(e.keyCode === 37) {
247
+ //left arrow
248
+ _previousStep.call(self);
249
+ } else if (e.keyCode === 39) {
250
+ //right arrow
251
+ _nextStep.call(self);
252
+ } else if (e.keyCode === 13) {
253
+ //srcElement === ie
254
+ var target = e.target || e.srcElement;
255
+ if (target && target.className.indexOf('introjs-prevbutton') > 0) {
256
+ //user hit enter while focusing on previous button
257
+ _previousStep.call(self);
258
+ } else if (target && target.className.indexOf('introjs-skipbutton') > 0) {
259
+ //user hit enter while focusing on skip button
260
+ if (self._introItems.length - 1 == self._currentStep && typeof (self._introCompleteCallback) === 'function') {
261
+ self._introCompleteCallback.call(self);
262
+ }
263
+
264
+ _exitIntro.call(self, targetElm);
265
+ } else {
266
+ //default behavior for responding to enter
267
+ _nextStep.call(self);
268
+ }
269
+
270
+ //prevent default behaviour on hitting Enter, to prevent steps being skipped in some browsers
271
+ if(e.preventDefault) {
272
+ e.preventDefault();
273
+ } else {
274
+ e.returnValue = false;
275
+ }
276
+ }
277
+ };
278
+
279
+ self._onResize = function(e) {
280
+ self.refresh.call(self);
281
+ };
282
+
283
+ if (window.addEventListener) {
284
+ if (this._options.keyboardNavigation) {
285
+ window.addEventListener('keydown', self._onKeyDown, true);
286
+ }
287
+ //for window resize
288
+ window.addEventListener('resize', self._onResize, true);
289
+ } else if (document.attachEvent) { //IE
290
+ if (this._options.keyboardNavigation) {
291
+ document.attachEvent('onkeydown', self._onKeyDown);
292
+ }
293
+ //for window resize
294
+ document.attachEvent('onresize', self._onResize);
295
+ }
296
+ }
297
+ return false;
298
+ }
299
+
300
+ /*
301
+ * makes a copy of the object
302
+ * @api private
303
+ * @method _cloneObject
304
+ */
305
+ function _cloneObject(object) {
306
+ if (object == null || typeof (object) != 'object' || typeof (object.nodeType) != 'undefined') {
307
+ return object;
308
+ }
309
+ var temp = {};
310
+ for (var key in object) {
311
+ if (typeof (jQuery) != 'undefined' && object[key] instanceof jQuery) {
312
+ temp[key] = object[key];
313
+ } else {
314
+ temp[key] = _cloneObject(object[key]);
315
+ }
316
+ }
317
+ return temp;
318
+ }
319
+ /**
320
+ * Go to specific step of introduction
321
+ *
322
+ * @api private
323
+ * @method _goToStep
324
+ */
325
+ function _goToStep(step) {
326
+ //because steps starts with zero
327
+ this._currentStep = step - 2;
328
+ if (typeof (this._introItems) !== 'undefined') {
329
+ _nextStep.call(this);
330
+ }
331
+ }
332
+
333
+ /**
334
+ * Go to the specific step of introduction with the explicit [data-step] number
335
+ *
336
+ * @api private
337
+ * @method _goToStepNumber
338
+ */
339
+ function _goToStepNumber(step) {
340
+ this._currentStepNumber = step;
341
+ if (typeof (this._introItems) !== 'undefined') {
342
+ _nextStep.call(this);
343
+ }
344
+ }
345
+
346
+ /**
347
+ * Go to next step on intro
348
+ *
349
+ * @api private
350
+ * @method _nextStep
351
+ */
352
+ function _nextStep() {
353
+ this._direction = 'forward';
354
+
355
+ if (typeof (this._currentStepNumber) !== 'undefined') {
356
+ for( var i = 0, len = this._introItems.length; i < len; i++ ) {
357
+ var item = this._introItems[i];
358
+ if( item.step === this._currentStepNumber ) {
359
+ this._currentStep = i - 1;
360
+ this._currentStepNumber = undefined;
361
+ }
362
+ }
363
+ }
364
+
365
+ if (typeof (this._currentStep) === 'undefined') {
366
+ this._currentStep = 0;
367
+ } else {
368
+ ++this._currentStep;
369
+ }
370
+
371
+ if ((this._introItems.length) <= this._currentStep) {
372
+ //end of the intro
373
+ //check if any callback is defined
374
+ if (typeof (this._introCompleteCallback) === 'function') {
375
+ this._introCompleteCallback.call(this);
376
+ }
377
+ _exitIntro.call(this, this._targetElement);
378
+ return;
379
+ }
380
+
381
+ var nextStep = this._introItems[this._currentStep];
382
+ if (typeof (this._introBeforeChangeCallback) !== 'undefined') {
383
+ this._introBeforeChangeCallback.call(this, nextStep.element);
384
+ }
385
+
386
+ _showElement.call(this, nextStep);
387
+ }
388
+
389
+ /**
390
+ * Go to previous step on intro
391
+ *
392
+ * @api private
393
+ * @method _previousStep
394
+ */
395
+ function _previousStep() {
396
+ this._direction = 'backward';
397
+
398
+ if (this._currentStep === 0) {
399
+ return false;
400
+ }
401
+
402
+ var nextStep = this._introItems[--this._currentStep];
403
+ if (typeof (this._introBeforeChangeCallback) !== 'undefined') {
404
+ this._introBeforeChangeCallback.call(this, nextStep.element);
405
+ }
406
+
407
+ _showElement.call(this, nextStep);
408
+ }
409
+
410
+ /**
411
+ * Update placement of the intro objects on the screen
412
+ * @api private
413
+ */
414
+ function _refresh() {
415
+ // re-align intros
416
+ _setHelperLayerPosition.call(this, document.querySelector('.introjs-helperLayer'));
417
+ _setHelperLayerPosition.call(this, document.querySelector('.introjs-tooltipReferenceLayer'));
418
+
419
+ // re-align tooltip
420
+ if(this._currentStep !== undefined && this._currentStep !== null) {
421
+ var oldHelperNumberLayer = document.querySelector('.introjs-helperNumberLayer'),
422
+ oldArrowLayer = document.querySelector('.introjs-arrow'),
423
+ oldtooltipContainer = document.querySelector('.introjs-tooltip');
424
+ _placeTooltip.call(this, this._introItems[this._currentStep].element, oldtooltipContainer, oldArrowLayer, oldHelperNumberLayer);
425
+ }
426
+
427
+ //re-align hints
428
+ _reAlignHints.call(this);
429
+ return this;
430
+ }
431
+
432
+ /**
433
+ * Exit from intro
434
+ *
435
+ * @api private
436
+ * @method _exitIntro
437
+ * @param {Object} targetElement
438
+ * @param {Boolean} force - Setting to `true` will skip the result of beforeExit callback
439
+ */
440
+ function _exitIntro(targetElement, force) {
441
+ var continueExit = true;
442
+
443
+ // calling onbeforeexit callback
444
+ //
445
+ // If this callback return `false`, it would halt the process
446
+ if (this._introBeforeExitCallback != undefined) {
447
+ continueExit = this._introBeforeExitCallback.call(self);
448
+ }
449
+
450
+ // skip this check if `force` parameter is `true`
451
+ // otherwise, if `onbeforeexit` returned `false`, don't exit the intro
452
+ if (!force && continueExit === false) return;
453
+
454
+ //remove overlay layers from the page
455
+ var overlayLayers = targetElement.querySelectorAll('.introjs-overlay');
456
+
457
+ if (overlayLayers && overlayLayers.length > 0) {
458
+ for (var i = overlayLayers.length - 1; i >= 0; i--) {
459
+ //for fade-out animation
460
+ var overlayLayer = overlayLayers[i];
461
+ overlayLayer.style.opacity = 0;
462
+ setTimeout(function () {
463
+ if (this.parentNode) {
464
+ this.parentNode.removeChild(this);
465
+ }
466
+ }.bind(overlayLayer), 500);
467
+ };
468
+ }
469
+
470
+ //remove all helper layers
471
+ var helperLayer = targetElement.querySelector('.introjs-helperLayer');
472
+ if (helperLayer) {
473
+ helperLayer.parentNode.removeChild(helperLayer);
474
+ }
475
+
476
+ var referenceLayer = targetElement.querySelector('.introjs-tooltipReferenceLayer');
477
+ if (referenceLayer) {
478
+ referenceLayer.parentNode.removeChild(referenceLayer);
479
+ }
480
+
481
+ //remove disableInteractionLayer
482
+ var disableInteractionLayer = targetElement.querySelector('.introjs-disableInteraction');
483
+ if (disableInteractionLayer) {
484
+ disableInteractionLayer.parentNode.removeChild(disableInteractionLayer);
485
+ }
486
+
487
+ //remove intro floating element
488
+ var floatingElement = document.querySelector('.introjsFloatingElement');
489
+ if (floatingElement) {
490
+ floatingElement.parentNode.removeChild(floatingElement);
491
+ }
492
+
493
+ _removeShowElement();
494
+
495
+ //remove `introjs-fixParent` class from the elements
496
+ var fixParents = document.querySelectorAll('.introjs-fixParent');
497
+ if (fixParents && fixParents.length > 0) {
498
+ for (var i = fixParents.length - 1; i >= 0; i--) {
499
+ fixParents[i].className = fixParents[i].className.replace(/introjs-fixParent/g, '').replace(/^\s+|\s+$/g, '');
500
+ }
501
+ }
502
+
503
+ //clean listeners
504
+ if (window.removeEventListener) {
505
+ window.removeEventListener('keydown', this._onKeyDown, true);
506
+ } else if (document.detachEvent) { //IE
507
+ document.detachEvent('onkeydown', this._onKeyDown);
508
+ }
509
+
510
+ //check if any callback is defined
511
+ if (this._introExitCallback != undefined) {
512
+ this._introExitCallback.call(self);
513
+ }
514
+
515
+ //set the step to zero
516
+ this._currentStep = undefined;
517
+ }
518
+
519
+ /**
520
+ * Render tooltip box in the page
521
+ *
522
+ * @api private
523
+ * @method _placeTooltip
524
+ * @param {HTMLElement} targetElement
525
+ * @param {HTMLElement} tooltipLayer
526
+ * @param {HTMLElement} arrowLayer
527
+ * @param {HTMLElement} helperNumberLayer
528
+ * @param {Boolean} hintMode
529
+ */
530
+ function _placeTooltip(targetElement, tooltipLayer, arrowLayer, helperNumberLayer, hintMode) {
531
+ var tooltipCssClass = '',
532
+ currentStepObj,
533
+ tooltipOffset,
534
+ targetOffset,
535
+ windowSize,
536
+ currentTooltipPosition;
537
+
538
+ hintMode = hintMode || false;
539
+
540
+ //reset the old style
541
+ tooltipLayer.style.top = null;
542
+ tooltipLayer.style.right = null;
543
+ tooltipLayer.style.bottom = null;
544
+ tooltipLayer.style.left = null;
545
+ tooltipLayer.style.marginLeft = null;
546
+ tooltipLayer.style.marginTop = null;
547
+
548
+ arrowLayer.style.display = 'inherit';
549
+
550
+ if (typeof(helperNumberLayer) != 'undefined' && helperNumberLayer != null) {
551
+ helperNumberLayer.style.top = null;
552
+ helperNumberLayer.style.left = null;
553
+ }
554
+
555
+ //prevent error when `this._currentStep` is undefined
556
+ if (!this._introItems[this._currentStep]) return;
557
+
558
+ //if we have a custom css class for each step
559
+ currentStepObj = this._introItems[this._currentStep];
560
+ if (typeof (currentStepObj.tooltipClass) === 'string') {
561
+ tooltipCssClass = currentStepObj.tooltipClass;
562
+ } else {
563
+ tooltipCssClass = this._options.tooltipClass;
564
+ }
565
+
566
+ tooltipLayer.className = ('introjs-tooltip ' + tooltipCssClass).replace(/^\s+|\s+$/g, '');
567
+
568
+ currentTooltipPosition = this._introItems[this._currentStep].position;
569
+
570
+ if (currentTooltipPosition != "floating") { // Floating is always valid, no point in calculating
571
+ if (currentTooltipPosition === "auto") {
572
+ currentTooltipPosition = _determineAutoPosition.call(this, targetElement, tooltipLayer);
573
+ } else {
574
+ currentTooltipPosition = _determineAutoPosition.call(this, targetElement, tooltipLayer, currentTooltipPosition);
575
+ }
576
+ }
577
+
578
+ targetOffset = _getOffset(targetElement);
579
+ tooltipOffset = _getOffset(tooltipLayer);
580
+ windowSize = _getWinSize();
581
+
582
+ switch (currentTooltipPosition) {
583
+ case 'top':
584
+ arrowLayer.className = 'introjs-arrow bottom';
585
+
586
+ if (hintMode) {
587
+ var tooltipLayerStyleLeft = 0;
588
+ } else {
589
+ var tooltipLayerStyleLeft = 15;
590
+ }
591
+
592
+ _checkRight(targetOffset, tooltipLayerStyleLeft, tooltipOffset, windowSize, tooltipLayer);
593
+ tooltipLayer.style.bottom = (targetOffset.height + 20) + 'px';
594
+ break;
595
+ case 'right':
596
+ tooltipLayer.style.left = (targetOffset.width + 20) + 'px';
597
+ if (targetOffset.top + tooltipOffset.height > windowSize.height) {
598
+ // In this case, right would have fallen below the bottom of the screen.
599
+ // Modify so that the bottom of the tooltip connects with the target
600
+ arrowLayer.className = "introjs-arrow left-bottom";
601
+ tooltipLayer.style.top = "-" + (tooltipOffset.height - targetOffset.height - 20) + "px";
602
+ } else {
603
+ arrowLayer.className = 'introjs-arrow left';
604
+ }
605
+ break;
606
+ case 'left':
607
+ if (!hintMode && this._options.showStepNumbers == true) {
608
+ tooltipLayer.style.top = '15px';
609
+ }
610
+
611
+ if (targetOffset.top + tooltipOffset.height > windowSize.height) {
612
+ // In this case, left would have fallen below the bottom of the screen.
613
+ // Modify so that the bottom of the tooltip connects with the target
614
+ tooltipLayer.style.top = "-" + (tooltipOffset.height - targetOffset.height - 20) + "px";
615
+ arrowLayer.className = 'introjs-arrow right-bottom';
616
+ } else {
617
+ arrowLayer.className = 'introjs-arrow right';
618
+ }
619
+ tooltipLayer.style.right = (targetOffset.width + 20) + 'px';
620
+
621
+ break;
622
+ case 'floating':
623
+ arrowLayer.style.display = 'none';
624
+
625
+ //we have to adjust the top and left of layer manually for intro items without element
626
+ tooltipLayer.style.left = '50%';
627
+ tooltipLayer.style.top = '50%';
628
+ tooltipLayer.style.marginLeft = '-' + (tooltipOffset.width / 2) + 'px';
629
+ tooltipLayer.style.marginTop = '-' + (tooltipOffset.height / 2) + 'px';
630
+
631
+ if (typeof(helperNumberLayer) != 'undefined' && helperNumberLayer != null) {
632
+ helperNumberLayer.style.left = '-' + ((tooltipOffset.width / 2) + 18) + 'px';
633
+ helperNumberLayer.style.top = '-' + ((tooltipOffset.height / 2) + 18) + 'px';
634
+ }
635
+
636
+ break;
637
+ case 'bottom-right-aligned':
638
+ arrowLayer.className = 'introjs-arrow top-right';
639
+
640
+ var tooltipLayerStyleRight = 0;
641
+ _checkLeft(targetOffset, tooltipLayerStyleRight, tooltipOffset, tooltipLayer);
642
+ tooltipLayer.style.top = (targetOffset.height + 20) + 'px';
643
+ break;
644
+
645
+ case 'bottom-middle-aligned':
646
+ arrowLayer.className = 'introjs-arrow top-middle';
647
+
648
+ var tooltipLayerStyleLeftRight = targetOffset.width / 2 - tooltipOffset.width / 2;
649
+
650
+ // a fix for middle aligned hints
651
+ if (hintMode) {
652
+ tooltipLayerStyleLeftRight += 5;
653
+ }
654
+
655
+ if (_checkLeft(targetOffset, tooltipLayerStyleLeftRight, tooltipOffset, tooltipLayer)) {
656
+ tooltipLayer.style.right = null;
657
+ _checkRight(targetOffset, tooltipLayerStyleLeftRight, tooltipOffset, windowSize, tooltipLayer);
658
+ }
659
+ tooltipLayer.style.top = (targetOffset.height + 20) + 'px';
660
+ break;
661
+
662
+ case 'bottom-left-aligned':
663
+ // Bottom-left-aligned is the same as the default bottom
664
+ case 'bottom':
665
+ // Bottom going to follow the default behavior
666
+ default:
667
+ arrowLayer.className = 'introjs-arrow top';
668
+
669
+ var tooltipLayerStyleLeft = 0;
670
+ _checkRight(targetOffset, tooltipLayerStyleLeft, tooltipOffset, windowSize, tooltipLayer);
671
+ tooltipLayer.style.top = (targetOffset.height + 20) + 'px';
672
+ break;
673
+ }
674
+ }
675
+
676
+ /**
677
+ * Set tooltip left so it doesn't go off the right side of the window
678
+ *
679
+ * @return boolean true, if tooltipLayerStyleLeft is ok. false, otherwise.
680
+ */
681
+ function _checkRight(targetOffset, tooltipLayerStyleLeft, tooltipOffset, windowSize, tooltipLayer) {
682
+ if (targetOffset.left + tooltipLayerStyleLeft + tooltipOffset.width > windowSize.width) {
683
+ // off the right side of the window
684
+ tooltipLayer.style.left = (windowSize.width - tooltipOffset.width - targetOffset.left) + 'px';
685
+ return false;
686
+ }
687
+ tooltipLayer.style.left = tooltipLayerStyleLeft + 'px';
688
+ return true;
689
+ }
690
+
691
+ /**
692
+ * Set tooltip right so it doesn't go off the left side of the window
693
+ *
694
+ * @return boolean true, if tooltipLayerStyleRight is ok. false, otherwise.
695
+ */
696
+ function _checkLeft(targetOffset, tooltipLayerStyleRight, tooltipOffset, tooltipLayer) {
697
+ if (targetOffset.left + targetOffset.width - tooltipLayerStyleRight - tooltipOffset.width < 0) {
698
+ // off the left side of the window
699
+ tooltipLayer.style.left = (-targetOffset.left) + 'px';
700
+ return false;
701
+ }
702
+ tooltipLayer.style.right = tooltipLayerStyleRight + 'px';
703
+ return true;
704
+ }
705
+
706
+ /**
707
+ * Determines the position of the tooltip based on the position precedence and availability
708
+ * of screen space.
709
+ *
710
+ * @param {Object} targetElement
711
+ * @param {Object} tooltipLayer
712
+ * @param {Object} desiredTooltipPosition
713
+ *
714
+ */
715
+ function _determineAutoPosition(targetElement, tooltipLayer, desiredTooltipPosition) {
716
+
717
+ // Take a clone of position precedence. These will be the available
718
+ var possiblePositions = this._options.positionPrecedence.slice();
719
+
720
+ var windowSize = _getWinSize();
721
+ var tooltipHeight = _getOffset(tooltipLayer).height + 10;
722
+ var tooltipWidth = _getOffset(tooltipLayer).width + 20;
723
+ var targetOffset = _getOffset(targetElement);
724
+
725
+ // If we check all the possible areas, and there are no valid places for the tooltip, the element
726
+ // must take up most of the screen real estate. Show the tooltip floating in the middle of the screen.
727
+ var calculatedPosition = "floating";
728
+
729
+ // Check if the width of the tooltip + the starting point would spill off the right side of the screen
730
+ // If no, neither bottom or top are valid
731
+ if (targetOffset.left + tooltipWidth > windowSize.width || ((targetOffset.left + (targetOffset.width / 2)) - tooltipWidth) < 0) {
732
+ _removeEntry(possiblePositions, "bottom");
733
+ _removeEntry(possiblePositions, "top");
734
+ } else {
735
+ // Check for space below
736
+ if ((targetOffset.height + targetOffset.top + tooltipHeight) > windowSize.height) {
737
+ _removeEntry(possiblePositions, "bottom");
738
+ }
739
+
740
+ // Check for space above
741
+ if (targetOffset.top - tooltipHeight < 0) {
742
+ _removeEntry(possiblePositions, "top");
743
+ }
744
+ }
745
+
746
+ // Check for space to the right
747
+ if (targetOffset.width + targetOffset.left + tooltipWidth > windowSize.width) {
748
+ _removeEntry(possiblePositions, "right");
749
+ }
750
+
751
+ // Check for space to the left
752
+ if (targetOffset.left - tooltipWidth < 0) {
753
+ _removeEntry(possiblePositions, "left");
754
+ }
755
+
756
+ // At this point, our array only has positions that are valid. Pick the first one, as it remains in order
757
+ if (possiblePositions.length > 0) {
758
+ calculatedPosition = possiblePositions[0];
759
+ }
760
+
761
+ // If the requested position is in the list, replace our calculated choice with that
762
+ if (desiredTooltipPosition && desiredTooltipPosition != "auto") {
763
+ if (possiblePositions.indexOf(desiredTooltipPosition) > -1) {
764
+ calculatedPosition = desiredTooltipPosition;
765
+ }
766
+ }
767
+
768
+ return calculatedPosition;
769
+ }
770
+
771
+ /**
772
+ * Remove an entry from a string array if it's there, does nothing if it isn't there.
773
+ *
774
+ * @param {Array} stringArray
775
+ * @param {String} stringToRemove
776
+ */
777
+ function _removeEntry(stringArray, stringToRemove) {
778
+ if (stringArray.indexOf(stringToRemove) > -1) {
779
+ stringArray.splice(stringArray.indexOf(stringToRemove), 1);
780
+ }
781
+ }
782
+
783
+ /**
784
+ * Update the position of the helper layer on the screen
785
+ *
786
+ * @api private
787
+ * @method _setHelperLayerPosition
788
+ * @param {Object} helperLayer
789
+ */
790
+ function _setHelperLayerPosition(helperLayer) {
791
+ if (helperLayer) {
792
+ //prevent error when `this._currentStep` in undefined
793
+ if (!this._introItems[this._currentStep]) return;
794
+
795
+ var currentElement = this._introItems[this._currentStep],
796
+ elementPosition = _getOffset(currentElement.element),
797
+ widthHeightPadding = 10;
798
+
799
+ // If the target element is fixed, the tooltip should be fixed as well.
800
+ // Otherwise, remove a fixed class that may be left over from the previous
801
+ // step.
802
+ if (_isFixed(currentElement.element)) {
803
+ helperLayer.className += ' introjs-fixedTooltip';
804
+ } else {
805
+ helperLayer.className = helperLayer.className.replace(' introjs-fixedTooltip', '');
806
+ }
807
+
808
+ if (currentElement.position == 'floating') {
809
+ widthHeightPadding = 0;
810
+ }
811
+
812
+ //set new position to helper layer
813
+ helperLayer.setAttribute('style', 'width: ' + (elementPosition.width + widthHeightPadding) + 'px; ' +
814
+ 'height:' + (elementPosition.height + widthHeightPadding) + 'px; ' +
815
+ 'top:' + (elementPosition.top - 5) + 'px;' +
816
+ 'left: ' + (elementPosition.left - 5) + 'px;');
817
+
818
+ }
819
+ }
820
+
821
+ /**
822
+ * Add disableinteraction layer and adjust the size and position of the layer
823
+ *
824
+ * @api private
825
+ * @method _disableInteraction
826
+ */
827
+ function _disableInteraction() {
828
+ var disableInteractionLayer = document.querySelector('.introjs-disableInteraction');
829
+
830
+ if (disableInteractionLayer === null) {
831
+ disableInteractionLayer = document.createElement('div');
832
+ disableInteractionLayer.className = 'introjs-disableInteraction';
833
+ this._targetElement.appendChild(disableInteractionLayer);
834
+ }
835
+
836
+ _setHelperLayerPosition.call(this, disableInteractionLayer);
837
+ }
838
+
839
+ /**
840
+ * Setting anchors to behave like buttons
841
+ *
842
+ * @api private
843
+ * @method _setAnchorAsButton
844
+ */
845
+ function _setAnchorAsButton(anchor){
846
+ anchor.setAttribute('role', 'button');
847
+ anchor.tabIndex = 0;
848
+ }
849
+
850
+ /**
851
+ * Show an element on the page
852
+ *
853
+ * @api private
854
+ * @method _showElement
855
+ * @param {Object} targetElement
856
+ */
857
+ function _showElement(targetElement) {
858
+ if (typeof (this._introChangeCallback) !== 'undefined') {
859
+ this._introChangeCallback.call(this, targetElement.element);
860
+ }
861
+
862
+ var self = this,
863
+ oldHelperLayer = document.querySelector('.introjs-helperLayer'),
864
+ oldReferenceLayer = document.querySelector('.introjs-tooltipReferenceLayer'),
865
+ highlightClass = 'introjs-helperLayer',
866
+ elementPosition = _getOffset(targetElement.element);
867
+
868
+ //check for a current step highlight class
869
+ if (typeof (targetElement.highlightClass) === 'string') {
870
+ highlightClass += (' ' + targetElement.highlightClass);
871
+ }
872
+ //check for options highlight class
873
+ if (typeof (this._options.highlightClass) === 'string') {
874
+ highlightClass += (' ' + this._options.highlightClass);
875
+ }
876
+
877
+ if (oldHelperLayer != null) {
878
+ var oldHelperNumberLayer = oldReferenceLayer.querySelector('.introjs-helperNumberLayer'),
879
+ oldtooltipLayer = oldReferenceLayer.querySelector('.introjs-tooltiptext'),
880
+ oldArrowLayer = oldReferenceLayer.querySelector('.introjs-arrow'),
881
+ oldtooltipContainer = oldReferenceLayer.querySelector('.introjs-tooltip'),
882
+ skipTooltipButton = oldReferenceLayer.querySelector('.introjs-skipbutton'),
883
+ prevTooltipButton = oldReferenceLayer.querySelector('.introjs-prevbutton'),
884
+ nextTooltipButton = oldReferenceLayer.querySelector('.introjs-nextbutton');
885
+
886
+ //update or reset the helper highlight class
887
+ oldHelperLayer.className = highlightClass;
888
+ //hide the tooltip
889
+ oldtooltipContainer.style.opacity = 0;
890
+ oldtooltipContainer.style.display = "none";
891
+
892
+ if (oldHelperNumberLayer != null) {
893
+ var lastIntroItem = this._introItems[(targetElement.step - 2 >= 0 ? targetElement.step - 2 : 0)];
894
+
895
+ if (lastIntroItem != null && (this._direction == 'forward' && lastIntroItem.position == 'floating') || (this._direction == 'backward' && targetElement.position == 'floating')) {
896
+ oldHelperNumberLayer.style.opacity = 0;
897
+ }
898
+ }
899
+
900
+ //set new position to helper layer
901
+ _setHelperLayerPosition.call(self, oldHelperLayer);
902
+ _setHelperLayerPosition.call(self, oldReferenceLayer);
903
+
904
+ //remove `introjs-fixParent` class from the elements
905
+ var fixParents = document.querySelectorAll('.introjs-fixParent');
906
+ if (fixParents && fixParents.length > 0) {
907
+ for (var i = fixParents.length - 1; i >= 0; i--) {
908
+ fixParents[i].className = fixParents[i].className.replace(/introjs-fixParent/g, '').replace(/^\s+|\s+$/g, '');
909
+ };
910
+ }
911
+
912
+ //remove old classes if the element still exist
913
+ _removeShowElement();
914
+
915
+ //we should wait until the CSS3 transition is competed (it's 0.3 sec) to prevent incorrect `height` and `width` calculation
916
+ if (self._lastShowElementTimer) {
917
+ clearTimeout(self._lastShowElementTimer);
918
+ }
919
+
920
+ self._lastShowElementTimer = setTimeout(function() {
921
+ //set current step to the label
922
+ if (oldHelperNumberLayer != null) {
923
+ oldHelperNumberLayer.innerHTML = targetElement.step;
924
+ }
925
+ //set current tooltip text
926
+ oldtooltipLayer.innerHTML = targetElement.intro;
927
+ //set the tooltip position
928
+ oldtooltipContainer.style.display = "block";
929
+ _placeTooltip.call(self, targetElement.element, oldtooltipContainer, oldArrowLayer, oldHelperNumberLayer);
930
+
931
+ //change active bullet
932
+ if (self._options.showBullets) {
933
+ oldReferenceLayer.querySelector('.introjs-bullets li > a.active').className = '';
934
+ oldReferenceLayer.querySelector('.introjs-bullets li > a[data-stepnumber="' + targetElement.step + '"]').className = 'active';
935
+ }
936
+ oldReferenceLayer.querySelector('.introjs-progress .introjs-progressbar').setAttribute('style', 'width:' + _getProgress.call(self) + '%;');
937
+
938
+ //show the tooltip
939
+ oldtooltipContainer.style.opacity = 1;
940
+ if (oldHelperNumberLayer) oldHelperNumberLayer.style.opacity = 1;
941
+
942
+ //reset button focus
943
+ if (typeof skipTooltipButton !== "undefined" && skipTooltipButton != null && /introjs-donebutton/gi.test(skipTooltipButton.className)) {
944
+ // skip button is now "done" button
945
+ skipTooltipButton.focus();
946
+ } else if (typeof nextTooltipButton !== "undefined" && nextTooltipButton != null) {
947
+ //still in the tour, focus on next
948
+ nextTooltipButton.focus();
949
+ }
950
+
951
+ // change the scroll of the window, if needed
952
+ _scrollTo.call(self, targetElement.scrollTo, targetElement, oldtooltipLayer);
953
+ }, 350);
954
+
955
+ // end of old element if-else condition
956
+ } else {
957
+ var helperLayer = document.createElement('div'),
958
+ referenceLayer = document.createElement('div'),
959
+ arrowLayer = document.createElement('div'),
960
+ tooltipLayer = document.createElement('div'),
961
+ tooltipTextLayer = document.createElement('div'),
962
+ bulletsLayer = document.createElement('div'),
963
+ progressLayer = document.createElement('div'),
964
+ buttonsLayer = document.createElement('div');
965
+
966
+ helperLayer.className = highlightClass;
967
+ referenceLayer.className = 'introjs-tooltipReferenceLayer';
968
+
969
+ //set new position to helper layer
970
+ _setHelperLayerPosition.call(self, helperLayer);
971
+ _setHelperLayerPosition.call(self, referenceLayer);
972
+
973
+ //add helper layer to target element
974
+ this._targetElement.appendChild(helperLayer);
975
+ this._targetElement.appendChild(referenceLayer);
976
+
977
+ arrowLayer.className = 'introjs-arrow';
978
+
979
+ tooltipTextLayer.className = 'introjs-tooltiptext';
980
+ tooltipTextLayer.innerHTML = targetElement.intro;
981
+
982
+ bulletsLayer.className = 'introjs-bullets';
983
+
984
+ if (this._options.showBullets === false) {
985
+ bulletsLayer.style.display = 'none';
986
+ }
987
+
988
+ var ulContainer = document.createElement('ul');
989
+
990
+ for (var i = 0, stepsLength = this._introItems.length; i < stepsLength; i++) {
991
+ var innerLi = document.createElement('li');
992
+ var anchorLink = document.createElement('a');
993
+
994
+ anchorLink.onclick = function() {
995
+ self.goToStep(this.getAttribute('data-stepnumber'));
996
+ };
997
+
998
+ if (i === (targetElement.step-1)) anchorLink.className = 'active';
999
+
1000
+ _setAnchorAsButton(anchorLink);
1001
+ anchorLink.innerHTML = "&nbsp;";
1002
+ anchorLink.setAttribute('data-stepnumber', this._introItems[i].step);
1003
+
1004
+ innerLi.appendChild(anchorLink);
1005
+ ulContainer.appendChild(innerLi);
1006
+ }
1007
+
1008
+ bulletsLayer.appendChild(ulContainer);
1009
+
1010
+ progressLayer.className = 'introjs-progress';
1011
+
1012
+ if (this._options.showProgress === false) {
1013
+ progressLayer.style.display = 'none';
1014
+ }
1015
+ var progressBar = document.createElement('div');
1016
+ progressBar.className = 'introjs-progressbar';
1017
+ progressBar.setAttribute('style', 'width:' + _getProgress.call(this) + '%;');
1018
+
1019
+ progressLayer.appendChild(progressBar);
1020
+
1021
+ buttonsLayer.className = 'introjs-tooltipbuttons';
1022
+ if (this._options.showButtons === false) {
1023
+ buttonsLayer.style.display = 'none';
1024
+ }
1025
+
1026
+ tooltipLayer.className = 'introjs-tooltip';
1027
+ tooltipLayer.appendChild(tooltipTextLayer);
1028
+ tooltipLayer.appendChild(bulletsLayer);
1029
+ tooltipLayer.appendChild(progressLayer);
1030
+
1031
+ //add helper layer number
1032
+ if (this._options.showStepNumbers == true) {
1033
+ var helperNumberLayer = document.createElement('span');
1034
+ helperNumberLayer.className = 'introjs-helperNumberLayer';
1035
+ helperNumberLayer.innerHTML = targetElement.step;
1036
+ referenceLayer.appendChild(helperNumberLayer);
1037
+ }
1038
+
1039
+ tooltipLayer.appendChild(arrowLayer);
1040
+ referenceLayer.appendChild(tooltipLayer);
1041
+
1042
+ //next button
1043
+ var nextTooltipButton = document.createElement('a');
1044
+
1045
+ nextTooltipButton.onclick = function() {
1046
+ if (self._introItems.length - 1 != self._currentStep) {
1047
+ _nextStep.call(self);
1048
+ }
1049
+ };
1050
+
1051
+ _setAnchorAsButton(nextTooltipButton);
1052
+ nextTooltipButton.innerHTML = this._options.nextLabel;
1053
+
1054
+ //previous button
1055
+ var prevTooltipButton = document.createElement('a');
1056
+
1057
+ prevTooltipButton.onclick = function() {
1058
+ if (self._currentStep != 0) {
1059
+ _previousStep.call(self);
1060
+ }
1061
+ };
1062
+
1063
+ _setAnchorAsButton(prevTooltipButton);
1064
+ prevTooltipButton.innerHTML = this._options.prevLabel;
1065
+
1066
+ //skip button
1067
+ var skipTooltipButton = document.createElement('a');
1068
+ skipTooltipButton.className = 'introjs-button introjs-skipbutton';
1069
+ _setAnchorAsButton(skipTooltipButton);
1070
+ skipTooltipButton.innerHTML = this._options.skipLabel;
1071
+
1072
+ skipTooltipButton.onclick = function() {
1073
+ if (self._introItems.length - 1 == self._currentStep && typeof (self._introCompleteCallback) === 'function') {
1074
+ self._introCompleteCallback.call(self);
1075
+ }
1076
+
1077
+ _exitIntro.call(self, self._targetElement);
1078
+ };
1079
+
1080
+ buttonsLayer.appendChild(skipTooltipButton);
1081
+
1082
+ //in order to prevent displaying next/previous button always
1083
+ if (this._introItems.length > 1) {
1084
+ buttonsLayer.appendChild(prevTooltipButton);
1085
+ buttonsLayer.appendChild(nextTooltipButton);
1086
+ }
1087
+
1088
+ tooltipLayer.appendChild(buttonsLayer);
1089
+
1090
+ //set proper position
1091
+ _placeTooltip.call(self, targetElement.element, tooltipLayer, arrowLayer, helperNumberLayer);
1092
+
1093
+ // change the scroll of the window, if needed
1094
+ _scrollTo.call(this, targetElement.scrollTo, targetElement, tooltipLayer);
1095
+
1096
+ //end of new element if-else condition
1097
+ }
1098
+
1099
+ // removing previous disable interaction layer
1100
+ var disableInteractionLayer = self._targetElement.querySelector('.introjs-disableInteraction');
1101
+ if (disableInteractionLayer) {
1102
+ disableInteractionLayer.parentNode.removeChild(disableInteractionLayer);
1103
+ }
1104
+
1105
+ //disable interaction
1106
+ if (targetElement.disableInteraction) {
1107
+ _disableInteraction.call(self);
1108
+ }
1109
+
1110
+ if (typeof nextTooltipButton !== "undefined" && nextTooltipButton != null) {
1111
+ nextTooltipButton.removeAttribute('tabIndex');
1112
+ }
1113
+ if (typeof prevTooltipButton !== "undefined" && prevTooltipButton != null) {
1114
+ prevTooltipButton.removeAttribute('tabIndex');
1115
+ }
1116
+
1117
+ // when it's the first step of tour
1118
+ if (this._currentStep == 0 && this._introItems.length > 1) {
1119
+ if (typeof skipTooltipButton !== "undefined" && skipTooltipButton != null) {
1120
+ skipTooltipButton.className = 'introjs-button introjs-skipbutton';
1121
+ }
1122
+ if (typeof nextTooltipButton !== "undefined" && nextTooltipButton != null) {
1123
+ nextTooltipButton.className = 'introjs-button introjs-nextbutton';
1124
+ }
1125
+
1126
+ if (this._options.hidePrev == true) {
1127
+ if (typeof prevTooltipButton !== "undefined" && prevTooltipButton != null) {
1128
+ prevTooltipButton.className = 'introjs-button introjs-prevbutton introjs-hidden';
1129
+ }
1130
+ if (typeof nextTooltipButton !== "undefined" && nextTooltipButton != null) {
1131
+ nextTooltipButton.className += ' introjs-fullbutton';
1132
+ }
1133
+ } else {
1134
+ if (typeof prevTooltipButton !== "undefined" && prevTooltipButton != null) {
1135
+ prevTooltipButton.className = 'introjs-button introjs-prevbutton introjs-disabled';
1136
+ }
1137
+ }
1138
+
1139
+ if (typeof prevTooltipButton !== "undefined" && prevTooltipButton != null) {
1140
+ prevTooltipButton.tabIndex = '-1';
1141
+ }
1142
+ if (typeof skipTooltipButton !== "undefined" && skipTooltipButton != null) {
1143
+ skipTooltipButton.innerHTML = this._options.skipLabel;
1144
+ }
1145
+ } else if (this._introItems.length - 1 == this._currentStep || this._introItems.length == 1) {
1146
+ // last step of tour
1147
+ if (typeof skipTooltipButton !== "undefined" && skipTooltipButton != null) {
1148
+ skipTooltipButton.innerHTML = this._options.doneLabel;
1149
+ // adding donebutton class in addition to skipbutton
1150
+ skipTooltipButton.className += ' introjs-donebutton';
1151
+ }
1152
+ if (typeof prevTooltipButton !== "undefined" && prevTooltipButton != null) {
1153
+ prevTooltipButton.className = 'introjs-button introjs-prevbutton';
1154
+ }
1155
+
1156
+ if (this._options.hideNext == true) {
1157
+ if (typeof nextTooltipButton !== "undefined" && nextTooltipButton != null) {
1158
+ nextTooltipButton.className = 'introjs-button introjs-nextbutton introjs-hidden';
1159
+ }
1160
+ if (typeof prevTooltipButton !== "undefined" && prevTooltipButton != null) {
1161
+ prevTooltipButton.className += ' introjs-fullbutton';
1162
+ }
1163
+ } else {
1164
+ if (typeof nextTooltipButton !== "undefined" && nextTooltipButton != null) {
1165
+ nextTooltipButton.className = 'introjs-button introjs-nextbutton introjs-disabled';
1166
+ }
1167
+ }
1168
+
1169
+ if (typeof nextTooltipButton !== "undefined" && nextTooltipButton != null) {
1170
+ nextTooltipButton.tabIndex = '-1';
1171
+ }
1172
+ } else {
1173
+ // steps between start and end
1174
+ if (typeof skipTooltipButton !== "undefined" && skipTooltipButton != null) {
1175
+ skipTooltipButton.className = 'introjs-button introjs-skipbutton';
1176
+ }
1177
+ if (typeof prevTooltipButton !== "undefined" && prevTooltipButton != null) {
1178
+ prevTooltipButton.className = 'introjs-button introjs-prevbutton';
1179
+ }
1180
+ if (typeof nextTooltipButton !== "undefined" && nextTooltipButton != null) {
1181
+ nextTooltipButton.className = 'introjs-button introjs-nextbutton';
1182
+ }
1183
+ if (typeof skipTooltipButton !== "undefined" && skipTooltipButton != null) {
1184
+ skipTooltipButton.innerHTML = this._options.skipLabel;
1185
+ }
1186
+ }
1187
+
1188
+ //Set focus on "next" button, so that hitting Enter always moves you onto the next step
1189
+ if (typeof nextTooltipButton !== "undefined" && nextTooltipButton != null) {
1190
+ nextTooltipButton.focus();
1191
+ }
1192
+
1193
+ _setShowElement(targetElement);
1194
+
1195
+ if (typeof (this._introAfterChangeCallback) !== 'undefined') {
1196
+ this._introAfterChangeCallback.call(this, targetElement.element);
1197
+ }
1198
+ }
1199
+
1200
+ /**
1201
+ * To change the scroll of `window` after highlighting an element
1202
+ *
1203
+ * @api private
1204
+ * @method _scrollTo
1205
+ * @param {String} scrollTo
1206
+ * @param {Object} targetElement
1207
+ * @param {Object} tooltipLayer
1208
+ */
1209
+ function _scrollTo(scrollTo, targetElement, tooltipLayer) {
1210
+ if (!this._options.scrollToElement) return;
1211
+
1212
+ if (scrollTo === 'tooltip') {
1213
+ var rect = tooltipLayer.getBoundingClientRect();
1214
+ } else {
1215
+ var rect = targetElement.element.getBoundingClientRect();
1216
+ }
1217
+
1218
+ if (!_elementInViewport(targetElement.element)) {
1219
+ var winHeight = _getWinSize().height;
1220
+ var top = rect.bottom - (rect.bottom - rect.top);
1221
+ var bottom = rect.bottom - winHeight;
1222
+
1223
+ // TODO (afshinm): do we need scroll padding now?
1224
+ // I have changed the scroll option and now it scrolls the window to
1225
+ // the center of the target element or tooltip.
1226
+
1227
+ if (top < 0 || targetElement.element.clientHeight > winHeight) {
1228
+ window.scrollBy(0, rect.top - ((winHeight / 2) - (rect.height / 2)) - this._options.scrollPadding); // 30px padding from edge to look nice
1229
+
1230
+ //Scroll down
1231
+ } else {
1232
+ window.scrollBy(0, rect.top - ((winHeight / 2) - (rect.height / 2)) + this._options.scrollPadding); // 30px padding from edge to look nice
1233
+ }
1234
+ }
1235
+ }
1236
+
1237
+ /**
1238
+ * To remove all show element(s)
1239
+ *
1240
+ * @api private
1241
+ * @method _removeShowElement
1242
+ */
1243
+ function _removeShowElement() {
1244
+ var elms = document.querySelectorAll('.introjs-showElement');
1245
+
1246
+ for (var i = 0, l = elms.length; i < l; i++) {
1247
+ var elm = elms[i];
1248
+ _removeClass(elm, /introjs-[a-zA-Z]+/g);
1249
+ }
1250
+ }
1251
+
1252
+ /**
1253
+ * To set the show element
1254
+ * This function set a relative (in most cases) position and changes the z-index
1255
+ *
1256
+ * @api private
1257
+ * @method _setShowElement
1258
+ * @param {Object} targetElement
1259
+ */
1260
+ function _setShowElement(targetElement) {
1261
+ // we need to add this show element class to the parent of SVG elements
1262
+ // because the SVG elements can't have independent z-index
1263
+ if (targetElement.element instanceof SVGElement) {
1264
+ var parentElm = targetElement.element.parentNode;
1265
+
1266
+ while (targetElement.element.parentNode != null) {
1267
+ if (!parentElm.tagName || parentElm.tagName.toLowerCase() === 'body') break;
1268
+
1269
+ if (parentElm.tagName.toLowerCase() === 'svg') {
1270
+ _setClass(parentElm, 'introjs-showElement introjs-relativePosition');
1271
+ }
1272
+
1273
+ parentElm = parentElm.parentNode;
1274
+ }
1275
+ }
1276
+
1277
+ _setClass(targetElement.element, 'introjs-showElement');
1278
+
1279
+ var currentElementPosition = _getPropValue(targetElement.element, 'position');
1280
+ if (currentElementPosition !== 'absolute' &&
1281
+ currentElementPosition !== 'relative' &&
1282
+ currentElementPosition !== 'fixed') {
1283
+ //change to new intro item
1284
+ //targetElement.element.className += ' introjs-relativePosition';
1285
+ _setClass(targetElement.element, 'introjs-relativePosition')
1286
+ }
1287
+
1288
+ var parentElm = targetElement.element.parentNode;
1289
+ while (parentElm != null) {
1290
+ if (!parentElm.tagName || parentElm.tagName.toLowerCase() === 'body') break;
1291
+
1292
+ //fix The Stacking Context problem.
1293
+ //More detail: https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Understanding_z_index/The_stacking_context
1294
+ var zIndex = _getPropValue(parentElm, 'z-index');
1295
+ var opacity = parseFloat(_getPropValue(parentElm, 'opacity'));
1296
+ var transform = _getPropValue(parentElm, 'transform') || _getPropValue(parentElm, '-webkit-transform') || _getPropValue(parentElm, '-moz-transform') || _getPropValue(parentElm, '-ms-transform') || _getPropValue(parentElm, '-o-transform');
1297
+ if (/[0-9]+/.test(zIndex) || opacity < 1 || (transform !== 'none' && transform !== undefined)) {
1298
+ parentElm.className += ' introjs-fixParent';
1299
+ }
1300
+
1301
+ parentElm = parentElm.parentNode;
1302
+ }
1303
+ }
1304
+
1305
+ function _setClass(element, className) {
1306
+ if (element instanceof SVGElement) {
1307
+ var pre = element.getAttribute('class') || '';
1308
+
1309
+ element.setAttribute('class', pre + ' ' + className);
1310
+ } else {
1311
+ element.className += ' ' + className;
1312
+ }
1313
+ }
1314
+
1315
+ function _removeClass(element, classNameRegex) {
1316
+ if (element instanceof SVGElement) {
1317
+ var pre = element.getAttribute('class') || '';
1318
+
1319
+ element.setAttribute('class', pre.replace(classNameRegex, '').replace(/^\s+|\s+$/g, ''));
1320
+ } else {
1321
+ element.className = element.className.replace(classNameRegex, '').replace(/^\s+|\s+$/g, '');
1322
+ }
1323
+ }
1324
+
1325
+ /**
1326
+ * Get an element CSS property on the page
1327
+ * Thanks to JavaScript Kit: http://www.javascriptkit.com/dhtmltutors/dhtmlcascade4.shtml
1328
+ *
1329
+ * @api private
1330
+ * @method _getPropValue
1331
+ * @param {Object} element
1332
+ * @param {String} propName
1333
+ * @returns Element's property value
1334
+ */
1335
+ function _getPropValue (element, propName) {
1336
+ var propValue = '';
1337
+ if (element.currentStyle) { //IE
1338
+ propValue = element.currentStyle[propName];
1339
+ } else if (document.defaultView && document.defaultView.getComputedStyle) { //Others
1340
+ propValue = document.defaultView.getComputedStyle(element, null).getPropertyValue(propName);
1341
+ }
1342
+
1343
+ //Prevent exception in IE
1344
+ if (propValue && propValue.toLowerCase) {
1345
+ return propValue.toLowerCase();
1346
+ } else {
1347
+ return propValue;
1348
+ }
1349
+ }
1350
+
1351
+ /**
1352
+ * Checks to see if target element (or parents) position is fixed or not
1353
+ *
1354
+ * @api private
1355
+ * @method _isFixed
1356
+ * @param {Object} element
1357
+ * @returns Boolean
1358
+ */
1359
+ function _isFixed (element) {
1360
+ var p = element.parentNode;
1361
+
1362
+ if (!p || p.nodeName === 'HTML') {
1363
+ return false;
1364
+ }
1365
+
1366
+ if (_getPropValue(element, 'position') == 'fixed') {
1367
+ return true;
1368
+ }
1369
+
1370
+ return _isFixed(p);
1371
+ }
1372
+
1373
+ /**
1374
+ * Provides a cross-browser way to get the screen dimensions
1375
+ * via: http://stackoverflow.com/questions/5864467/internet-explorer-innerheight
1376
+ *
1377
+ * @api private
1378
+ * @method _getWinSize
1379
+ * @returns {Object} width and height attributes
1380
+ */
1381
+ function _getWinSize() {
1382
+ if (window.innerWidth != undefined) {
1383
+ return { width: window.innerWidth, height: window.innerHeight };
1384
+ } else {
1385
+ var D = document.documentElement;
1386
+ return { width: D.clientWidth, height: D.clientHeight };
1387
+ }
1388
+ }
1389
+
1390
+ /**
1391
+ * Check to see if the element is in the viewport or not
1392
+ * http://stackoverflow.com/questions/123999/how-to-tell-if-a-dom-element-is-visible-in-the-current-viewport
1393
+ *
1394
+ * @api private
1395
+ * @method _elementInViewport
1396
+ * @param {Object} el
1397
+ */
1398
+ function _elementInViewport(el) {
1399
+ var rect = el.getBoundingClientRect();
1400
+
1401
+ return (
1402
+ rect.top >= 0 &&
1403
+ rect.left >= 0 &&
1404
+ (rect.bottom+80) <= window.innerHeight && // add 80 to get the text right
1405
+ rect.right <= window.innerWidth
1406
+ );
1407
+ }
1408
+
1409
+ /**
1410
+ * Add overlay layer to the page
1411
+ *
1412
+ * @api private
1413
+ * @method _addOverlayLayer
1414
+ * @param {Object} targetElm
1415
+ */
1416
+ function _addOverlayLayer(targetElm) {
1417
+ var overlayLayer = document.createElement('div'),
1418
+ styleText = '',
1419
+ self = this;
1420
+
1421
+ //set css class name
1422
+ overlayLayer.className = 'introjs-overlay';
1423
+
1424
+ //check if the target element is body, we should calculate the size of overlay layer in a better way
1425
+ if (!targetElm.tagName || targetElm.tagName.toLowerCase() === 'body') {
1426
+ styleText += 'top: 0;bottom: 0; left: 0;right: 0;position: fixed;';
1427
+ overlayLayer.setAttribute('style', styleText);
1428
+ } else {
1429
+ //set overlay layer position
1430
+ var elementPosition = _getOffset(targetElm);
1431
+ if (elementPosition) {
1432
+ styleText += 'width: ' + elementPosition.width + 'px; height:' + elementPosition.height + 'px; top:' + elementPosition.top + 'px;left: ' + elementPosition.left + 'px;';
1433
+ overlayLayer.setAttribute('style', styleText);
1434
+ }
1435
+ }
1436
+
1437
+ targetElm.appendChild(overlayLayer);
1438
+
1439
+ overlayLayer.onclick = function() {
1440
+ if (self._options.exitOnOverlayClick == true) {
1441
+ _exitIntro.call(self, targetElm);
1442
+ }
1443
+ };
1444
+
1445
+ setTimeout(function() {
1446
+ styleText += 'opacity: ' + self._options.overlayOpacity.toString() + ';';
1447
+ overlayLayer.setAttribute('style', styleText);
1448
+ }, 10);
1449
+
1450
+ return true;
1451
+ }
1452
+
1453
+ /**
1454
+ * Removes open hint (tooltip hint)
1455
+ *
1456
+ * @api private
1457
+ * @method _removeHintTooltip
1458
+ */
1459
+ function _removeHintTooltip() {
1460
+ var tooltip = this._targetElement.querySelector('.introjs-hintReference');
1461
+
1462
+ if (tooltip) {
1463
+ var step = tooltip.getAttribute('data-step');
1464
+ tooltip.parentNode.removeChild(tooltip);
1465
+ return step;
1466
+ }
1467
+ }
1468
+
1469
+ /**
1470
+ * Start parsing hint items
1471
+ *
1472
+ * @api private
1473
+ * @param {Object} targetElm
1474
+ * @method _startHint
1475
+ */
1476
+ function _populateHints(targetElm) {
1477
+ var self = this;
1478
+ this._introItems = [];
1479
+
1480
+ if (this._options.hints) {
1481
+ for (var i = 0, l = this._options.hints.length; i < l; i++) {
1482
+ var currentItem = _cloneObject(this._options.hints[i]);
1483
+
1484
+ if (typeof(currentItem.element) === 'string') {
1485
+ //grab the element with given selector from the page
1486
+ currentItem.element = document.querySelector(currentItem.element);
1487
+ }
1488
+
1489
+ currentItem.hintPosition = currentItem.hintPosition || this._options.hintPosition;
1490
+ currentItem.hintAnimation = currentItem.hintAnimation || this._options.hintAnimation;
1491
+
1492
+ if (currentItem.element != null) {
1493
+ this._introItems.push(currentItem);
1494
+ }
1495
+ }
1496
+ } else {
1497
+ var hints = targetElm.querySelectorAll('*[data-hint]');
1498
+
1499
+ if (hints.length < 1) {
1500
+ return false;
1501
+ }
1502
+
1503
+ //first add intro items with data-step
1504
+ for (var i = 0, l = hints.length; i < l; i++) {
1505
+ var currentElement = hints[i];
1506
+
1507
+ // hint animation
1508
+ var hintAnimation = currentElement.getAttribute('data-hintAnimation');
1509
+
1510
+ if (hintAnimation) {
1511
+ hintAnimation = (hintAnimation == 'true');
1512
+ } else {
1513
+ hintAnimation = this._options.hintAnimation;
1514
+ }
1515
+
1516
+ this._introItems.push({
1517
+ element: currentElement,
1518
+ hint: currentElement.getAttribute('data-hint'),
1519
+ hintPosition: currentElement.getAttribute('data-hintPosition') || this._options.hintPosition,
1520
+ hintAnimation: hintAnimation,
1521
+ tooltipClass: currentElement.getAttribute('data-tooltipClass'),
1522
+ position: currentElement.getAttribute('data-position') || this._options.tooltipPosition
1523
+ });
1524
+ }
1525
+ }
1526
+
1527
+ _addHints.call(this);
1528
+
1529
+ if (document.addEventListener) {
1530
+ document.addEventListener('click', _removeHintTooltip.bind(this), false);
1531
+ //for window resize
1532
+ window.addEventListener('resize', _reAlignHints.bind(this), true);
1533
+ } else if (document.attachEvent) { //IE
1534
+ //for window resize
1535
+ document.attachEvent('onclick', _removeHintTooltip.bind(this));
1536
+ document.attachEvent('onresize', _reAlignHints.bind(this));
1537
+ }
1538
+ }
1539
+
1540
+ /**
1541
+ * Re-aligns all hint elements
1542
+ *
1543
+ * @api private
1544
+ * @method _reAlignHints
1545
+ */
1546
+ function _reAlignHints() {
1547
+ for (var i = 0, l = this._introItems.length; i < l; i++) {
1548
+ var item = this._introItems[i];
1549
+
1550
+ if (typeof (item.targetElement) == 'undefined') continue;
1551
+
1552
+ _alignHintPosition.call(this, item.hintPosition, item.element, item.targetElement)
1553
+ }
1554
+ }
1555
+
1556
+ /**
1557
+ * Hide a hint
1558
+ *
1559
+ * @api private
1560
+ * @method _hideHint
1561
+ */
1562
+ function _hideHint(stepId) {
1563
+ _removeHintTooltip.call(this);
1564
+ var hint = this._targetElement.querySelector('.introjs-hint[data-step="' + stepId + '"]');
1565
+
1566
+ if (hint) {
1567
+ hint.className += ' introjs-hidehint';
1568
+ }
1569
+
1570
+ // call the callback function (if any)
1571
+ if (typeof (this._hintCloseCallback) !== 'undefined') {
1572
+ this._hintCloseCallback.call(this, stepId);
1573
+ }
1574
+ }
1575
+
1576
+ /**
1577
+ * Hide all hints
1578
+ *
1579
+ * @api private
1580
+ * @method _hideHints
1581
+ */
1582
+ function _hideHints() {
1583
+ var hints = this._targetElement.querySelectorAll('.introjs-hint');
1584
+
1585
+ if (hints && hints.length > 0) {
1586
+ for (var i = 0; i < hints.length; i++) {
1587
+ _hideHint.call(this, hints[i].getAttribute('data-step'));
1588
+ }
1589
+ }
1590
+ }
1591
+
1592
+ /**
1593
+ * Show all hints
1594
+ *
1595
+ * @api private
1596
+ * @method _showHints
1597
+ */
1598
+ function _showHints() {
1599
+ var hints = this._targetElement.querySelectorAll('.introjs-hint');
1600
+
1601
+ if (hints && hints.length > 0) {
1602
+ for (var i = 0; i < hints.length; i++) {
1603
+ _showHint.call(this, hints[i].getAttribute('data-step'));
1604
+ }
1605
+ } else {
1606
+ _populateHints.call(this, this._targetElement);
1607
+ }
1608
+ };
1609
+
1610
+ /**
1611
+ * Show a hint
1612
+ *
1613
+ * @api private
1614
+ * @method _showHint
1615
+ */
1616
+ function _showHint(stepId) {
1617
+ var hint = this._targetElement.querySelector('.introjs-hint[data-step="' + stepId + '"]');
1618
+
1619
+ if (hint) {
1620
+ hint.className = hint.className.replace(/introjs\-hidehint/g, '');
1621
+ }
1622
+ };
1623
+
1624
+ /**
1625
+ * Removes all hint elements on the page
1626
+ * Useful when you want to destroy the elements and add them again (e.g. a modal or popup)
1627
+ *
1628
+ * @api private
1629
+ * @method _removeHints
1630
+ */
1631
+ function _removeHints() {
1632
+ var hints = this._targetElement.querySelectorAll('.introjs-hint');
1633
+
1634
+ if (hints && hints.length > 0) {
1635
+ for (var i = 0; i < hints.length; i++) {
1636
+ _removeHint.call(this, hints[i].getAttribute('data-step'));
1637
+ }
1638
+ }
1639
+ };
1640
+
1641
+ /**
1642
+ * Remove one single hint element from the page
1643
+ * Useful when you want to destroy the element and add them again (e.g. a modal or popup)
1644
+ * Use removeHints if you want to remove all elements.
1645
+ *
1646
+ * @api private
1647
+ * @method _removeHint
1648
+ */
1649
+ function _removeHint(stepId) {
1650
+ var hint = this._targetElement.querySelector('.introjs-hint[data-step="' + stepId + '"]');
1651
+
1652
+ if (hint) {
1653
+ hint.parentNode.removeChild(hint);
1654
+ }
1655
+ };
1656
+
1657
+ /**
1658
+ * Add all available hints to the page
1659
+ *
1660
+ * @api private
1661
+ * @method _addHints
1662
+ */
1663
+ function _addHints() {
1664
+ var self = this;
1665
+
1666
+ var oldHintsWrapper = document.querySelector('.introjs-hints');
1667
+
1668
+ if (oldHintsWrapper != null) {
1669
+ hintsWrapper = oldHintsWrapper;
1670
+ } else {
1671
+ var hintsWrapper = document.createElement('div');
1672
+ hintsWrapper.className = 'introjs-hints';
1673
+ }
1674
+
1675
+ for (var i = 0, l = this._introItems.length; i < l; i++) {
1676
+ var item = this._introItems[i];
1677
+
1678
+ // avoid append a hint twice
1679
+ if (document.querySelector('.introjs-hint[data-step="' + i + '"]'))
1680
+ continue;
1681
+
1682
+ var hint = document.createElement('a');
1683
+ _setAnchorAsButton(hint);
1684
+
1685
+ (function (hint, item, i) {
1686
+ // when user clicks on the hint element
1687
+ hint.onclick = function(e) {
1688
+ var evt = e ? e : window.event;
1689
+ if (evt.stopPropagation) evt.stopPropagation();
1690
+ if (evt.cancelBubble != null) evt.cancelBubble = true;
1691
+
1692
+ _showHintDialog.call(self, i);
1693
+ };
1694
+ }(hint, item, i));
1695
+
1696
+ hint.className = 'introjs-hint';
1697
+
1698
+ if (!item.hintAnimation) {
1699
+ hint.className += ' introjs-hint-no-anim';
1700
+ }
1701
+
1702
+ // hint's position should be fixed if the target element's position is fixed
1703
+ if (_isFixed(item.element)) {
1704
+ hint.className += ' introjs-fixedhint';
1705
+ }
1706
+
1707
+ var hintDot = document.createElement('div');
1708
+ hintDot.className = 'introjs-hint-dot';
1709
+ var hintPulse = document.createElement('div');
1710
+ hintPulse.className = 'introjs-hint-pulse';
1711
+
1712
+ hint.appendChild(hintDot);
1713
+ hint.appendChild(hintPulse);
1714
+ hint.setAttribute('data-step', i);
1715
+
1716
+ // we swap the hint element with target element
1717
+ // because _setHelperLayerPosition uses `element` property
1718
+ item.targetElement = item.element;
1719
+ item.element = hint;
1720
+
1721
+ // align the hint position
1722
+ _alignHintPosition.call(this, item.hintPosition, hint, item.targetElement);
1723
+
1724
+ hintsWrapper.appendChild(hint);
1725
+ }
1726
+
1727
+ // adding the hints wrapper
1728
+ document.body.appendChild(hintsWrapper);
1729
+
1730
+ // call the callback function (if any)
1731
+ if (typeof (this._hintsAddedCallback) !== 'undefined') {
1732
+ this._hintsAddedCallback.call(this);
1733
+ }
1734
+ }
1735
+
1736
+ /**
1737
+ * Aligns hint position
1738
+ *
1739
+ * @api private
1740
+ * @method _alignHintPosition
1741
+ * @param {String} position
1742
+ * @param {Object} hint
1743
+ * @param {Object} element
1744
+ */
1745
+ function _alignHintPosition(position, hint, element) {
1746
+ // get/calculate offset of target element
1747
+ var offset = _getOffset.call(this, element);
1748
+ var iconWidth = 20;
1749
+ var iconHeight = 20;
1750
+
1751
+ // align the hint element
1752
+ switch (position) {
1753
+ default:
1754
+ case 'top-left':
1755
+ hint.style.left = offset.left + 'px';
1756
+ hint.style.top = offset.top + 'px';
1757
+ break;
1758
+ case 'top-right':
1759
+ hint.style.left = (offset.left + offset.width - iconWidth) + 'px';
1760
+ hint.style.top = offset.top + 'px';
1761
+ break;
1762
+ case 'bottom-left':
1763
+ hint.style.left = offset.left + 'px';
1764
+ hint.style.top = (offset.top + offset.height - iconHeight) + 'px';
1765
+ break;
1766
+ case 'bottom-right':
1767
+ hint.style.left = (offset.left + offset.width - iconWidth) + 'px';
1768
+ hint.style.top = (offset.top + offset.height - iconHeight) + 'px';
1769
+ break;
1770
+ case 'middle-left':
1771
+ hint.style.left = offset.left + 'px';
1772
+ hint.style.top = (offset.top + (offset.height - iconHeight) / 2) + 'px';
1773
+ break;
1774
+ case 'middle-right':
1775
+ hint.style.left = (offset.left + offset.width - iconWidth) + 'px';
1776
+ hint.style.top = (offset.top + (offset.height - iconHeight) / 2) + 'px';
1777
+ break;
1778
+ case 'middle-middle':
1779
+ hint.style.left = (offset.left + (offset.width - iconWidth) / 2) + 'px';
1780
+ hint.style.top = (offset.top + (offset.height - iconHeight) / 2) + 'px';
1781
+ break;
1782
+ case 'bottom-middle':
1783
+ hint.style.left = (offset.left + (offset.width - iconWidth) / 2) + 'px';
1784
+ hint.style.top = (offset.top + offset.height - iconHeight) + 'px';
1785
+ break;
1786
+ case 'top-middle':
1787
+ hint.style.left = (offset.left + (offset.width - iconWidth) / 2) + 'px';
1788
+ hint.style.top = offset.top + 'px';
1789
+ break;
1790
+ }
1791
+ }
1792
+
1793
+ /**
1794
+ * Triggers when user clicks on the hint element
1795
+ *
1796
+ * @api private
1797
+ * @method _showHintDialog
1798
+ * @param {Number} stepId
1799
+ */
1800
+ function _showHintDialog(stepId) {
1801
+ var hintElement = document.querySelector('.introjs-hint[data-step="' + stepId + '"]');
1802
+ var item = this._introItems[stepId];
1803
+
1804
+ // call the callback function (if any)
1805
+ if (typeof (this._hintClickCallback) !== 'undefined') {
1806
+ this._hintClickCallback.call(this, hintElement, item, stepId);
1807
+ }
1808
+
1809
+ // remove all open tooltips
1810
+ var removedStep = _removeHintTooltip.call(this);
1811
+
1812
+ // to toggle the tooltip
1813
+ if (parseInt(removedStep, 10) == stepId) {
1814
+ return;
1815
+ }
1816
+
1817
+ var tooltipLayer = document.createElement('div');
1818
+ var tooltipTextLayer = document.createElement('div');
1819
+ var arrowLayer = document.createElement('div');
1820
+ var referenceLayer = document.createElement('div');
1821
+
1822
+ tooltipLayer.className = 'introjs-tooltip';
1823
+
1824
+ tooltipLayer.onclick = function (e) {
1825
+ //IE9 & Other Browsers
1826
+ if (e.stopPropagation) {
1827
+ e.stopPropagation();
1828
+ }
1829
+ //IE8 and Lower
1830
+ else {
1831
+ e.cancelBubble = true;
1832
+ }
1833
+ };
1834
+
1835
+ tooltipTextLayer.className = 'introjs-tooltiptext';
1836
+
1837
+ var tooltipWrapper = document.createElement('p');
1838
+ tooltipWrapper.innerHTML = item.hint;
1839
+
1840
+ var closeButton = document.createElement('a');
1841
+ closeButton.className = 'introjs-button';
1842
+ closeButton.innerHTML = this._options.hintButtonLabel;
1843
+ closeButton.onclick = _hideHint.bind(this, stepId);
1844
+
1845
+ tooltipTextLayer.appendChild(tooltipWrapper);
1846
+ tooltipTextLayer.appendChild(closeButton);
1847
+
1848
+ arrowLayer.className = 'introjs-arrow';
1849
+ tooltipLayer.appendChild(arrowLayer);
1850
+
1851
+ tooltipLayer.appendChild(tooltipTextLayer);
1852
+
1853
+ // set current step for _placeTooltip function
1854
+ this._currentStep = hintElement.getAttribute('data-step');
1855
+
1856
+ // align reference layer position
1857
+ referenceLayer.className = 'introjs-tooltipReferenceLayer introjs-hintReference';
1858
+ referenceLayer.setAttribute('data-step', hintElement.getAttribute('data-step'));
1859
+ _setHelperLayerPosition.call(this, referenceLayer);
1860
+
1861
+ referenceLayer.appendChild(tooltipLayer);
1862
+ document.body.appendChild(referenceLayer);
1863
+
1864
+ //set proper position
1865
+ _placeTooltip.call(this, hintElement, tooltipLayer, arrowLayer, null, true);
1866
+ }
1867
+
1868
+ /**
1869
+ * Get an element position on the page
1870
+ * Thanks to `meouw`: http://stackoverflow.com/a/442474/375966
1871
+ *
1872
+ * @api private
1873
+ * @method _getOffset
1874
+ * @param {Object} element
1875
+ * @returns Element's position info
1876
+ */
1877
+ function _getOffset(element) {
1878
+ var elementPosition = {};
1879
+
1880
+ var body = document.body;
1881
+ var docEl = document.documentElement;
1882
+
1883
+ var scrollTop = window.pageYOffset || docEl.scrollTop || body.scrollTop;
1884
+ var scrollLeft = window.pageXOffset || docEl.scrollLeft || body.scrollLeft;
1885
+
1886
+ if (element instanceof SVGElement) {
1887
+ var x = element.getBoundingClientRect()
1888
+ elementPosition.top = x.top + scrollTop;
1889
+ elementPosition.width = x.width;
1890
+ elementPosition.height = x.height;
1891
+ elementPosition.left = x.left + scrollLeft;
1892
+ } else {
1893
+ //set width
1894
+ elementPosition.width = element.offsetWidth;
1895
+
1896
+ //set height
1897
+ elementPosition.height = element.offsetHeight;
1898
+
1899
+ //calculate element top and left
1900
+ var _x = 0;
1901
+ var _y = 0;
1902
+ while (element && !isNaN(element.offsetLeft) && !isNaN(element.offsetTop)) {
1903
+ _x += element.offsetLeft;
1904
+ _y += element.offsetTop;
1905
+ element = element.offsetParent;
1906
+ }
1907
+ //set top
1908
+ elementPosition.top = _y;
1909
+ //set left
1910
+ elementPosition.left = _x;
1911
+ }
1912
+
1913
+ return elementPosition;
1914
+ }
1915
+
1916
+ /**
1917
+ * Gets the current progress percentage
1918
+ *
1919
+ * @api private
1920
+ * @method _getProgress
1921
+ * @returns current progress percentage
1922
+ */
1923
+ function _getProgress() {
1924
+ // Steps are 0 indexed
1925
+ var currentStep = parseInt((this._currentStep + 1), 10);
1926
+ return ((currentStep / this._introItems.length) * 100);
1927
+ }
1928
+
1929
+ /**
1930
+ * Overwrites obj1's values with obj2's and adds obj2's if non existent in obj1
1931
+ * via: http://stackoverflow.com/questions/171251/how-can-i-merge-properties-of-two-javascript-objects-dynamically
1932
+ *
1933
+ * @param obj1
1934
+ * @param obj2
1935
+ * @returns obj3 a new object based on obj1 and obj2
1936
+ */
1937
+ function _mergeOptions(obj1,obj2) {
1938
+ var obj3 = {};
1939
+ for (var attrname in obj1) { obj3[attrname] = obj1[attrname]; }
1940
+ for (var attrname in obj2) { obj3[attrname] = obj2[attrname]; }
1941
+ return obj3;
1942
+ }
1943
+
1944
+ var introJs = function (targetElm) {
1945
+ if (typeof (targetElm) === 'object') {
1946
+ //Ok, create a new instance
1947
+ return new IntroJs(targetElm);
1948
+
1949
+ } else if (typeof (targetElm) === 'string') {
1950
+ //select the target element with query selector
1951
+ var targetElement = document.querySelector(targetElm);
1952
+
1953
+ if (targetElement) {
1954
+ return new IntroJs(targetElement);
1955
+ } else {
1956
+ throw new Error('There is no element with given selector.');
1957
+ }
1958
+ } else {
1959
+ return new IntroJs(document.body);
1960
+ }
1961
+ };
1962
+
1963
+ /**
1964
+ * Current IntroJs version
1965
+ *
1966
+ * @property version
1967
+ * @type String
1968
+ */
1969
+ introJs.version = VERSION;
1970
+
1971
+ //Prototype
1972
+ introJs.fn = IntroJs.prototype = {
1973
+ clone: function () {
1974
+ return new IntroJs(this);
1975
+ },
1976
+ setOption: function(option, value) {
1977
+ this._options[option] = value;
1978
+ return this;
1979
+ },
1980
+ setOptions: function(options) {
1981
+ this._options = _mergeOptions(this._options, options);
1982
+ return this;
1983
+ },
1984
+ start: function () {
1985
+ _introForElement.call(this, this._targetElement);
1986
+ return this;
1987
+ },
1988
+ goToStep: function(step) {
1989
+ _goToStep.call(this, step);
1990
+ return this;
1991
+ },
1992
+ addStep: function(options) {
1993
+ if (!this._options.steps) {
1994
+ this._options.steps = [];
1995
+ }
1996
+
1997
+ this._options.steps.push(options);
1998
+
1999
+ return this;
2000
+ },
2001
+ addSteps: function(steps) {
2002
+ if (!steps.length) return;
2003
+
2004
+ for(var index = 0; index < steps.length; index++) {
2005
+ this.addStep(steps[index]);
2006
+ }
2007
+
2008
+ return this;
2009
+ },
2010
+ goToStepNumber: function(step) {
2011
+ _goToStepNumber.call(this, step);
2012
+
2013
+ return this;
2014
+ },
2015
+ nextStep: function() {
2016
+ _nextStep.call(this);
2017
+ return this;
2018
+ },
2019
+ previousStep: function() {
2020
+ _previousStep.call(this);
2021
+ return this;
2022
+ },
2023
+ exit: function(force) {
2024
+ _exitIntro.call(this, this._targetElement, force);
2025
+ return this;
2026
+ },
2027
+ refresh: function() {
2028
+ _refresh.call(this);
2029
+ return this;
2030
+ },
2031
+ onbeforechange: function(providedCallback) {
2032
+ if (typeof (providedCallback) === 'function') {
2033
+ this._introBeforeChangeCallback = providedCallback;
2034
+ } else {
2035
+ throw new Error('Provided callback for onbeforechange was not a function');
2036
+ }
2037
+ return this;
2038
+ },
2039
+ onchange: function(providedCallback) {
2040
+ if (typeof (providedCallback) === 'function') {
2041
+ this._introChangeCallback = providedCallback;
2042
+ } else {
2043
+ throw new Error('Provided callback for onchange was not a function.');
2044
+ }
2045
+ return this;
2046
+ },
2047
+ onafterchange: function(providedCallback) {
2048
+ if (typeof (providedCallback) === 'function') {
2049
+ this._introAfterChangeCallback = providedCallback;
2050
+ } else {
2051
+ throw new Error('Provided callback for onafterchange was not a function');
2052
+ }
2053
+ return this;
2054
+ },
2055
+ oncomplete: function(providedCallback) {
2056
+ if (typeof (providedCallback) === 'function') {
2057
+ this._introCompleteCallback = providedCallback;
2058
+ } else {
2059
+ throw new Error('Provided callback for oncomplete was not a function.');
2060
+ }
2061
+ return this;
2062
+ },
2063
+ onhintsadded: function(providedCallback) {
2064
+ if (typeof (providedCallback) === 'function') {
2065
+ this._hintsAddedCallback = providedCallback;
2066
+ } else {
2067
+ throw new Error('Provided callback for onhintsadded was not a function.');
2068
+ }
2069
+ return this;
2070
+ },
2071
+ onhintclick: function(providedCallback) {
2072
+ if (typeof (providedCallback) === 'function') {
2073
+ this._hintClickCallback = providedCallback;
2074
+ } else {
2075
+ throw new Error('Provided callback for onhintclick was not a function.');
2076
+ }
2077
+ return this;
2078
+ },
2079
+ onhintclose: function(providedCallback) {
2080
+ if (typeof (providedCallback) === 'function') {
2081
+ this._hintCloseCallback = providedCallback;
2082
+ } else {
2083
+ throw new Error('Provided callback for onhintclose was not a function.');
2084
+ }
2085
+ return this;
2086
+ },
2087
+ onexit: function(providedCallback) {
2088
+ if (typeof (providedCallback) === 'function') {
2089
+ this._introExitCallback = providedCallback;
2090
+ } else {
2091
+ throw new Error('Provided callback for onexit was not a function.');
2092
+ }
2093
+ return this;
2094
+ },
2095
+ onbeforeexit: function(providedCallback) {
2096
+ if (typeof (providedCallback) === 'function') {
2097
+ this._introBeforeExitCallback = providedCallback;
2098
+ } else {
2099
+ throw new Error('Provided callback for onbeforeexit was not a function.');
2100
+ }
2101
+ return this;
2102
+ },
2103
+ addHints: function() {
2104
+ _populateHints.call(this, this._targetElement);
2105
+ return this;
2106
+ },
2107
+ hideHint: function (stepId) {
2108
+ _hideHint.call(this, stepId);
2109
+ return this;
2110
+ },
2111
+ hideHints: function () {
2112
+ _hideHints.call(this);
2113
+ return this;
2114
+ },
2115
+ showHint: function (stepId) {
2116
+ _showHint.call(this, stepId);
2117
+ return this;
2118
+ },
2119
+ showHints: function () {
2120
+ _showHints.call(this);
2121
+ return this;
2122
+ },
2123
+ removeHints: function () {
2124
+ _removeHints.call(this);
2125
+ return this;
2126
+ },
2127
+ removeHint: function (stepId) {
2128
+ _removeHint.call(this, stepId);
2129
+ return this;
2130
+ },
2131
+ showHintDialog: function (stepId) {
2132
+ _showHintDialog.call(this, stepId);
2133
+ return this;
2134
+ }
2135
+ };
2136
+
2137
+ exports.introJs = introJs;
2138
+ return introJs;
2139
+ }));
readme.txt CHANGED
@@ -4,7 +4,7 @@ Buy plugin: https://buttonizer.pro
4
  Tags: floating menu, action button, call button, floating action button, Social Sharing button
5
  Requires at least: 4.2
6
  Tested up to: 4.8.1
7
- Stable tag: 1.0.8
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
@@ -116,6 +116,26 @@ And the beauty of all: All actions are hidden in one button. The moment a visito
116
  In fact the Buttonizer is an addiction to use. You&#39;re website visitors will interact as never before.
117
 
118
  == Changelog ==
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
119
  = 1.0.8 =
120
  There are multiple changes inside Buttonizer. Like the settings page is better organised now and re-styled.
121
 
4
  Tags: floating menu, action button, call button, floating action button, Social Sharing button
5
  Requires at least: 4.2
6
  Tested up to: 4.8.1
7
+ Stable tag: 1.0.9
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
116
  In fact the Buttonizer is an addiction to use. You&#39;re website visitors will interact as never before.
117
 
118
  == Changelog ==
119
+ = 1.0.9 =
120
+ A new cool update! We added for our new users a tour (of course, if you are already using Buttonizer, feel free to take the tour!). If someone has some feedback, about the tour, contact us through the plugin! :)
121
+
122
+ We've added something magical! For each button it's now possible to use one of the following actions:
123
+ - Website URL: For opening a webpage
124
+ - Phone number: Direct action
125
+ - E-mail: Opens the desktop client from the user with your mail address filled in already
126
+
127
+ New this release:
128
+ - Information tour for our new users
129
+ - Button actions easier
130
+
131
+ Changed:
132
+ - Re-ordered the settings page a bit
133
+
134
+ Fixed:
135
+ - When enabling 'show on hover' with only one button, the label won't be visible anymore
136
+ - Some caching plugins liked to cache our javascript, we've added a fix to that
137
+ - Other bugs
138
+
139
  = 1.0.8 =
140
  There are multiple changes inside Buttonizer. Like the settings page is better organised now and re-styled.
141